Core/Spawning: Actually check spawn group state before processing a respawn. It feels like that is something that should've been noticed at some point.

Port From (https://github.com/TrinityCore/TrinityCore/commit/5c80f5073493a3d31fd4b463891727beb6fe7eb1)
This commit is contained in:
hondacrx
2021-08-09 09:37:36 -04:00
parent bfd44a494d
commit 85ffed189f
6 changed files with 583 additions and 631 deletions
+13 -11
View File
@@ -443,15 +443,20 @@ namespace Game.Entities
{ {
case DeathState.JustRespawned: case DeathState.JustRespawned:
case DeathState.JustDied: case DeathState.JustDied:
Log.outError(LogFilter.Unit, "Creature ({0}) in wrong state: {1}", GetGUID().ToString(), m_deathState); Log.outError(LogFilter.Unit, $"Creature ({GetGUID()}) in wrong state: {m_deathState}");
break; break;
case DeathState.Dead: case DeathState.Dead:
{ {
if (!m_respawnCompatibilityMode)
{
Log.outError(LogFilter.Unit, $"Creature (GUID: {GetGUID().GetCounter()} Entry: {GetEntry()}) in wrong state: DEAD (3)");
break;
}
long now = GameTime.GetGameTime(); long now = GameTime.GetGameTime();
if (m_respawnTime <= now) if (m_respawnTime <= now)
{ {
// First check if there are any scripts that object to us respawning // Delay respawn if spawn group is not active
if (!Global.ScriptMgr.CanSpawn(GetSpawnId(), GetEntry(), GetCreatureData(), GetMap())) if (m_creatureData != null && !GetMap().IsSpawnGroupActive(m_creatureData.spawnGroupData.groupId))
{ {
m_respawnTime = now + RandomHelper.URand(4, 7); m_respawnTime = now + RandomHelper.URand(4, 7);
break; // Will be rechecked on next Update call after delay expires break; // Will be rechecked on next Update call after delay expires
@@ -3119,7 +3124,7 @@ namespace Game.Entities
CreatureData data = Global.ObjectMgr.GetCreatureData(spawnId); CreatureData data = Global.ObjectMgr.GetCreatureData(spawnId);
if (data == null) if (data == null)
{ {
Log.outError(LogFilter.Sql, "Creature (GUID: {0}) not found in table `creature`, can't load. ", spawnId); Log.outError(LogFilter.Sql, $"Creature (SpawnID: {spawnId}) not found in table `creature`, can't load.");
return false; return false;
} }
@@ -3128,12 +3133,6 @@ namespace Game.Entities
m_creatureData = data; m_creatureData = data;
m_respawnradius = data.spawndist; m_respawnradius = data.spawndist;
m_respawnDelay = (uint)data.spawntimesecs; m_respawnDelay = (uint)data.spawntimesecs;
// Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
if (!m_respawnCompatibilityMode && m_respawnTime == 0 && !Global.ScriptMgr.CanSpawn(spawnId, data.Id, data, map))
{
SaveRespawnTime(RandomHelper.URand(4, 7));
return false;
}
if (!Create(map.GenerateLowGuid(HighGuid.Creature), map, data.Id, data.spawnPoint, data, 0, !m_respawnCompatibilityMode)) if (!Create(map.GenerateLowGuid(HighGuid.Creature), map, data.Id, data.spawnPoint, data, 0, !m_respawnCompatibilityMode))
return false; return false;
@@ -3146,8 +3145,11 @@ namespace Game.Entities
m_respawnTime = GetMap().GetCreatureRespawnTime(m_spawnId); m_respawnTime = GetMap().GetCreatureRespawnTime(m_spawnId);
// Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update) // Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update)
if (m_respawnCompatibilityMode && m_respawnTime == 0 && !Global.ScriptMgr.CanSpawn(spawnId, GetEntry(), GetCreatureData(), map)) if (m_respawnTime == 0 && !map.IsSpawnGroupActive(data.spawnGroupData.groupId))
{
Cypher.Assert(m_respawnCompatibilityMode, $"Creature (SpawnID {spawnId}) trying to load in inactive spawn group {data.spawnGroupData.name}.");
m_respawnTime = GameTime.GetGameTime() + RandomHelper.URand(4, 7); m_respawnTime = GameTime.GetGameTime() + RandomHelper.URand(4, 7);
}
if (m_respawnTime != 0) // respawn on UpdateLoadCreatureFromDB if (m_respawnTime != 0) // respawn on UpdateLoadCreatureFromDB
{ {
+1 -1
View File
@@ -249,7 +249,7 @@ namespace Game.Maps
if (doSpawn) if (doSpawn)
instance.SpawnGroupSpawn(groupId, instance); instance.SpawnGroupSpawn(groupId, instance);
else // otherwise, set it as inactive so it no longer respawns (but don't despawn it) else // otherwise, set it as inactive so it no longer respawns (but don't despawn it)
instance.SetSpawnGroupActive(groupId, false); instance.SetSpawnGroupInactive(groupId);
} }
} }
+18 -19
View File
@@ -2329,8 +2329,18 @@ namespace Game.Maps
bool CheckRespawn(RespawnInfo info) bool CheckRespawn(RespawnInfo info)
{ {
SpawnData data = Global.ObjectMgr.GetSpawnData(info.type, info.spawnId);
Cypher.Assert(data != null, $"Invalid respawn info with type {info.type}, spawnID {info.spawnId} in respawn queue.");
// First, check if this creature's spawn group is inactive
if (!IsSpawnGroupActive(data.spawnGroupData.groupId))
{
info.respawnTime = 0;
return false;
}
uint poolId = info.spawnId != 0 ? Global.PoolMgr.IsPartOfAPool(info.type, info.spawnId) : 0; uint poolId = info.spawnId != 0 ? Global.PoolMgr.IsPartOfAPool(info.type, info.spawnId) : 0;
// First, check if there's already an instance of this object that would block the respawn // Next, check if there's already an instance of this object that would block the respawn
// Only do this for unpooled spawns // Only do this for unpooled spawns
if (poolId == 0) if (poolId == 0)
{ {
@@ -2340,14 +2350,7 @@ namespace Game.Maps
case SpawnObjectType.Creature: case SpawnObjectType.Creature:
{ {
// escort check for creatures only (if the world config boolean is set) // escort check for creatures only (if the world config boolean is set)
bool isEscort = false; bool isEscort = WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && data.spawnGroupData.flags.HasFlag(SpawnGroupFlags.EscortQuestNpc);
if (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && info.type == SpawnObjectType.Creature)
{
CreatureData cdata = Global.ObjectMgr.GetCreatureData(info.spawnId);
if (cdata != null)
if (cdata.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.EscortQuestNpc))
isEscort = true;
}
var range = _creatureBySpawnIdStore.LookupByKey(info.spawnId); var range = _creatureBySpawnIdStore.LookupByKey(info.spawnId);
foreach (var creature in range) foreach (var creature in range)
@@ -2411,15 +2414,7 @@ namespace Game.Maps
return false; return false;
} }
// if we're a creature, see if the script objects to us spawning // everything ok, let's spawn
if (info.type == SpawnObjectType.Creature)
{
if (!Global.ScriptMgr.CanSpawn(info.spawnId, info.entry, Global.ObjectMgr.GetCreatureData(info.spawnId), this))
{ // if a script blocks our respawn, schedule next check in a little bit
info.respawnTime = GameTime.GetGameTime() + RandomHelper.URand(4, 7);
return false;
}
}
return true; return true;
} }
@@ -2822,6 +2817,10 @@ namespace Game.Maps
_toggledSpawnGroupIds.Remove(groupId); _toggledSpawnGroupIds.Remove(groupId);
} }
// Disable the spawn group, which prevents any creatures in the group from respawning until re-enabled
// This will not affect any already-present creatures in the group
public void SetSpawnGroupInactive(uint groupId) { SetSpawnGroupActive(groupId, false); }
public bool IsSpawnGroupActive(uint groupId) public bool IsSpawnGroupActive(uint groupId)
{ {
SpawnGroupTemplateData data = GetSpawnGroupData(groupId); SpawnGroupTemplateData data = GetSpawnGroupData(groupId);
@@ -5050,7 +5049,7 @@ namespace Game.Maps
// @todo Not sure about checking player level: already done in HandleAreaTriggerOpcode // @todo Not sure about checking player level: already done in HandleAreaTriggerOpcode
// GMs still can teleport player in instance. // GMs still can teleport player in instance.
// Is it needed? // Is it needed?
lock(_mapLock) lock (_mapLock)
{ {
// Dungeon only code // Dungeon only code
if (IsDungeon()) if (IsDungeon())
-8
View File
@@ -110,14 +110,6 @@ namespace Game.Maps
continue; continue;
} }
} }
// If script is blocking spawn, don't spawn but queue for a re-check in a little bit
if (!group.flags.HasFlag(SpawnGroupFlags.CompatibilityMode) && !Global.ScriptMgr.CanSpawn(guid, cdata.Id, cdata, map))
{
map.SaveRespawnTime(SpawnObjectType.Creature, guid, cdata.Id, GameTime.GetGameTime() + RandomHelper.URand(4, 7), map.GetZoneId(PhasingHandler.EmptyPhaseShift, cdata.spawnPoint), GridDefines.ComputeGridCoord(cdata.spawnPoint.GetPositionX(), cdata.spawnPoint.GetPositionY()).GetId(), false);
obj.Dispose();
continue;
}
} }
else if (obj.IsTypeId(TypeId.GameObject)) else if (obj.IsTypeId(TypeId.GameObject))
{ {
-3
View File
@@ -333,9 +333,6 @@ namespace Game.Scripting
public override bool IsDatabaseBound() { return true; } public override bool IsDatabaseBound() { return true; }
// Called when the creature tries to spawn. Return false to block spawn and re-evaluate on next tick.
public virtual bool CanSpawn(ulong spawnId, uint entry, CreatureTemplate baseTemplate, CreatureTemplate actTemplate, CreatureData cData, Map map) { return true; }
// Called when a CreatureAI object is needed for the creature. // Called when a CreatureAI object is needed for the creature.
public virtual CreatureAI GetAI(Creature creature) { return null; } public virtual CreatureAI GetAI(Creature creature) { return null; }
} }
-38
View File
@@ -681,44 +681,6 @@ namespace Game.Scripting
return RunScriptRet<ItemScript>(tmpscript => tmpscript.OnCastItemCombatSpell(player, victim, spellInfo, item), item.GetScriptId()); return RunScriptRet<ItemScript>(tmpscript => tmpscript.OnCastItemCombatSpell(player, victim, spellInfo, item), item.GetScriptId());
} }
//CreatureScript
public bool CanSpawn(ulong spawnId, uint entry, CreatureData cData, Map map)
{
Cypher.Assert(map != null);
CreatureTemplate baseTemplate = Global.ObjectMgr.GetCreatureTemplate(entry);
Cypher.Assert(baseTemplate != null);
// find out which template we'd be using
CreatureTemplate actTemplate = null;
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(map.GetDifficultyID());
while (actTemplate == null && difficultyEntry != null)
{
int idx = CreatureTemplate.DifficultyIDToDifficultyEntryIndex(difficultyEntry.Id);
if (idx == -1)
break;
if (baseTemplate.DifficultyEntry[idx] != 0)
{
actTemplate = Global.ObjectMgr.GetCreatureTemplate(baseTemplate.DifficultyEntry[idx]);
break;
}
if (difficultyEntry.FallbackDifficultyID == 0)
break;
difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficultyEntry.FallbackDifficultyID);
}
if (actTemplate == null)
actTemplate = baseTemplate;
uint scriptId = baseTemplate.ScriptID;
if (cData != null && cData.ScriptId != 0)
scriptId = cData.ScriptId;
return RunScriptRet<CreatureScript, bool>(p => p.CanSpawn(spawnId, entry, baseTemplate, actTemplate, cData, map), scriptId, true);
}
public CreatureAI GetCreatureAI(Creature creature) public CreatureAI GetCreatureAI(Creature creature)
{ {
Cypher.Assert(creature != null); Cypher.Assert(creature != null);