aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Chat/Chat.cpp26
-rw-r--r--src/server/game/Chat/Chat.h4
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp2
-rw-r--r--src/server/game/DungeonFinding/LFGScripts.cpp6
-rw-r--r--src/server/game/DungeonFinding/LFGScripts.h2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp22
-rw-r--r--src/server/game/Entities/Creature/Creature.h1
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp8
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h4
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp87
-rw-r--r--src/server/game/Globals/ObjectMgr.h3
-rw-r--r--src/server/game/Instances/InstanceScript.cpp14
-rw-r--r--src/server/game/Instances/InstanceScript.h8
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp6
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp1108
-rw-r--r--src/server/game/Scripting/ScriptMgr.h50
-rw-r--r--src/server/game/Spells/SpellScript.cpp7
-rw-r--r--src/server/game/Spells/SpellScript.h11
-rw-r--r--src/server/game/World/World.cpp1
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp2
20 files changed, 1076 insertions, 296 deletions
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index f0e98ee8c1f..ec90a5f7efb 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -32,18 +32,19 @@
#include "ScriptMgr.h"
#include "ChatLink.h"
-bool ChatHandler::load_command_table = true;
+// Lazy loading of the command table cache from commands and the
+// ScriptMgr should be thread safe since the player commands,
+// cli commands and ScriptMgr updates are all dispatched one after
+// one inside the world update loop.
+static Optional<std::vector<ChatCommand>> commandTableCache;
std::vector<ChatCommand> const& ChatHandler::getCommandTable()
{
- static std::vector<ChatCommand> commandTableCache;
-
- if (LoadCommandTable())
+ if (!commandTableCache)
{
- SetLoadCommandTable(false);
-
- std::vector<ChatCommand> cmds = sScriptMgr->GetChatCommands();
- commandTableCache.swap(cmds);
+ // We need to initialize this at top since SetDataForCommandInTable
+ // calls getCommandTable() recursively.
+ commandTableCache = sScriptMgr->GetChatCommands();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_COMMANDS);
PreparedQueryResult result = WorldDatabase.Query(stmt);
@@ -54,13 +55,18 @@ std::vector<ChatCommand> const& ChatHandler::getCommandTable()
Field* fields = result->Fetch();
std::string name = fields[0].GetString();
- SetDataForCommandInTable(commandTableCache, name.c_str(), fields[1].GetUInt16(), fields[2].GetString(), name);
+ SetDataForCommandInTable(*commandTableCache, name.c_str(), fields[1].GetUInt16(), fields[2].GetString(), name);
}
while (result->NextRow());
}
}
- return commandTableCache;
+ return *commandTableCache;
+}
+
+void ChatHandler::invalidateCommandTable()
+{
+ commandTableCache.reset();
}
char const* ChatHandler::GetTrinityString(uint32 entry) const
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index a061ff63b00..1c9368275ad 100644
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -96,6 +96,7 @@ class TC_GAME_API ChatHandler
bool ParseCommands(const char* text);
static std::vector<ChatCommand> const& getCommandTable();
+ static void invalidateCommandTable();
bool isValidChatMessage(const char* msg);
void SendGlobalSysMessage(const char *str);
@@ -143,8 +144,6 @@ class TC_GAME_API ChatHandler
GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(ObjectGuid::LowType lowguid, uint32 entry);
bool HasSentErrorMessage() const { return sentErrorMessage; }
void SetSentErrorMessage(bool val){ sentErrorMessage = val; }
- static bool LoadCommandTable() { return load_command_table; }
- static void SetLoadCommandTable(bool val) { load_command_table = val; }
bool ShowHelpForCommand(std::vector<ChatCommand> const& table, const char* cmd);
protected:
@@ -157,7 +156,6 @@ class TC_GAME_API ChatHandler
WorldSession* m_session; // != NULL for chat command call and NULL for CLI command
// common global flag
- static bool load_command_table;
bool sentErrorMessage;
};
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp
index 02fc69b8365..e72859c23e7 100644
--- a/src/server/game/DungeonFinding/LFGMgr.cpp
+++ b/src/server/game/DungeonFinding/LFGMgr.cpp
@@ -40,8 +40,6 @@ namespace lfg
LFGMgr::LFGMgr(): m_QueueTimer(0), m_lfgProposalId(1),
m_options(sWorld->getIntConfig(CONFIG_LFG_OPTIONSMASK))
{
- new LFGPlayerScript();
- new LFGGroupScript();
}
LFGMgr::~LFGMgr()
diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp
index 79d36055870..1d2b963b254 100644
--- a/src/server/game/DungeonFinding/LFGScripts.cpp
+++ b/src/server/game/DungeonFinding/LFGScripts.cpp
@@ -241,4 +241,10 @@ void LFGGroupScript::OnInviteMember(Group* group, ObjectGuid guid)
sLFGMgr->LeaveLfg(leader);
}
+void AddSC_LFGScripts()
+{
+ new LFGPlayerScript();
+ new LFGGroupScript();
+}
+
} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFGScripts.h b/src/server/game/DungeonFinding/LFGScripts.h
index ec64604a282..9f52668ea61 100644
--- a/src/server/game/DungeonFinding/LFGScripts.h
+++ b/src/server/game/DungeonFinding/LFGScripts.h
@@ -53,4 +53,6 @@ class TC_GAME_API LFGGroupScript : public GroupScript
void OnInviteMember(Group* group, ObjectGuid guid) override;
};
+/*keep private*/ void AddSC_LFGScripts();
+
} // namespace lfg
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 61d5d7d9459..2b595b71e01 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -791,6 +791,24 @@ void Creature::DoFleeToGetAssistance()
}
}
+bool Creature::AIM_Destroy()
+{
+ if (m_AI_locked)
+ {
+ TC_LOG_DEBUG("scripts", "AIM_Destroy: failed to destroy, locked.");
+ return false;
+ }
+
+ ASSERT(!i_disabledAI,
+ "The disabled AI wasn't cleared!");
+
+ delete i_AI;
+ i_AI = nullptr;
+
+ IsAIEnabled = false;
+ return true;
+}
+
bool Creature::AIM_Initialize(CreatureAI* ai)
{
// make sure nothing can change the AI during AI update
@@ -800,12 +818,12 @@ bool Creature::AIM_Initialize(CreatureAI* ai)
return false;
}
- UnitAI* oldAI = i_AI;
+ AIM_Destroy();
Motion_Initialize();
i_AI = ai ? ai : FactorySelector::selectAI(this);
- delete oldAI;
+
IsAIEnabled = true;
i_AI->InitializeAI();
// Initialize vehicle
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index c6b19dbb939..8466dad9e95 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -476,6 +476,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool IsInEvadeMode() const { return HasUnitState(UNIT_STATE_EVADE); }
+ bool AIM_Destroy();
bool AIM_Initialize(CreatureAI* ai = NULL);
void Motion_Initialize();
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 3fcd249c9be..7f922f89572 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -71,9 +71,15 @@ GameObject::~GameObject()
// CleanupsBeforeDelete();
}
-bool GameObject::AIM_Initialize()
+void GameObject::AIM_Destroy()
{
delete m_AI;
+ m_AI = nullptr;
+}
+
+bool GameObject::AIM_Initialize()
+{
+ AIM_Destroy();
m_AI = FactorySelector::SelectGameObjectAI(this);
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 49f94013ac2..2b092427178 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -849,8 +849,10 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void UpdateModelPosition();
- protected:
+ void AIM_Destroy();
bool AIM_Initialize();
+
+ protected:
GameObjectModel* CreateModel();
void UpdateModel(); // updates model in case displayId were changed
uint32 m_spellId;
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 596411f3d67..3513b5d97b8 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -5162,7 +5162,7 @@ void ObjectMgr::LoadSpellScriptNames()
while (spellInfo)
{
- _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
+ _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
spellInfo = spellInfo->GetNextRankSpell();
}
}
@@ -5171,7 +5171,7 @@ void ObjectMgr::LoadSpellScriptNames()
if (spellInfo->IsRanked())
TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName.c_str(), spellId);
- _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
+ _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
}
++count;
@@ -5193,45 +5193,48 @@ void ObjectMgr::ValidateSpellScripts()
uint32 count = 0;
- for (SpellScriptsContainer::iterator itr = _spellScriptsStore.begin(); itr != _spellScriptsStore.end();)
+ for (auto spell : _spellScriptsStore)
{
- SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(itr->first);
- std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> > SpellScriptLoaders;
- sScriptMgr->CreateSpellScriptLoaders(itr->first, SpellScriptLoaders);
- itr = _spellScriptsStore.upper_bound(itr->first);
+ SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spell.first);
- for (std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> >::iterator sitr = SpellScriptLoaders.begin(); sitr != SpellScriptLoaders.end(); ++sitr)
+ auto const bounds = sObjectMgr->GetSpellScriptsBounds(spell.first);
+
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
{
- SpellScript* spellScript = sitr->first->GetSpellScript();
- AuraScript* auraScript = sitr->first->GetAuraScript();
- bool valid = true;
- if (!spellScript && !auraScript)
- {
- TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(sitr->second->second).c_str());
- valid = false;
- }
- if (spellScript)
- {
- spellScript->_Init(&sitr->first->GetName(), spellEntry->Id);
- spellScript->_Register();
- if (!spellScript->_Validate(spellEntry))
- valid = false;
- delete spellScript;
- }
- if (auraScript)
- {
- auraScript->_Init(&sitr->first->GetName(), spellEntry->Id);
- auraScript->_Register();
- if (!auraScript->_Validate(spellEntry))
- valid = false;
- delete auraScript;
- }
- if (!valid)
+ if (SpellScriptLoader* spellScriptLoader = sScriptMgr->GetSpellScriptLoader(itr->second.first))
{
- _spellScriptsStore.erase(sitr->second);
+ ++count;
+
+ std::unique_ptr<SpellScript> spellScript(spellScriptLoader->GetSpellScript());
+ std::unique_ptr<AuraScript> auraScript(spellScriptLoader->GetAuraScript());
+
+ if (!spellScript && !auraScript)
+ {
+ TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(itr->second.first).c_str());
+
+ itr->second.second = false;
+ continue;
+ }
+
+ if (spellScript)
+ {
+ spellScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
+ spellScript->_Register();
+
+ if (!spellScript->_Validate(spellEntry))
+ itr->second.second = false;
+ }
+
+ if (auraScript)
+ {
+ auraScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
+ auraScript->_Register();
+
+ if (!auraScript->_Validate(spellEntry))
+ itr->second.second = false;
+ }
}
}
- ++count;
}
TC_LOG_INFO("server.loading", ">> Validated %u scripts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
@@ -8571,6 +8574,8 @@ void ObjectMgr::LoadScriptNames()
{
uint32 oldMSTime = getMSTime();
+ // We insert an empty placeholder here so we can use the
+ // script id 0 as dummy for "no script found".
_scriptNamesStore.emplace_back("");
QueryResult result = WorldDatabase.Query(
@@ -8612,18 +8617,18 @@ void ObjectMgr::LoadScriptNames()
std::sort(_scriptNamesStore.begin(), _scriptNamesStore.end());
-#ifdef SCRIPTS
- for (size_t i = 1; i < _scriptNamesStore.size(); ++i)
- UnusedScriptNames.push_back(_scriptNamesStore[i]);
-#endif
-
TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " ScriptNames in %u ms", _scriptNamesStore.size(), GetMSTimeDiffToNow(oldMSTime));
}
+ObjectMgr::ScriptNameContainer const& ObjectMgr::GetAllScriptNames() const
+{
+ return _scriptNamesStore;
+}
+
std::string const& ObjectMgr::GetScriptName(uint32 id) const
{
static std::string const empty = "";
- return id < _scriptNamesStore.size() ? _scriptNamesStore[id] : empty;
+ return (id < _scriptNamesStore.size()) ? _scriptNamesStore[id] : empty;
}
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 73c03849699..b59aa5a8d98 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -370,7 +370,7 @@ struct ScriptInfo
typedef std::multimap<uint32, ScriptInfo> ScriptMap;
typedef std::map<uint32, ScriptMap > ScriptMapMap;
-typedef std::multimap<uint32, uint32> SpellScriptsContainer;
+typedef std::multimap<uint32 /*spell id*/, std::pair<uint32 /*script id*/, bool /*disabled*/>> SpellScriptsContainer;
typedef std::pair<SpellScriptsContainer::iterator, SpellScriptsContainer::iterator> SpellScriptsBounds;
TC_GAME_API extern ScriptMapMap sSpellScripts;
TC_GAME_API extern ScriptMapMap sEventScripts;
@@ -1272,6 +1272,7 @@ class TC_GAME_API ObjectMgr
bool IsVendorItemValid(uint32 vendor_entry, uint32 item, int32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* player = NULL, std::set<uint32>* skip_vendors = NULL, uint32 ORnpcflag = 0) const;
void LoadScriptNames();
+ ScriptNameContainer const& GetAllScriptNames() const;
std::string const& GetScriptName(uint32 id) const;
uint32 GetScriptId(std::string const& name);
diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp
index 1575b50098f..0887d183a8b 100644
--- a/src/server/game/Instances/InstanceScript.cpp
+++ b/src/server/game/Instances/InstanceScript.cpp
@@ -29,6 +29,8 @@
#include "Pet.h"
#include "WorldSession.h"
#include "Opcodes.h"
+#include "ScriptReloadMgr.h"
+#include "ScriptMgr.h"
BossBoundaryData::~BossBoundaryData()
{
@@ -36,6 +38,18 @@ BossBoundaryData::~BossBoundaryData()
delete it->Boundary;
}
+InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0)
+{
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ uint32 scriptId = sObjectMgr->GetInstanceTemplate(map->GetId())->ScriptId;
+ auto const scriptname = sObjectMgr->GetScriptName(scriptId);
+ ASSERT(!scriptname.empty());
+ // Acquire a strong reference from the script module
+ // to keep it loaded until this object is destroyed.
+ module_reference = sScriptMgr->AcquireModuleReferenceOfScriptName(scriptname);
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
+}
+
void InstanceScript::SaveToDB()
{
std::string data = GetSaveData();
diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h
index 949cdf5abcb..3814afe2f4b 100644
--- a/src/server/game/Instances/InstanceScript.h
+++ b/src/server/game/Instances/InstanceScript.h
@@ -36,6 +36,7 @@ class Unit;
class Player;
class GameObject;
class Creature;
+class ModuleReference;
enum EncounterFrameType
{
@@ -137,7 +138,7 @@ typedef std::map<uint32 /*entry*/, uint32 /*type*/> ObjectInfoMap;
class TC_GAME_API InstanceScript : public ZoneScript
{
public:
- explicit InstanceScript(Map* map) : instance(map), completedEncounters(0) { }
+ explicit InstanceScript(Map* map);
virtual ~InstanceScript() { }
@@ -289,6 +290,11 @@ class TC_GAME_API InstanceScript : public ZoneScript
ObjectInfoMap _gameObjectInfo;
ObjectGuidMap _objectGuids;
uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets
+
+ #ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Strong reference to the associated script module
+ std::shared_ptr<ModuleReference> module_reference;
+ #endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
};
template<class AI, class T>
diff --git a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
index 7f0695e16b3..aa00d211d6e 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
@@ -33,8 +33,14 @@ void OutdoorPvPMgr::Die()
for (OutdoorPvPSet::iterator itr = m_OutdoorPvPSet.begin(); itr != m_OutdoorPvPSet.end(); ++itr)
delete *itr;
+ m_OutdoorPvPSet.clear();
+
for (OutdoorPvPDataMap::iterator itr = m_OutdoorPvPDatas.begin(); itr != m_OutdoorPvPDatas.end(); ++itr)
delete itr->second;
+
+ m_OutdoorPvPDatas.clear();
+
+ m_OutdoorPvPMap.clear();
}
OutdoorPvPMgr* OutdoorPvPMgr::instance()
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 26a7809eaa9..4c9487874de 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -17,6 +17,7 @@
*/
#include "ScriptMgr.h"
+#include "ScriptReloadMgr.h"
#include "Config.h"
#include "DatabaseEnv.h"
#include "DBCStores.h"
@@ -34,11 +35,9 @@
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Chat.h"
-
-// namespace
-// {
- UnusedScriptNamesContainer UnusedScriptNames;
-// }
+#include "MapManager.h"
+#include "LFGScripts.h"
+#include "InstanceScript.h"
// Trait which indicates whether this script type
// must be assigned in the database.
@@ -67,6 +66,10 @@ struct is_script_database_bound<GameObjectScript>
: std::true_type { };
template<>
+struct is_script_database_bound<VehicleScript>
+ : std::true_type { };
+
+template<>
struct is_script_database_bound<AreaTriggerScript>
: std::true_type { };
@@ -94,143 +97,838 @@ template<>
struct is_script_database_bound<AchievementCriteriaScript>
: std::true_type { };
+enum Spells
+{
+ SPELL_HOTSWAP_VISUAL_SPELL_EFFECT = 40162 // 59084
+};
+
+class ScriptRegistryInterface
+{
+public:
+ ScriptRegistryInterface() { }
+ virtual ~ScriptRegistryInterface() { }
+
+ ScriptRegistryInterface(ScriptRegistryInterface const&) = delete;
+ ScriptRegistryInterface(ScriptRegistryInterface&&) = delete;
+
+ ScriptRegistryInterface& operator= (ScriptRegistryInterface const&) = delete;
+ ScriptRegistryInterface& operator= (ScriptRegistryInterface&&) = delete;
+
+ /// Removes all scripts associated with the given script context.
+ /// Requires ScriptRegistryBase::SwapContext to be called after all transfers have finished.
+ virtual void ReleaseContext(std::string const& context) = 0;
+
+ /// Injects and updates the changed script objects.
+ virtual void SwapContext(bool initialize) = 0;
+
+ /// Removes the scripts used by this registry from the given container.
+ /// Used to find unused script names.
+ virtual void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) = 0;
+
+ /// Unloads the script registry.
+ virtual void Unload() = 0;
+};
+
+template<class>
+class ScriptRegistry;
+
+class ScriptRegistryCompositum
+ : public ScriptRegistryInterface
+{
+ ScriptRegistryCompositum() { }
+
+ template<class>
+ friend class ScriptRegistry;
+
+ /// Type erasure wrapper for objects
+ class DeleteableObjectBase
+ {
+ public:
+ DeleteableObjectBase() { }
+ virtual ~DeleteableObjectBase() { }
+
+ DeleteableObjectBase(DeleteableObjectBase const&) = delete;
+ DeleteableObjectBase& operator= (DeleteableObjectBase const&) = delete;
+ };
+
+ template<typename T>
+ class DeleteableObject
+ : public DeleteableObjectBase
+ {
+ public:
+ DeleteableObject(T&& object)
+ : _object(std::forward<T>(object)) { }
+
+ private:
+ T _object;
+ };
+
+public:
+ void SetScriptNameInContext(std::string const& scriptname, std::string const& context)
+ {
+ ASSERT(_scriptnames_to_context.find(scriptname) == _scriptnames_to_context.end());
+ _scriptnames_to_context.insert(std::make_pair(scriptname, context));
+ }
+
+ std::string const& GetScriptContextOfScriptName(std::string const& scriptname) const
+ {
+ auto itr = _scriptnames_to_context.find(scriptname);
+ ASSERT(itr != _scriptnames_to_context.end() &&
+ "Given scriptname doesn't exist!");
+ return itr->second;
+ }
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ for (auto itr = _scriptnames_to_context.begin();
+ itr != _scriptnames_to_context.end();)
+ if (itr->second == context)
+ itr = _scriptnames_to_context.erase(itr);
+ else
+ ++itr;
+
+ for (auto const registry : _registries)
+ registry->ReleaseContext(context);
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ for (auto const registry : _registries)
+ registry->SwapContext(initialize);
+
+ DoDelayedDelete();
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const registry : _registries)
+ registry->RemoveUsedScriptsFromContainer(scripts);
+ }
+
+ void Unload() final override
+ {
+ for (auto const registry : _registries)
+ registry->Unload();
+ }
+
+ template<typename T>
+ void QueueForDelayedDelete(T&& any)
+ {
+ _delayed_delete_queue.push_back(
+ Trinity::make_unique<
+ DeleteableObject<typename std::decay<T>::type>
+ >(std::forward<T>(any))
+ );
+ }
+
+ static ScriptRegistryCompositum* Instance()
+ {
+ static ScriptRegistryCompositum instance;
+ return &instance;
+ }
+
+private:
+ void Register(ScriptRegistryInterface* registry)
+ {
+ _registries.insert(registry);
+ }
+
+ void DoDelayedDelete()
+ {
+ _delayed_delete_queue.clear();
+ }
+
+ std::unordered_set<ScriptRegistryInterface*> _registries;
+
+ std::vector<std::unique_ptr<DeleteableObjectBase>> _delayed_delete_queue;
+
+ std::unordered_map<
+ std::string /*script name*/,
+ std::string /*context*/
+ > _scriptnames_to_context;
+};
+
+#define sScriptRegistryCompositum ScriptRegistryCompositum::Instance()
+
+template<typename /*ScriptType*/, bool /*IsDatabaseBound*/>
+class SpecializedScriptRegistry;
+
// This is the global static registry of scripts.
-template<class TScript>
-class ScriptRegistry
+template<class ScriptType>
+class ScriptRegistry final
+ : public SpecializedScriptRegistry<
+ ScriptType, is_script_database_bound<ScriptType>::value>
+{
+ ScriptRegistry()
+ {
+ sScriptRegistryCompositum->Register(this);
+ }
+
+public:
+ static ScriptRegistry* Instance()
+ {
+ static ScriptRegistry instance;
+ return &instance;
+ }
+
+ void LogDuplicatedScriptPointerError(ScriptType const* first, ScriptType const* second)
+ {
+ // See if the script is using the same memory as another script. If this happens, it means that
+ // someone forgot to allocate new memory for a script.
+ TC_LOG_ERROR("scripts", "Script '%s' has same memory pointer as '%s'.",
+ first->GetName().c_str(), second->GetName().c_str());
+ }
+};
+
+class ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHookBase() { }
+ virtual ~ScriptRegistrySwapHookBase() { }
+
+ ScriptRegistrySwapHookBase(ScriptRegistrySwapHookBase const&) = delete;
+ ScriptRegistrySwapHookBase(ScriptRegistrySwapHookBase&&) = delete;
+
+ ScriptRegistrySwapHookBase& operator= (ScriptRegistrySwapHookBase const&) = delete;
+ ScriptRegistrySwapHookBase& operator= (ScriptRegistrySwapHookBase&&) = delete;
+
+ /// Called before the actual context release happens
+ virtual void BeforeReleaseContext(std::string const& /*context*/) { }
+
+ /// Called before SwapContext
+ virtual void BeforeSwapContext() { }
+
+ /// Called before Unload
+ virtual void BeforeUnload() { }
+};
+
+template<typename ScriptType, typename Base>
+class ScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
+{
+};
+
+/// This hook is responsible for swapping OutdoorPvP's
+template<typename Base>
+class UnsupportedScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+ ASSERT(bounds.first == bounds.second);
+ }
+};
+
+/// This hook is responsible for swapping Creature and GameObject AI's
+template<typename ObjectType, typename ScriptType, typename Base>
+class CreatureGameObjectScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
{
+ template<typename W>
+ class AIFunctionMapWorker
+ {
public:
+ template<typename T>
+ AIFunctionMapWorker(T&& worker)
+ : _worker(std::forward<T>(worker)) { }
+
+ void Visit(std::unordered_map<ObjectGuid, ObjectType*>& objects)
+ {
+ _worker(objects);
+ }
+
+ template<typename O>
+ void Visit(std::unordered_map<ObjectGuid, O*>&) { }
- typedef std::map<uint32, TScript*> ScriptMap;
- typedef typename ScriptMap::iterator ScriptMapIterator;
+ private:
+ W _worker;
+ };
- // The actual list of scripts. This will be accessed concurrently, so it must not be modified
- // after server startup.
- static ScriptMap ScriptPointerList;
- static std::vector<TScript*> Scripts;
+ class AsyncCastHotswapEffectEvent : public BasicEvent
+ {
+ public:
+ explicit AsyncCastHotswapEffectEvent(Unit* owner) : owner_(owner) { }
- static void AddScript(TScript* const script, bool addToDeleteContainer = true)
+ bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override
{
- ASSERT(script);
+ owner_->CastSpell(owner_, SPELL_HOTSWAP_VISUAL_SPELL_EFFECT, true);
+ return true;
+ }
+
+ private:
+ Unit* owner_;
+ };
+
+ // Hook which is called before a creature is swapped
+ static void UnloadStage1(Creature* creature)
+ {
+ if (creature->IsCharmed())
+ creature->RemoveCharmedBy(nullptr);
+
+ ASSERT(!creature->IsCharmed(),
+ "There is a disabled AI which is still loaded.");
+
+ creature->AI()->EnterEvadeMode();
+ }
+
+ static void UnloadStage2(Creature* creature)
+ {
+ bool const destroyed = creature->AIM_Destroy();
+ ASSERT(destroyed,
+ "Destroying the AI should never fail here!");
+ (void)destroyed;
+
+ ASSERT(!creature->AI(),
+ "The AI should be null here!");
+ }
+
+ // Hook which is called before a gameobject is swapped
+ static void UnloadStage1(GameObject* gameobject)
+ {
+ gameobject->AI()->Reset();
+ }
+
+ static void UnloadStage2(GameObject* gameobject)
+ {
+ gameobject->AIM_Destroy();
- // See if the script is using the same memory as another script. If this happens, it means that
- // someone forgot to allocate new memory for a script.
- for (ScriptMapIterator it = ScriptPointerList.begin(); it != ScriptPointerList.end(); ++it)
+ ASSERT(!gameobject->AI(),
+ "The AI should be null here!");
+ }
+
+ // Hook which is called after a creature was swapped
+ static void LoadStage1(Creature* creature)
+ {
+ ASSERT(!creature->AI(),
+ "The AI should be null here!");
+
+ if (creature->IsAlive())
+ creature->ClearUnitState(UNIT_STATE_EVADE);
+
+ bool const created = creature->AIM_Initialize();
+ ASSERT(created,
+ "Creating the AI should never fail here!");
+ (void)created;
+ }
+
+ static void LoadStage2(Creature* creature)
+ {
+ if (!creature->IsAlive())
+ return;
+
+ creature->AI()->EnterEvadeMode();
+
+ // Cast a dummy visual spell asynchronously here to signal
+ // that the AI was hot swapped
+ creature->m_Events.AddEvent(new AsyncCastHotswapEffectEvent(creature),
+ creature->m_Events.CalculateTime(0));
+ }
+
+ // Hook which is called after a gameobject was swapped
+ static void LoadStage1(GameObject* gameobject)
+ {
+ ASSERT(!gameobject->AI(),
+ "The AI should be null here!");
+
+ gameobject->AIM_Initialize();
+ }
+
+ static void LoadStage2(GameObject* gameobject)
+ {
+ gameobject->AI()->Reset();
+ }
+
+ template<typename T>
+ void RunOverAllEntities(T fn)
+ {
+ auto evaluator = [&](std::unordered_map<ObjectGuid, ObjectType*>& objects)
+ {
+ for (auto object : objects)
+ fn(object.second);
+ };
+
+ AIFunctionMapWorker<typename std::decay<decltype(evaluator)>::type> worker(std::move(evaluator));
+ TypeContainerVisitor<decltype(worker), MapStoredObjectTypesContainer> visitor(worker);
+
+ sMapMgr->DoForAllMaps([&](Map* map)
+ {
+ // Run the worker over all maps
+ visitor.Visit(map->GetObjectsStore());
+ });
+ }
+
+public:
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto ids_to_remove = static_cast<Base*>(this)->GetScriptIDsToRemove(context);
+
+ std::vector<ObjectType*> stage2;
+
+ RunOverAllEntities([&](ObjectType* object)
+ {
+ if (ids_to_remove.find(object->GetScriptId()) != ids_to_remove.end())
{
- if (it->second == script)
- {
- TC_LOG_ERROR("scripts", "Script '%s' has same memory pointer as '%s'.",
- script->GetName().c_str(), it->second->GetName().c_str());
+ UnloadStage1(object);
+ stage2.push_back(object);
+ }
+ });
- return;
+ for (auto object : stage2)
+ UnloadStage2(object);
+
+ // Add the new ids which are removed to the global ids to remove set
+ ids_removed_.insert(ids_to_remove.begin(), ids_to_remove.end());
+ }
+
+ void BeforeSwapContext() final override
+ {
+ // Add the recently added scripts to the deleted scripts to replace
+ // default AI's with recently added core scripts.
+ ids_removed_.insert(static_cast<Base*>(this)->GetRecentlyAddedScriptIDs().begin(),
+ static_cast<Base*>(this)->GetRecentlyAddedScriptIDs().end());
+
+ std::vector<ObjectType*> remove;
+ std::vector<ObjectType*> stage2;
+
+ RunOverAllEntities([&](ObjectType* object)
+ {
+ if (ids_removed_.find(object->GetScriptId()) != ids_removed_.end())
+ {
+ if (object->AI())
+ {
+ // Overwrite existing (default) AI's which are replaced by a new script
+ UnloadStage1(object);
+ remove.push_back(object);
}
+
+ stage2.push_back(object);
}
+ });
+
+ for (auto object : remove)
+ UnloadStage2(object);
+
+ for (auto object : stage2)
+ LoadStage1(object);
+
+ for (auto object : stage2)
+ LoadStage2(object);
+
+ ids_removed_.clear();
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(ids_removed_.empty());
+ }
- AddScript(is_script_database_bound<TScript>{}, script);
- if (addToDeleteContainer)
- Scripts.push_back(script);
+private:
+ std::unordered_set<uint32> ids_removed_;
+};
+
+// This hook is responsible for swapping CreatureAI's
+template<typename Base>
+class ScriptRegistrySwapHooks<CreatureScript, Base>
+ : public CreatureGameObjectScriptRegistrySwapHooks<
+ Creature, CreatureScript, Base
+ > { };
+
+// This hook is responsible for swapping GameObjectAI's
+template<typename Base>
+class ScriptRegistrySwapHooks<GameObjectScript, Base>
+ : public CreatureGameObjectScriptRegistrySwapHooks<
+ GameObject, GameObjectScript, Base
+ > { };
+
+/// This hook is responsible for swapping BattlegroundScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<BattlegroundScript, Base>
+ : public UnsupportedScriptRegistrySwapHooks<Base> { };
+
+/// This hook is responsible for swapping OutdoorPvP's
+template<typename Base>
+class ScriptRegistrySwapHooks<OutdoorPvPScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+
+ if ((!swapped) && (bounds.first != bounds.second))
+ {
+ swapped = true;
+ sOutdoorPvPMgr->Die();
}
+ }
- // Gets a script by its ID (assigned by ObjectMgr).
- static TScript* GetScriptById(uint32 id)
+ void BeforeSwapContext() final override
+ {
+ if (swapped)
{
- ScriptMapIterator it = ScriptPointerList.find(id);
- if (it != ScriptPointerList.end())
- return it->second;
+ sOutdoorPvPMgr->InitOutdoorPvP();
+ swapped = false;
+ }
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+/// This hook is responsible for swapping InstanceMapScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<InstanceMapScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+ if (bounds.first != bounds.second)
+ swapped = true;
+ }
+
+ void BeforeSwapContext() final override
+ {
+ swapped = false;
+ }
- return NULL;
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+/// This hook is responsible for swapping SpellScriptLoader's
+template<typename Base>
+class ScriptRegistrySwapHooks<SpellScriptLoader, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+
+ if (bounds.first != bounds.second)
+ swapped = true;
+ }
+
+ void BeforeSwapContext() final override
+ {
+ if (swapped)
+ {
+ sObjectMgr->ValidateSpellScripts();
+ swapped = false;
}
+ }
- private:
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+// Database bound script registry
+template<typename ScriptType>
+class SpecializedScriptRegistry<ScriptType, true>
+ : public ScriptRegistryInterface,
+ public ScriptRegistrySwapHooks<ScriptType, ScriptRegistry<ScriptType>>
+{
+ template<typename>
+ friend class UnsupportedScriptRegistrySwapHooks;
+
+ template<typename, typename>
+ friend class ScriptRegistrySwapHooks;
+
+ template<typename, typename, typename>
+ friend class CreatureGameObjectScriptRegistrySwapHooks;
+
+public:
+ SpecializedScriptRegistry() { }
- // Adds a database bound script
- static void AddScript(std::true_type, TScript* const script)
+ typedef std::unordered_map<
+ uint32 /*script id*/,
+ std::unique_ptr<ScriptType>
+ > ScriptStoreType;
+
+ typedef typename ScriptStoreType::iterator ScriptStoreIteratorType;
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ this->BeforeReleaseContext(context);
+
+ auto const bounds = _ids_of_contexts.equal_range(context);
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
+ _scripts.erase(itr->second);
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ if (!initialize)
+ this->BeforeSwapContext();
+
+ _recently_added_ids.clear();
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const& script : _scripts)
+ scripts.erase(script.second->GetName());
+ }
+
+ void Unload() final override
+ {
+ this->BeforeUnload();
+
+ ASSERT(_recently_added_ids.empty(),
+ "Recently added script ids should be empty here!");
+
+ _scripts.clear();
+ _ids_of_contexts.clear();
+ }
+
+ // Adds a database bound script
+ void AddScript(ScriptType* script)
+ {
+ ASSERT(script,
+ "Tried to call AddScript with a nullpointer!");
+ ASSERT(!sScriptMgr->GetCurrentScriptContext().empty(),
+ "Tried to register a script without being in a valid script context!");
+
+ 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. An ID only exists if it's a script that is assigned in the database
- // through a script name (or similar).
- uint32 id = sObjectMgr->GetScriptId(script->GetName());
- if (id)
+ // Try to find an existing script.
+ for (auto const& stored_script : _scripts)
{
- // Try to find an existing script.
- bool existing = false;
- for (ScriptMapIterator it = ScriptPointerList.begin(); it != ScriptPointerList.end(); ++it)
- {
- // If the script names match...
- if (it->second->GetName() == script->GetName())
- {
- // ... It exists.
- existing = true;
- break;
- }
- }
-
- // If the script isn't assigned -> assign it!
- if (!existing)
- {
- ScriptPointerList[id] = script;
- sScriptMgr->IncrementScriptCount();
-
- #ifdef SCRIPTS
- UnusedScriptNamesContainer::iterator itr = std::lower_bound(UnusedScriptNames.begin(), UnusedScriptNames.end(), script->GetName());
- if (itr != UnusedScriptNames.end() && *itr == script->GetName())
- UnusedScriptNames.erase(itr);
- #endif
- }
- else
+ // 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());
+ TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, "
+ "so the script can't work.", script->GetName().c_str());
- ABORT(); // Error that should be fixed ASAP.
+ // 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());
- }
- }
- // Adds a non database bound script
- static void AddScript(std::false_type, TScript* const script)
+ // 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());
+ }
+ else
{
- // We're dealing with a code-only script; just add it.
- ScriptPointerList[_scriptIdCounter++] = script;
- sScriptMgr->IncrementScriptCount();
+ // 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;
}
+ }
+
+ // Gets a script by its ID (assigned by ObjectMgr).
+ ScriptType* GetScriptById(uint32 id)
+ {
+ auto const itr = _scripts.find(id);
+ if (itr != _scripts.end())
+ return itr->second.get();
+
+ return nullptr;
+ }
+
+ ScriptStoreType& GetScripts()
+ {
+ return _scripts;
+ }
+
+protected:
+ // Returns the script id's which are registered to a certain context
+ std::unordered_set<uint32> GetScriptIDsToRemove(std::string const& context) const
+ {
+ // Create a set of all ids which are removed
+ std::unordered_set<uint32> scripts_to_remove;
+
+ auto const bounds = _ids_of_contexts.equal_range(context);
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
+ scripts_to_remove.insert(itr->second);
+
+ return scripts_to_remove;
+ }
+
+ std::unordered_set<uint32> const& GetRecentlyAddedScriptIDs() const
+ {
+ return _recently_added_ids;
+ }
+
+private:
+ ScriptStoreType _scripts;
+
+ // Scripts of a specific context
+ std::unordered_multimap<std::string /*context*/, uint32 /*id*/> _ids_of_contexts;
+
+ // Script id's which were registered recently
+ std::unordered_set<uint32> _recently_added_ids;
+};
+
+/// This hook is responsible for swapping CommandScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<CommandScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ void BeforeReleaseContext(std::string const& /*context*/) final override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+
+ void BeforeSwapContext() final override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+
+ void BeforeUnload() final override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+};
+
+// Database unbound script registry
+template<typename ScriptType>
+class SpecializedScriptRegistry<ScriptType, false>
+ : public ScriptRegistryInterface,
+ public ScriptRegistrySwapHooks<ScriptType, ScriptRegistry<ScriptType>>
+{
+ template<typename, typename>
+ friend class ScriptRegistrySwapHooks;
+
+public:
+ typedef std::unordered_multimap<std::string /*context*/, std::unique_ptr<ScriptType>> ScriptStoreType;
+ typedef typename ScriptStoreType::iterator ScriptStoreIteratorType;
+
+ SpecializedScriptRegistry() { }
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ this->BeforeReleaseContext(context);
+
+ _scripts.erase(context);
+ }
+
+ void SwapContext(bool) final override
+ {
+ this->BeforeSwapContext();
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const& script : _scripts)
+ scripts.erase(script.second->GetName());
+ }
+
+ void Unload() final override
+ {
+ this->BeforeUnload();
+
+ _scripts.clear();
+ }
+
+ // Adds a non database bound script
+ void AddScript(ScriptType* script)
+ {
+ ASSERT(script,
+ "Tried to call AddScript with a nullpointer!");
+ ASSERT(!sScriptMgr->GetCurrentScriptContext().empty(),
+ "Tried to register a script without being in a valid script context!");
+
+ std::unique_ptr<ScriptType> script_ptr(script);
+
+ for (auto const& entry : _scripts)
+ if (entry.second.get() == script)
+ {
+ static_cast<ScriptRegistry<ScriptType>*>(this)->
+ LogDuplicatedScriptPointerError(script, entry.second.get());
+
+ sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr));
+ return;
+ }
+
+ // We're dealing with a code-only script, just add it.
+ _scripts.insert(std::make_pair(sScriptMgr->GetCurrentScriptContext(), std::move(script_ptr)));
+ }
- // Counter used for code-only scripts.
- static uint32 _scriptIdCounter;
+ ScriptStoreType& GetScripts()
+ {
+ return _scripts;
+ }
+
+private:
+ ScriptStoreType _scripts;
};
// Utility macros to refer to the script registry.
-#define SCR_REG_MAP(T) ScriptRegistry<T>::ScriptMap
-#define SCR_REG_ITR(T) ScriptRegistry<T>::ScriptMapIterator
-#define SCR_REG_LST(T) ScriptRegistry<T>::ScriptPointerList
-#define SCR_REG_VEC(T) ScriptRegistry<T>::Scripts
+#define SCR_REG_MAP(T) ScriptRegistry<T>::ScriptStoreType
+#define SCR_REG_ITR(T) ScriptRegistry<T>::ScriptStoreIteratorType
+#define SCR_REG_LST(T) ScriptRegistry<T>::Instance()->GetScripts()
// Utility macros for looping over scripts.
#define FOR_SCRIPTS(T, C, E) \
if (SCR_REG_LST(T).empty()) \
return; \
+ \
for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \
C != SCR_REG_LST(T).end(); ++C)
+
#define FOR_SCRIPTS_RET(T, C, E, R) \
if (SCR_REG_LST(T).empty()) \
return R; \
+ \
for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \
C != SCR_REG_LST(T).end(); ++C)
+
#define FOREACH_SCRIPT(T) \
FOR_SCRIPTS(T, itr, end) \
- itr->second
+ itr->second
// Utility macros for finding specific scripts.
#define GET_SCRIPT(T, I, V) \
- T* V = ScriptRegistry<T>::GetScriptById(I); \
+ T* V = ScriptRegistry<T>::Instance()->GetScriptById(I); \
if (!V) \
return;
+
#define GET_SCRIPT_RET(T, I, V, R) \
- T* V = ScriptRegistry<T>::GetScriptById(I); \
+ T* V = ScriptRegistry<T>::Instance()->GetScriptById(I); \
if (!V) \
return R;
@@ -240,6 +938,16 @@ struct TSpellSummary
uint8 Effects; // set of enum SelectEffect
} *SpellSummary;
+ScriptObject::ScriptObject(const char* name) : _name(name)
+{
+ sScriptMgr->IncreaseScriptCount();
+}
+
+ScriptObject::~ScriptObject()
+{
+ sScriptMgr->DecreaseScriptCount();
+}
+
ScriptMgr::ScriptMgr()
: _scriptCount(0), _script_loader_callback(nullptr)
{
@@ -255,6 +963,9 @@ ScriptMgr* ScriptMgr::instance()
void ScriptMgr::Initialize()
{
+ ASSERT(sSpellMgr->GetSpellInfo(SPELL_HOTSWAP_VISUAL_SPELL_EFFECT)
+ && "Reload hotswap spell effect for creatures isn't valid!");
+
uint32 oldMSTime = getMSTime();
LoadDatabase();
@@ -263,80 +974,84 @@ void ScriptMgr::Initialize()
FillSpellSummary();
- // Load core script systems
+ // Load core scripts
+ SetScriptContext("___static___");
+
// SmartAI
- BeginScriptContext("core scripts");
AddSC_SmartScripts();
- FinishScriptContext();
+
+ // LFGScripts
+ lfg::AddSC_LFGScripts();
// Load all static linked scripts through the script loader function.
- BeginScriptContext("static scripts");
ASSERT(_script_loader_callback,
"Script loader callback wasn't registered!");
_script_loader_callback();
- FinishScriptContext();
-#ifdef SCRIPTS
- for (std::string const& scriptName : UnusedScriptNames)
+ // Initialize all dynamic scripts
+ // and finishes the context switch to do
+ // bulk loading
+ sScriptReloadMgr->Initialize();
+
+ // Loads all scripts from the current context
+ sScriptMgr->SwapScriptContext(true);
+
+ // Print unused script names.
+ std::unordered_set<std::string> unusedScriptNames(
+ sObjectMgr->GetAllScriptNames().begin(),
+ sObjectMgr->GetAllScriptNames().end());
+
+ // Remove the used scripts from the given container.
+ sScriptRegistryCompositum->RemoveUsedScriptsFromContainer(unusedScriptNames);
+
+ for (std::string const& scriptName : unusedScriptNames)
{
- TC_LOG_ERROR("sql.sql", "ScriptName '%s' exists in database, but no core script found!", scriptName.c_str());
+ // Avoid complaining about empty script names since the
+ // script name container contains a placeholder as the 0 element.
+ if (scriptName.empty())
+ continue;
+
+ TC_LOG_ERROR("sql.sql", "ScriptName '%s' exists in database, "
+ "but no core script found!", scriptName.c_str());
}
-#endif
- TC_LOG_INFO("server.loading", ">> Loaded %u C++ scripts in %u ms", GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u C++ scripts in %u ms",
+ GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
}
-void ScriptMgr::BeginScriptContext(std::string const& context)
+void ScriptMgr::SetScriptContext(std::string const& context)
{
_currentContext = context;
}
-void ScriptMgr::FinishScriptContext()
+void ScriptMgr::SwapScriptContext(bool initialize)
{
+ sScriptRegistryCompositum->SwapContext(initialize);
_currentContext.clear();
}
-void ScriptMgr::ReleaseScriptContext(std::string const& /*context*/)
+void ScriptMgr::ReleaseScriptContext(std::string const& context)
{
- // ToDo
+ sScriptRegistryCompositum->ReleaseContext(context);
+}
+
+std::shared_ptr<ModuleReference>
+ ScriptMgr::AcquireModuleReferenceOfScriptName(std::string const& scriptname) const
+{
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Returns the reference to the module of the given scriptname
+ return ScriptReloadMgr::AcquireModuleReferenceOfContext(
+ sScriptRegistryCompositum->GetScriptContextOfScriptName(scriptname));
+#else
+ // Something went wrong when this function is used in
+ // a static linked context.
+ WPAbort();
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
}
void ScriptMgr::Unload()
{
- #define SCR_CLEAR(T) \
- for (T* scr : SCR_REG_VEC(T)) \
- delete scr; \
- SCR_REG_VEC(T).clear();
-
- // Clear scripts for every script type.
- SCR_CLEAR(SpellScriptLoader);
- SCR_CLEAR(ServerScript);
- SCR_CLEAR(WorldScript);
- SCR_CLEAR(FormulaScript);
- SCR_CLEAR(WorldMapScript);
- SCR_CLEAR(InstanceMapScript);
- SCR_CLEAR(BattlegroundMapScript);
- SCR_CLEAR(ItemScript);
- SCR_CLEAR(CreatureScript);
- SCR_CLEAR(GameObjectScript);
- SCR_CLEAR(AreaTriggerScript);
- SCR_CLEAR(BattlegroundScript);
- SCR_CLEAR(OutdoorPvPScript);
- SCR_CLEAR(CommandScript);
- SCR_CLEAR(WeatherScript);
- SCR_CLEAR(AuctionHouseScript);
- SCR_CLEAR(ConditionScript);
- SCR_CLEAR(VehicleScript);
- SCR_CLEAR(DynamicObjectScript);
- SCR_CLEAR(TransportScript);
- SCR_CLEAR(AchievementCriteriaScript);
- SCR_CLEAR(PlayerScript);
- SCR_CLEAR(AccountScript);
- SCR_CLEAR(GuildScript);
- SCR_CLEAR(GroupScript);
- SCR_CLEAR(UnitScript);
-
- #undef SCR_CLEAR
+ sScriptRegistryCompositum->Unload();
delete[] SpellSummary;
delete[] UnitAI::AISpellInfo;
@@ -434,38 +1149,22 @@ void ScriptMgr::FillSpellSummary()
}
}
-void ScriptMgr::CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector)
+template<typename T, typename F>
+void CreateSpellOrAuraScripts(uint32 spellId, std::list<T*>& scriptVector, F&& extractor)
{
SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
{
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
- if (!tmpscript)
+ // When the script is disabled continue with the next one
+ if (itr->second.second)
continue;
- SpellScript* script = tmpscript->GetSpellScript();
-
- if (!script)
- continue;
-
- script->_Init(&tmpscript->GetName(), spellId);
-
- scriptVector.push_back(script);
- }
-}
-
-void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector)
-{
- SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
-
- for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
- {
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
+ SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::Instance()->GetScriptById(itr->second.first);
if (!tmpscript)
continue;
- AuraScript* script = tmpscript->GetAuraScript();
+ T* script = (*tmpscript.*extractor)();
if (!script)
continue;
@@ -476,19 +1175,19 @@ void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& script
}
}
-void ScriptMgr::CreateSpellScriptLoaders(uint32 spellId, std::vector<std::pair<SpellScriptLoader*, SpellScriptsContainer::iterator> >& scriptVector)
+void ScriptMgr::CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector)
{
- SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
- scriptVector.reserve(std::distance(bounds.first, bounds.second));
+ CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetSpellScript);
+}
- for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
- {
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
- if (!tmpscript)
- continue;
+void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector)
+{
+ CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetAuraScript);
+}
- scriptVector.push_back(std::make_pair(tmpscript, itr));
- }
+SpellScriptLoader* ScriptMgr::GetSpellScriptLoader(uint32 scriptId)
+{
+ return ScriptRegistry<SpellScriptLoader>::Instance()->GetScriptById(scriptId);
}
void ScriptMgr::OnNetworkStart()
@@ -1516,56 +2215,62 @@ void ScriptMgr::OnGroupDisband(Group* group)
void ScriptMgr::OnHeal(Unit* healer, Unit* reciever, uint32& gain)
{
FOREACH_SCRIPT(UnitScript)->OnHeal(healer, reciever, gain);
+ FOREACH_SCRIPT(PlayerScript)->OnHeal(healer, reciever, gain);
}
void ScriptMgr::OnDamage(Unit* attacker, Unit* victim, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->OnDamage(attacker, victim, damage);
+ FOREACH_SCRIPT(PlayerScript)->OnDamage(attacker, victim, damage);
}
void ScriptMgr::ModifyPeriodicDamageAurasTick(Unit* target, Unit* attacker, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifyPeriodicDamageAurasTick(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifyPeriodicDamageAurasTick(target, attacker, damage);
}
void ScriptMgr::ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifyMeleeDamage(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifyMeleeDamage(target, attacker, damage);
}
void ScriptMgr::ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifySpellDamageTaken(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifySpellDamageTaken(target, attacker, damage);
}
SpellScriptLoader::SpellScriptLoader(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<SpellScriptLoader>::AddScript(this);
+ ScriptRegistry<SpellScriptLoader>::Instance()->AddScript(this);
}
ServerScript::ServerScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ServerScript>::AddScript(this);
+ ScriptRegistry<ServerScript>::Instance()->AddScript(this);
}
WorldScript::WorldScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<WorldScript>::AddScript(this);
+ ScriptRegistry<WorldScript>::Instance()->AddScript(this);
}
FormulaScript::FormulaScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<FormulaScript>::AddScript(this);
+ ScriptRegistry<FormulaScript>::Instance()->AddScript(this);
}
UnitScript::UnitScript(const char* name, bool addToScripts)
: ScriptObject(name)
{
- ScriptRegistry<UnitScript>::AddScript(this, addToScripts);
+ if (addToScripts)
+ ScriptRegistry<UnitScript>::Instance()->AddScript(this);
}
WorldMapScript::WorldMapScript(const char* name, uint32 mapId)
@@ -1574,7 +2279,7 @@ WorldMapScript::WorldMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsWorldMap())
TC_LOG_ERROR("scripts", "WorldMapScript for map %u is invalid.", mapId);
- ScriptRegistry<WorldMapScript>::AddScript(this);
+ ScriptRegistry<WorldMapScript>::Instance()->AddScript(this);
}
InstanceMapScript::InstanceMapScript(const char* name, uint32 mapId)
@@ -1583,7 +2288,7 @@ InstanceMapScript::InstanceMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsDungeon())
TC_LOG_ERROR("scripts", "InstanceMapScript for map %u is invalid.", mapId);
- ScriptRegistry<InstanceMapScript>::AddScript(this);
+ ScriptRegistry<InstanceMapScript>::Instance()->AddScript(this);
}
BattlegroundMapScript::BattlegroundMapScript(const char* name, uint32 mapId)
@@ -1592,122 +2297,117 @@ BattlegroundMapScript::BattlegroundMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsBattleground())
TC_LOG_ERROR("scripts", "BattlegroundMapScript for map %u is invalid.", mapId);
- ScriptRegistry<BattlegroundMapScript>::AddScript(this);
+ ScriptRegistry<BattlegroundMapScript>::Instance()->AddScript(this);
}
ItemScript::ItemScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ItemScript>::AddScript(this);
+ ScriptRegistry<ItemScript>::Instance()->AddScript(this);
}
CreatureScript::CreatureScript(const char* name)
: UnitScript(name, false)
{
- ScriptRegistry<CreatureScript>::AddScript(this);
+ ScriptRegistry<CreatureScript>::Instance()->AddScript(this);
}
GameObjectScript::GameObjectScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GameObjectScript>::AddScript(this);
+ ScriptRegistry<GameObjectScript>::Instance()->AddScript(this);
}
AreaTriggerScript::AreaTriggerScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AreaTriggerScript>::AddScript(this);
+ ScriptRegistry<AreaTriggerScript>::Instance()->AddScript(this);
}
BattlegroundScript::BattlegroundScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<BattlegroundScript>::AddScript(this);
+ ScriptRegistry<BattlegroundScript>::Instance()->AddScript(this);
}
OutdoorPvPScript::OutdoorPvPScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<OutdoorPvPScript>::AddScript(this);
+ ScriptRegistry<OutdoorPvPScript>::Instance()->AddScript(this);
}
CommandScript::CommandScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<CommandScript>::AddScript(this);
+ ScriptRegistry<CommandScript>::Instance()->AddScript(this);
}
WeatherScript::WeatherScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<WeatherScript>::AddScript(this);
+ ScriptRegistry<WeatherScript>::Instance()->AddScript(this);
}
AuctionHouseScript::AuctionHouseScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AuctionHouseScript>::AddScript(this);
+ ScriptRegistry<AuctionHouseScript>::Instance()->AddScript(this);
}
ConditionScript::ConditionScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ConditionScript>::AddScript(this);
+ ScriptRegistry<ConditionScript>::Instance()->AddScript(this);
}
VehicleScript::VehicleScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<VehicleScript>::AddScript(this);
+ ScriptRegistry<VehicleScript>::Instance()->AddScript(this);
}
DynamicObjectScript::DynamicObjectScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<DynamicObjectScript>::AddScript(this);
+ ScriptRegistry<DynamicObjectScript>::Instance()->AddScript(this);
}
TransportScript::TransportScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<TransportScript>::AddScript(this);
+ ScriptRegistry<TransportScript>::Instance()->AddScript(this);
}
AchievementCriteriaScript::AchievementCriteriaScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AchievementCriteriaScript>::AddScript(this);
+ ScriptRegistry<AchievementCriteriaScript>::Instance()->AddScript(this);
}
PlayerScript::PlayerScript(const char* name)
: UnitScript(name, false)
{
- ScriptRegistry<PlayerScript>::AddScript(this);
+ ScriptRegistry<PlayerScript>::Instance()->AddScript(this);
}
AccountScript::AccountScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AccountScript>::AddScript(this);
+ ScriptRegistry<AccountScript>::Instance()->AddScript(this);
}
GuildScript::GuildScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GuildScript>::AddScript(this);
+ ScriptRegistry<GuildScript>::Instance()->AddScript(this);
}
GroupScript::GroupScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GroupScript>::AddScript(this);
+ ScriptRegistry<GroupScript>::Instance()->AddScript(this);
}
-// Instantiate static members of ScriptRegistry.
-template<class TScript> std::map<uint32, TScript*> ScriptRegistry<TScript>::ScriptPointerList;
-template<class TScript> std::vector<TScript*> ScriptRegistry<TScript>::Scripts;
-template<class TScript> uint32 ScriptRegistry<TScript>::_scriptIdCounter = 0;
-
// Specialize for each script type class like so:
template class TC_GAME_API ScriptRegistry<SpellScriptLoader>;
template class TC_GAME_API ScriptRegistry<ServerScript>;
@@ -1735,13 +2435,3 @@ template class TC_GAME_API ScriptRegistry<GuildScript>;
template class TC_GAME_API ScriptRegistry<GroupScript>;
template class TC_GAME_API ScriptRegistry<UnitScript>;
template class TC_GAME_API ScriptRegistry<AccountScript>;
-
-// Undefine utility macros.
-#undef GET_SCRIPT_RET
-#undef GET_SCRIPT
-#undef FOREACH_SCRIPT
-#undef FOR_SCRIPTS_RET
-#undef FOR_SCRIPTS
-#undef SCR_REG_LST
-#undef SCR_REG_ITR
-#undef SCR_REG_MAP
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index fb4f798bcc9..cc1b65fa593 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -46,6 +46,7 @@ class InstanceMap;
class InstanceScript;
class Item;
class Map;
+class ModuleReference;
class OutdoorPvP;
class Player;
class Quest;
@@ -157,14 +158,8 @@ class TC_GAME_API ScriptObject
protected:
- ScriptObject(const char* name)
- : _name(name)
- {
- }
-
- virtual ~ScriptObject()
- {
- }
+ ScriptObject(const char* name);
+ virtual ~ScriptObject();
private:
@@ -338,7 +333,8 @@ class TC_GAME_API WorldMapScript : public ScriptObject, public MapScript<Map>
WorldMapScript(const char* name, uint32 mapId);
};
-class TC_GAME_API InstanceMapScript : public ScriptObject, public MapScript<InstanceMap>
+class TC_GAME_API InstanceMapScript
+ : public ScriptObject, public MapScript<InstanceMap>
{
protected:
@@ -834,13 +830,6 @@ class TC_GAME_API GroupScript : public ScriptObject
virtual void OnDisband(Group* /*group*/) { }
};
-
-// namespace
-// {
- typedef std::list<std::string> UnusedScriptNamesContainer;
- TC_GAME_API extern UnusedScriptNamesContainer UnusedScriptNames;
-// }
-
// Manages registration, loading, and execution of scripts.
class TC_GAME_API ScriptMgr
{
@@ -853,14 +842,14 @@ class TC_GAME_API ScriptMgr
void FillSpellSummary();
void LoadDatabase();
+ void IncreaseScriptCount() { ++_scriptCount; }
+ void DecreaseScriptCount() { --_scriptCount; }
+
public: /* Initialization */
static ScriptMgr* instance();
void Initialize();
- const char* ScriptsVersion() const { return "Integrated Trinity Scripts"; }
-
- void IncrementScriptCount() { ++_scriptCount; }
uint32 GetScriptCount() const { return _scriptCount; }
typedef void(*ScriptLoaderCallbackType)();
@@ -873,10 +862,25 @@ class TC_GAME_API ScriptMgr
}
public: /* Script contexts */
- void BeginScriptContext(std::string const& context);
- void FinishScriptContext();
-
+ /// Set the current script context, which allows the ScriptMgr
+ /// to accept new scripts in this context.
+ /// Requires a SwapScriptContext() call afterwards to load the new scripts.
+ void SetScriptContext(std::string const& context);
+ /// Returns the current script context.
+ std::string const& GetCurrentScriptContext() const { return _currentContext; }
+ /// Releases all scripts associated with the given script context immediately.
+ /// Requires a SwapScriptContext() call afterwards to finish the unloading.
void ReleaseScriptContext(std::string const& context);
+ /// Executes all changed introduced by SetScriptContext and ReleaseScriptContext.
+ /// It is possible to combine multiple SetScriptContext and ReleaseScriptContext
+ /// calls for better performance (bulk changes).
+ void SwapScriptContext(bool initialize = false);
+
+ /// Acquires a strong module reference to the module containing the given script name,
+ /// which prevents the shared library which contains the script from unloading.
+ /// The shared library is lazy unloaded as soon as all references to it are released.
+ std::shared_ptr<ModuleReference> AcquireModuleReferenceOfScriptName(
+ std::string const& scriptname) const;
public: /* Unloading */
@@ -886,7 +890,7 @@ class TC_GAME_API ScriptMgr
void CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector);
void CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector);
- void CreateSpellScriptLoaders(uint32 spellId, std::vector<std::pair<SpellScriptLoader*, std::multimap<uint32, uint32>::iterator> >& scriptVector);
+ SpellScriptLoader* GetSpellScriptLoader(uint32 scriptId);
public: /* ServerScript */
diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp
index 6876f8fa7ef..e2598386466 100644
--- a/src/server/game/Spells/SpellScript.cpp
+++ b/src/server/game/Spells/SpellScript.cpp
@@ -16,6 +16,7 @@
*/
#include "Spell.h"
+#include "ScriptMgr.h"
#include "SpellAuras.h"
#include "SpellScript.h"
#include "SpellMgr.h"
@@ -50,6 +51,12 @@ void _SpellScript::_Init(std::string const* scriptname, uint32 spellId)
m_currentScriptState = SPELL_SCRIPT_STATE_NONE;
m_scriptName = scriptname;
m_scriptSpellId = spellId;
+
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Acquire a strong reference to the binary code
+ // to keep it loaded until all spells are destroyed.
+ m_moduleReference = sScriptMgr->AcquireModuleReferenceOfScriptName(*scriptname);
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
}
std::string const* _SpellScript::_GetScriptName() const
diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h
index 1d4efd8073d..539bc54cc94 100644
--- a/src/server/game/Spells/SpellScript.h
+++ b/src/server/game/Spells/SpellScript.h
@@ -22,6 +22,7 @@
#include "SharedDefines.h"
#include "SpellAuraDefines.h"
#include "Spell.h"
+#include "ScriptReloadMgr.h"
#include <stack>
class Unit;
@@ -105,6 +106,16 @@ class TC_GAME_API _SpellScript
uint8 m_currentScriptState;
std::string const* m_scriptName;
uint32 m_scriptSpellId;
+
+ private:
+
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+
+ // Strong reference to keep the binary code loaded
+ std::shared_ptr<ModuleReference> m_moduleReference;
+
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
+
public:
//
// SpellScript/AuraScript interface base
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index cd6d016ffa3..4b64ef0bbd8 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1800,7 +1800,6 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Initializing Scripts...");
sScriptMgr->Initialize();
sScriptMgr->OnConfigLoad(false); // must be done after the ScriptMgr has been properly initialized
- sScriptReloadMgr->Initialize();
TC_LOG_INFO("server.loading", "Validating spell scripts...");
sObjectMgr->ValidateSpellScripts();
diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp
index cd91ac1a5ce..eb28a8adae4 100644
--- a/src/server/scripts/Commands/cs_reload.cpp
+++ b/src/server/scripts/Commands/cs_reload.cpp
@@ -393,7 +393,7 @@ public:
static bool HandleReloadCommandCommand(ChatHandler* handler, const char* /*args*/)
{
- handler->SetLoadCommandTable(true);
+ ChatHandler::invalidateCommandTable();
handler->SendGlobalGMSysMessage("DB table `command` will be reloaded at next chat command use.");
return true;
}