mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-13 01:08:35 +00:00
[CORE] Rewritten ScriptMgr to be initialized before server load
Now ScriptMgr can be initialized before config allowing to create scripts that can change the behaviour of server before loading anything
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -44,6 +44,7 @@ CMakeLists.txt.user
|
||||
# exclude in all levels
|
||||
nbproject/
|
||||
.sync.ffs_db
|
||||
*.kate-swp
|
||||
|
||||
#
|
||||
# Eclipse
|
||||
|
||||
@@ -163,6 +163,7 @@ class ScriptRegistry
|
||||
ScriptMgr::ScriptMgr()
|
||||
: _scriptCount(0), _scheduledScripts(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ScriptMgr::~ScriptMgr()
|
||||
@@ -171,18 +172,8 @@ ScriptMgr::~ScriptMgr()
|
||||
|
||||
void ScriptMgr::Initialize()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
LoadDatabase();
|
||||
|
||||
sLog->outString("Loading C++ scripts");
|
||||
|
||||
FillSpellSummary();
|
||||
AddScripts();
|
||||
CheckIfScriptsInDatabaseExist();
|
||||
|
||||
sLog->outString(">> Loaded %u C++ scripts in %u ms", GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
|
||||
sLog->outString();
|
||||
sLog->outString("Loading C++ scripts");
|
||||
}
|
||||
|
||||
void ScriptMgr::Unload()
|
||||
@@ -223,7 +214,32 @@ void ScriptMgr::Unload()
|
||||
|
||||
void ScriptMgr::LoadDatabase()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
sScriptSystemMgr->LoadScriptWaypoints();
|
||||
|
||||
// Add all scripts that must be loaded after db/maps
|
||||
ScriptRegistry<WorldMapScript>::AddALScripts();
|
||||
ScriptRegistry<BattlegroundMapScript>::AddALScripts();
|
||||
ScriptRegistry<InstanceMapScript>::AddALScripts();
|
||||
ScriptRegistry<SpellScriptLoader>::AddALScripts();
|
||||
ScriptRegistry<ItemScript>::AddALScripts();
|
||||
ScriptRegistry<CreatureScript>::AddALScripts();
|
||||
ScriptRegistry<GameObjectScript>::AddALScripts();
|
||||
ScriptRegistry<AreaTriggerScript>::AddALScripts();
|
||||
ScriptRegistry<BattlegroundScript>::AddALScripts();
|
||||
ScriptRegistry<OutdoorPvPScript>::AddALScripts();
|
||||
ScriptRegistry<WeatherScript>::AddALScripts();
|
||||
ScriptRegistry<ConditionScript>::AddALScripts();
|
||||
ScriptRegistry<TransportScript>::AddALScripts();
|
||||
ScriptRegistry<AchievementCriteriaScript>::AddALScripts();
|
||||
|
||||
FillSpellSummary();
|
||||
|
||||
CheckIfScriptsInDatabaseExist();
|
||||
|
||||
sLog->outString(">> Loaded %u C++ scripts in %u ms", GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
|
||||
sLog->outString();
|
||||
}
|
||||
|
||||
struct TSpellSummary
|
||||
@@ -437,9 +453,14 @@ void ScriptMgr::OnOpenStateChange(bool open)
|
||||
FOREACH_SCRIPT(WorldScript)->OnOpenStateChange(open);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnConfigLoad(bool reload)
|
||||
void ScriptMgr::OnBeforeConfigLoad(bool reload)
|
||||
{
|
||||
FOREACH_SCRIPT(WorldScript)->OnConfigLoad(reload);
|
||||
FOREACH_SCRIPT(WorldScript)->OnBeforeConfigLoad(reload);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnAfterConfigLoad(bool reload)
|
||||
{
|
||||
FOREACH_SCRIPT(WorldScript)->OnAfterConfigLoad(reload);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnMotdChange(std::string& newMotd)
|
||||
@@ -1326,27 +1347,18 @@ FormulaScript::FormulaScript(const char* name)
|
||||
WorldMapScript::WorldMapScript(const char* name, uint32 mapId)
|
||||
: ScriptObject(name), MapScript<Map>(mapId)
|
||||
{
|
||||
if (GetEntry() && !GetEntry()->IsWorldMap())
|
||||
sLog->outError("WorldMapScript for map %u is invalid.", mapId);
|
||||
|
||||
ScriptRegistry<WorldMapScript>::AddScript(this);
|
||||
}
|
||||
|
||||
InstanceMapScript::InstanceMapScript(const char* name, uint32 mapId)
|
||||
: ScriptObject(name), MapScript<InstanceMap>(mapId)
|
||||
{
|
||||
if (GetEntry() && !GetEntry()->IsDungeon())
|
||||
sLog->outError("InstanceMapScript for map %u is invalid.", mapId);
|
||||
|
||||
ScriptRegistry<InstanceMapScript>::AddScript(this);
|
||||
}
|
||||
|
||||
BattlegroundMapScript::BattlegroundMapScript(const char* name, uint32 mapId)
|
||||
: ScriptObject(name), MapScript<BattlegroundMap>(mapId)
|
||||
{
|
||||
if (GetEntry() && !GetEntry()->IsBattleground())
|
||||
sLog->outError("BattlegroundMapScript for map %u is invalid.", mapId);
|
||||
|
||||
ScriptRegistry<BattlegroundMapScript>::AddScript(this);
|
||||
}
|
||||
|
||||
@@ -1454,6 +1466,7 @@ GroupScript::GroupScript(const char* name)
|
||||
|
||||
// Instantiate static members of ScriptRegistry.
|
||||
template<class TScript> std::map<uint32, TScript*> ScriptRegistry<TScript>::ScriptPointerList;
|
||||
template<class TScript> std::vector<TScript*> ScriptRegistry<TScript>::ALScripts;
|
||||
template<class TScript> uint32 ScriptRegistry<TScript>::_scriptIdCounter = 0;
|
||||
|
||||
// Specialize for each script type class like so:
|
||||
|
||||
@@ -159,6 +159,8 @@ class ScriptObject
|
||||
// Do not override this in scripts; it should be overridden by the various script type classes. It indicates
|
||||
// whether or not this script type must be assigned in the database.
|
||||
virtual bool IsDatabaseBound() const { return false; }
|
||||
virtual bool isAfterLoadScript() const { return IsDatabaseBound(); }
|
||||
virtual void checkValidity() { }
|
||||
|
||||
const std::string& GetName() const { return _name; }
|
||||
|
||||
@@ -242,7 +244,10 @@ class WorldScript : public ScriptObject
|
||||
virtual void OnOpenStateChange(bool /*open*/) { }
|
||||
|
||||
// Called after the world configuration is (re)loaded.
|
||||
virtual void OnConfigLoad(bool /*reload*/) { }
|
||||
virtual void OnAfterConfigLoad(bool /*reload*/) { }
|
||||
|
||||
// Called before the world configuration is (re)loaded.
|
||||
virtual void OnBeforeConfigLoad(bool /*reload*/) { }
|
||||
|
||||
// Called before the message of the day is changed.
|
||||
virtual void OnMotdChange(std::string& /*newMotd*/) { }
|
||||
@@ -296,17 +301,22 @@ class FormulaScript : public ScriptObject
|
||||
template<class TMap> class MapScript : public UpdatableScript<TMap>
|
||||
{
|
||||
MapEntry const* _mapEntry;
|
||||
uint32 _mapId;
|
||||
|
||||
protected:
|
||||
|
||||
MapScript(uint32 mapId)
|
||||
: _mapEntry(sMapStore.LookupEntry(mapId))
|
||||
: _mapId(mapId)
|
||||
{
|
||||
if (!_mapEntry)
|
||||
sLog->outError("Invalid MapScript for %u; no such map ID.", mapId);
|
||||
}
|
||||
|
||||
public:
|
||||
void checkMap() {
|
||||
_mapEntry = sMapStore.LookupEntry(_mapId);
|
||||
|
||||
if (!_mapEntry)
|
||||
sLog->outError("Invalid MapScript for %u; no such map ID.", _mapId);
|
||||
}
|
||||
|
||||
// Gets the MapEntry structure associated with this script. Can return NULL.
|
||||
MapEntry const* GetEntry() { return _mapEntry; }
|
||||
@@ -338,6 +348,17 @@ class WorldMapScript : public ScriptObject, public MapScript<Map>
|
||||
protected:
|
||||
|
||||
WorldMapScript(const char* name, uint32 mapId);
|
||||
|
||||
public:
|
||||
|
||||
bool isAfterLoadScript() const { return true; }
|
||||
|
||||
void checkValidity() {
|
||||
checkMap();
|
||||
|
||||
if (GetEntry() && !GetEntry()->IsWorldMap())
|
||||
sLog->outError("WorldMapScript for map %u is invalid.", GetEntry()->MapID);
|
||||
}
|
||||
};
|
||||
|
||||
class InstanceMapScript : public ScriptObject, public MapScript<InstanceMap>
|
||||
@@ -350,6 +371,13 @@ class InstanceMapScript : public ScriptObject, public MapScript<InstanceMap>
|
||||
|
||||
bool IsDatabaseBound() const { return true; }
|
||||
|
||||
void checkValidity() {
|
||||
checkMap();
|
||||
|
||||
if (GetEntry() && !GetEntry()->IsDungeon())
|
||||
sLog->outError("InstanceMapScript for map %u is invalid.", GetEntry()->MapID);
|
||||
}
|
||||
|
||||
// Gets an InstanceScript object for this instance.
|
||||
virtual InstanceScript* GetInstanceScript(InstanceMap* /*map*/) const { return NULL; }
|
||||
};
|
||||
@@ -359,6 +387,17 @@ class BattlegroundMapScript : public ScriptObject, public MapScript<Battleground
|
||||
protected:
|
||||
|
||||
BattlegroundMapScript(const char* name, uint32 mapId);
|
||||
|
||||
public:
|
||||
|
||||
bool isAfterLoadScript() const { return true; }
|
||||
|
||||
void checkValidity() {
|
||||
checkMap();
|
||||
|
||||
if (GetEntry() && !GetEntry()->IsBattleground())
|
||||
sLog->outError("BattlegroundMapScript for map %u is invalid.", GetEntry()->MapID);
|
||||
}
|
||||
};
|
||||
|
||||
class ItemScript : public ScriptObject
|
||||
@@ -826,7 +865,8 @@ class ScriptMgr
|
||||
public: /* WorldScript */
|
||||
|
||||
void OnOpenStateChange(bool open);
|
||||
void OnConfigLoad(bool reload);
|
||||
void OnBeforeConfigLoad(bool reload);
|
||||
void OnAfterConfigLoad(bool reload);
|
||||
void OnMotdChange(std::string& newMotd);
|
||||
void OnShutdownInitiate(ShutdownExitCode code, ShutdownMask mask);
|
||||
void OnShutdownCancel();
|
||||
@@ -1024,78 +1064,95 @@ class ScriptRegistry
|
||||
typedef std::map<uint32, TScript*> ScriptMap;
|
||||
typedef typename ScriptMap::iterator ScriptMapIterator;
|
||||
|
||||
typedef std::vector<TScript*> ScriptVector;
|
||||
typedef typename ScriptVector::iterator ScriptVectorIterator;
|
||||
|
||||
// The actual list of scripts. This will be accessed concurrently, so it must not be modified
|
||||
// after server startup.
|
||||
static ScriptMap ScriptPointerList;
|
||||
// After database load scripts
|
||||
static ScriptVector ALScripts;
|
||||
|
||||
static void AddScript(TScript* const script)
|
||||
{
|
||||
ASSERT(script);
|
||||
|
||||
// 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)
|
||||
if (!_checkMemory(script))
|
||||
return;
|
||||
|
||||
if (script->isAfterLoadScript())
|
||||
{
|
||||
if (it->second == script)
|
||||
{
|
||||
sLog->outError("Script '%s' has same memory pointer as '%s'.",
|
||||
script->GetName().c_str(), it->second->GetName().c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (script->IsDatabaseBound())
|
||||
{
|
||||
// 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().c_str());
|
||||
if (id)
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the script is already assigned -> delete it!
|
||||
sLog->outError("Script '%s' already assigned with the same script name, so the script can't work.",
|
||||
script->GetName().c_str());
|
||||
|
||||
ASSERT(false); // Error that should be fixed ASAP.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The script uses a script name from database, but isn't assigned to anything.
|
||||
if (script->GetName().find("Smart") == std::string::npos)
|
||||
sLog->outErrorDb("Script named '%s' does not have a script name assigned in database.",
|
||||
script->GetName().c_str());
|
||||
}
|
||||
ALScripts.push_back(script);
|
||||
}
|
||||
else
|
||||
{
|
||||
script->checkValidity();
|
||||
|
||||
// We're dealing with a code-only script; just add it.
|
||||
ScriptPointerList[_scriptIdCounter++] = script;
|
||||
sScriptMgr->IncrementScriptCount();
|
||||
}
|
||||
}
|
||||
|
||||
static void AddALScripts() {
|
||||
for(ScriptVectorIterator it = ALScripts.begin(); it != ALScripts.end(); ++it) {
|
||||
TScript* const script = *it;
|
||||
|
||||
script->checkValidity();
|
||||
|
||||
if (script->IsDatabaseBound()) {
|
||||
|
||||
if (!_checkMemory(script))
|
||||
return;
|
||||
|
||||
// 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().c_str());
|
||||
if (id)
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the script is already assigned -> delete it!
|
||||
sLog->outError("Script '%s' already assigned with the same script name, so the script can't work.",
|
||||
script->GetName().c_str());
|
||||
|
||||
ASSERT(false); // Error that should be fixed ASAP.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The script uses a script name from database, but isn't assigned to anything.
|
||||
if (script->GetName().find("Smart") == std::string::npos)
|
||||
sLog->outErrorDb("Script named '%s' does not have a script name assigned in database.",
|
||||
script->GetName().c_str());
|
||||
}
|
||||
} else {
|
||||
// We're dealing with a code-only script; just add it.
|
||||
ScriptPointerList[_scriptIdCounter++] = script;
|
||||
sScriptMgr->IncrementScriptCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gets a script by its ID (assigned by ObjectMgr).
|
||||
static TScript* GetScriptById(uint32 id)
|
||||
{
|
||||
@@ -1107,6 +1164,24 @@ class ScriptRegistry
|
||||
}
|
||||
|
||||
private:
|
||||
// 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.
|
||||
static bool _checkMemory(TScript* const script) {
|
||||
// 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)
|
||||
{
|
||||
if (it->second == script)
|
||||
{
|
||||
sLog->outError("Script '%s' has same memory pointer as '%s'.",
|
||||
script->GetName().c_str(), it->second->GetName().c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Counter used for code-only scripts.
|
||||
static uint32 _scriptIdCounter;
|
||||
|
||||
@@ -436,6 +436,8 @@ void World::LoadConfigSettings(bool reload)
|
||||
sLog->ReloadConfig(); // Reload log levels and filters
|
||||
}
|
||||
|
||||
sScriptMgr->OnBeforeConfigLoad(reload);
|
||||
|
||||
///- Read the player limit and the Message of the day from the config file
|
||||
if (!reload)
|
||||
SetPlayerAmountLimit(sConfigMgr->GetIntDefault("PlayerLimit", 100));
|
||||
@@ -1236,8 +1238,7 @@ void World::LoadConfigSettings(bool reload)
|
||||
m_int_configs[CONFIG_BIRTHDAY_TIME] = sConfigMgr->GetIntDefault("BirthdayTime", 1222964635);
|
||||
|
||||
// call ScriptMgr if we're reloading the configuration
|
||||
if (reload)
|
||||
sScriptMgr->OnConfigLoad(reload);
|
||||
sScriptMgr->OnAfterConfigLoad(reload);
|
||||
}
|
||||
|
||||
extern void LoadGameObjectModelList();
|
||||
@@ -1253,6 +1254,9 @@ void World::SetInitialWorldSettings()
|
||||
|
||||
///- Initialize detour memory management
|
||||
dtAllocSetCustom(dtCustomAlloc, dtCustomFree);
|
||||
|
||||
sLog->outString("Initializing Scripts...");
|
||||
sScriptMgr->Initialize();
|
||||
|
||||
///- Initialize config settings
|
||||
LoadConfigSettings();
|
||||
@@ -1700,9 +1704,8 @@ void World::SetInitialWorldSettings()
|
||||
sLog->outString("Loading Creature Text Locales...");
|
||||
sCreatureTextMgr->LoadCreatureTextLocales();
|
||||
|
||||
sLog->outString("Initializing Scripts...");
|
||||
sScriptMgr->Initialize();
|
||||
sScriptMgr->OnConfigLoad(false); // must be done after the ScriptMgr has been properly initialized
|
||||
sLog->outString("Loading Scripts...");
|
||||
sScriptMgr->LoadDatabase();
|
||||
|
||||
sLog->outString("Validating spell scripts...");
|
||||
sObjectMgr->ValidateSpellScripts();
|
||||
|
||||
Reference in New Issue
Block a user