From 4038b93aa10aac3f9d0e54c03895660d214153ea Mon Sep 17 00:00:00 2001 From: hondacrx Date: Wed, 15 Jun 2022 22:56:12 -0400 Subject: [PATCH] Core/Pools: Implemented pooling for instances Port From (https://github.com/TrinityCore/TrinityCore/commit/94d829c84fb184990e26178551d10ecdca049efd) --- Source/Game/Entities/Creature/Creature.cs | 8 +- Source/Game/Entities/GameObject/GameObject.cs | 8 +- Source/Game/Events/GameEventManager.cs | 34 +- Source/Game/Globals/ObjectManager.cs | 13 +- Source/Game/Maps/Map.cs | 11 +- Source/Game/Maps/SpawnData.cs | 1 + Source/Game/Pools/PoolManager.cs | 408 ++++++++++-------- 7 files changed, 286 insertions(+), 197 deletions(-) diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index f69e88da5..ca09cdfb8 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -1968,9 +1968,9 @@ namespace Game.Entities triggerJustAppeared = true; - uint poolid = GetSpawnId() != 0 ? Global.PoolMgr.IsPartOfAPool(GetSpawnId()) : 0; + uint poolid = GetCreatureData() != null ? GetCreatureData().poolId : 0; if (poolid != 0) - Global.PoolMgr.UpdatePool(poolid, GetSpawnId()); + Global.PoolMgr.UpdatePool(GetMap().GetPoolData(), poolid, GetSpawnId()); } UpdateObjectVisibility(); } @@ -3238,7 +3238,7 @@ namespace Game.Entities if (!m_respawnCompatibilityMode) { // @todo pools need fixing! this is just a temporary thing, but they violate dynspawn principles - if (Global.PoolMgr.IsPartOfAPool(spawnId) == 0) + if (data.poolId == 0) { Log.outError(LogFilter.Unit, $"Creature (SpawnID {spawnId}) trying to load in inactive spawn group '{data.spawnGroupData.name}':\n{GetDebugInfo()}"); return false; @@ -3253,7 +3253,7 @@ namespace Game.Entities if (!m_respawnCompatibilityMode) { // @todo same as above - if (Global.PoolMgr.IsPartOfAPool(spawnId) == 0) + if (data.poolId == 0) { Log.outError(LogFilter.Unit, $"Creature (SpawnID {spawnId}) trying to load despite a respawn timer in progress:\n{GetDebugInfo()}"); return false; diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index b1f2dfb71..bea138f1e 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -558,9 +558,9 @@ namespace Game.Entities GetAI().Reset(); // respawn timer - uint poolid = GetSpawnId() != 0 ? Global.PoolMgr.IsPartOfAPool(GetSpawnId()) : 0; + uint poolid = GetGameObjectData() != null ? GetGameObjectData().poolId : 0; if (poolid != 0) - Global.PoolMgr.UpdatePool(poolid, GetSpawnId()); + Global.PoolMgr.UpdatePool(GetMap().GetPoolData(), poolid, GetSpawnId()); else GetMap().AddToMap(this); } @@ -941,9 +941,9 @@ namespace Game.Entities if (goOverride != null) ReplaceAllFlags(goOverride.Flags); - uint poolid = GetSpawnId() != 0 ? Global.PoolMgr.IsPartOfAPool(GetSpawnId()) : 0; + uint poolid = GetGameObjectData() != null ? GetGameObjectData().poolId : 0; if (poolid != 0) - Global.PoolMgr.UpdatePool(poolid, GetSpawnId()); + Global.PoolMgr.UpdatePool(GetMap().GetPoolData(), poolid, GetSpawnId()); else AddObjectToRemoveList(); } diff --git a/Source/Game/Events/GameEventManager.cs b/Source/Game/Events/GameEventManager.cs index 52ca20ff0..a7d771173 100644 --- a/Source/Game/Events/GameEventManager.cs +++ b/Source/Game/Events/GameEventManager.cs @@ -395,9 +395,8 @@ namespace Game } // Log error for pooled object, but still spawn it - uint poolId = Global.PoolMgr.IsPartOfAPool(SpawnObjectType.Creature, guid); - if (poolId != 0) - Log.outError(LogFilter.Sql, $"`game_event_creature`: game event id ({event_id}) contains creature ({guid}) which is part of a pool ({poolId}). This should be spawned in game_event_pool"); + if (data.poolId != 0) + Log.outError(LogFilter.Sql, $"`game_event_creature`: game event id ({event_id}) contains creature ({guid}) which is part of a pool ({data.poolId}). This should be spawned in game_event_pool"); mGameEventCreatureGuids[internal_event_id].Add(guid); @@ -440,9 +439,8 @@ namespace Game } // Log error for pooled object, but still spawn it - uint poolId = Global.PoolMgr.IsPartOfAPool(SpawnObjectType.GameObject, guid); - if (poolId != 0) - Log.outError(LogFilter.Sql, $"`game_event_gameobject`: game event id ({event_id}) contains game object ({guid}) which is part of a pool ({poolId}). This should be spawned in game_event_pool"); + if (data.poolId != 0) + Log.outError(LogFilter.Sql, $"`game_event_gameobject`: game event id ({event_id}) contains game object ({guid}) which is part of a pool ({data.poolId}). This should be spawned in game_event_pool"); mGameEventGameobjectGuids[internal_event_id].Add(guid); @@ -1246,7 +1244,16 @@ namespace Game } foreach (var id in mGameEventPoolIds[internal_event_id]) - Global.PoolMgr.SpawnPool(id); + { + PoolTemplateData poolTemplate = Global.PoolMgr.GetPoolTemplate(id); + if (poolTemplate != null) + { + Global.MapMgr.DoForAllMapsWithMapId((uint)poolTemplate.MapId, map => + { + Global.PoolMgr.SpawnPool(map.GetPoolData(), id); + }); + } + } } void GameEventUnspawn(short event_id) @@ -1306,7 +1313,7 @@ namespace Game var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid); foreach (var go in gameobjectBounds) go.AddObjectToRemoveList(); - + }); } } @@ -1318,7 +1325,16 @@ namespace Game } foreach (var poolId in mGameEventPoolIds[internal_event_id]) - Global.PoolMgr.DespawnPool(poolId, true); + { + PoolTemplateData poolTemplate = Global.PoolMgr.GetPoolTemplate(poolId); + if (poolTemplate != null) + { + Global.MapMgr.DoForAllMapsWithMapId((uint)poolTemplate.MapId, map => + { + Global.PoolMgr.DespawnPool(map.GetPoolData(), poolId, true); + }); + } + } } void ChangeEquipOrModel(short event_id, bool activate) diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index dece5f183..86cfbc9de 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -3426,7 +3426,7 @@ namespace Game data.movementType = result.Read(14); data.SpawnDifficulties = ParseSpawnDifficulties(result.Read(15), "creature", guid, data.MapId, spawnMasks.LookupByKey(data.MapId)); short gameEvent = result.Read(16); - uint PoolId = result.Read(17); + data.poolId = result.Read(17); data.npcflag = result.Read(18); data.unit_flags = result.Read(19); data.unit_flags2 = result.Read(20); @@ -3595,8 +3595,8 @@ namespace Game DB.World.Execute(stmt); } - // Add to grid if not managed by the game event or pool system - if (gameEvent == 0 && PoolId == 0) + // Add to grid if not managed by the game event + if (gameEvent == 0) AddCreatureToGrid(data); creatureDataStorage[guid] = data; @@ -4326,7 +4326,7 @@ namespace Game } short gameEvent = result.Read(15); - uint PoolId = result.Read(16); + data.poolId = result.Read(16); data.PhaseUseFlags = (PhaseUseFlagsValues)result.Read(17); data.PhaseId = result.Read(18); data.PhaseGroup = result.Read(19); @@ -4433,8 +4433,9 @@ namespace Game stmt.AddValue(2, guid); DB.World.Execute(stmt); } - - if (gameEvent == 0 && PoolId == 0) // if not this is to be managed by GameEvent System or Pool system + + // if not this is to be managed by GameEvent System + if (gameEvent == 0) AddGameObjectToGrid(data); gameObjectDataStorage[guid] = data; diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index f4d27cac7..d17d01246 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -81,6 +81,8 @@ namespace Game.Maps GetGuidSequenceGenerator(HighGuid.Transport).Set(Global.ObjectMgr.GetGenerator(HighGuid.Transport).GetNextAfterMaxUsed()); + _poolData = Global.PoolMgr.InitPoolsForMap(this); + Global.MMapMgr.LoadMapInstance(Global.WorldMgr.GetDataPath(), GetId(), i_InstanceId); Global.ScriptMgr.OnCreateMap(this); @@ -2639,7 +2641,7 @@ namespace Game.Maps _respawnTimes.Remove(next); // step 2: tell pooling logic to do its thing - Global.PoolMgr.UpdatePool(poolId, next.type, next.spawnId); + Global.PoolMgr.UpdatePool(GetPoolData(), poolId, next.type, next.spawnId); // step 3: get rid of the actual entry RemoveRespawnTime(next.type, next.spawnId, null, true); @@ -2733,6 +2735,10 @@ namespace Game.Maps if (!IsSpawnGroupActive(spawnGroup.groupId)) return false; + if (spawnData.ToSpawnData().poolId != 0) + if (!GetPoolData().IsSpawnedObject(type, spawnId)) + return false; + return true; } @@ -4261,6 +4267,8 @@ namespace Game.Maps public MultiPersonalPhaseTracker GetMultiPersonalPhaseTracker() { return _multiPersonalPhaseTracker; } + public SpawnedPoolData GetPoolData() { return _poolData; } + #region Scripts // Put scripts in the execution queue @@ -5217,6 +5225,7 @@ namespace Game.Maps Dictionary _zoneDynamicInfo = new(); IntervalTimer _weatherUpdateTimer; Dictionary _guidGenerators = new(); + SpawnedPoolData _poolData; Dictionary _objectsStore = new(); MultiMap _creatureBySpawnIdStore = new(); MultiMap _gameobjectBySpawnIdStore = new(); diff --git a/Source/Game/Maps/SpawnData.cs b/Source/Game/Maps/SpawnData.cs index 8846a5d18..65a40f1d0 100644 --- a/Source/Game/Maps/SpawnData.cs +++ b/Source/Game/Maps/SpawnData.cs @@ -37,6 +37,7 @@ namespace Game.Maps public uint PhaseId; public uint PhaseGroup; public int terrainSwapMap; + public uint poolId; public int spawntimesecs; public List SpawnDifficulties; public uint ScriptId; diff --git a/Source/Game/Pools/PoolManager.cs b/Source/Game/Pools/PoolManager.cs index d05f48fab..c0056558d 100644 --- a/Source/Game/Pools/PoolManager.cs +++ b/Source/Game/Pools/PoolManager.cs @@ -56,6 +56,7 @@ namespace Game PoolTemplateData pPoolTemplate = new(); pPoolTemplate.MaxLimit = result.Read(1); + pPoolTemplate.MapId = -1; mPoolTemplate[pool_id] = pPoolTemplate; ++count; } @@ -102,7 +103,17 @@ namespace Game Log.outError(LogFilter.Sql, "`pool_creature` has an invalid chance ({0}) for creature guid ({1}) in pool id ({2}), skipped.", chance, guid, pool_id); continue; } + PoolTemplateData pPoolTemplate = mPoolTemplate[pool_id]; + if (pPoolTemplate.MapId == -1) + pPoolTemplate.MapId = (int)data.MapId; + + if (pPoolTemplate.MapId != data.MapId) + { + Log.outError(LogFilter.Sql, $"`pool_creature` has creature spawns on multiple different maps for creature guid ({guid}) in pool id ({pool_id}), skipped."); + continue; + } + PoolObject plObject = new(guid, chance); if (!mPoolCreatureGroups.ContainsKey(pool_id)) @@ -173,6 +184,15 @@ namespace Game } PoolTemplateData pPoolTemplate = mPoolTemplate[pool_id]; + if (pPoolTemplate.MapId == -1) + pPoolTemplate.MapId = (int)data.MapId; + + if (pPoolTemplate.MapId != data.MapId) + { + Log.outError(LogFilter.Sql, $"`pool_gameobject` has gameobject spawns on multiple different maps for gameobject guid ({guid}) in pool id ({pool_id}), skipped."); + continue; + } + PoolObject plObject = new(guid, chance); if (!mPoolGameobjectGroups.ContainsKey(pool_id)) @@ -252,6 +272,16 @@ namespace Game } } + foreach (var (poolId, templateData) in mPoolTemplate) + { + if (IsEmpty(poolId)) + { + Log.outError(LogFilter.Sql, $"Pool Id {poolId} is empty (has no creatures and no gameobects and either no child pools or child pools are all empty. The pool will not be spawned"); + continue; + } + Cypher.Assert(templateData.MapId != -1); + } + // The initialize method will spawn all pools not in an event and not in another pool, this is why there is 2 left joins with 2 null checks Log.outInfo(LogFilter.ServerLoading, "Starting objects pooling system..."); { @@ -273,6 +303,9 @@ namespace Game uint pool_entry = result.Read(0); uint pool_pool_id = result.Read(1); + if (IsEmpty(pool_entry)) + continue; + if (!CheckPool(pool_entry)) { if (pool_pool_id != 0) @@ -287,54 +320,68 @@ namespace Game // Don't spawn child pools, they are spawned recursively by their parent pools if (pool_pool_id == 0) { - SpawnPool(pool_entry); + mAutoSpawnPoolsPerMap.Add((uint)mPoolTemplate[pool_entry].MapId, pool_entry); count++; } } while (result.NextRow()); - Log.outInfo(LogFilter.ServerLoading, "Pool handling system initialized, {0} pools spawned in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); + Log.outInfo(LogFilter.ServerLoading, "Pool handling system initialized, {0} pools will be spawned by default in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } } } - void SpawnPool(uint pool_id, ulong db_guid) + void SpawnPool(SpawnedPoolData spawnedPoolData, uint pool_id, ulong db_guid) { switch (typeof(T).Name) { case "Creature": if (mPoolCreatureGroups.ContainsKey(pool_id) && !mPoolCreatureGroups[pool_id].IsEmpty()) - mPoolCreatureGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, db_guid); + mPoolCreatureGroups[pool_id].SpawnObject(spawnedPoolData, mPoolTemplate[pool_id].MaxLimit, db_guid); break; case "GameObject": if (mPoolGameobjectGroups.ContainsKey(pool_id) && !mPoolGameobjectGroups[pool_id].IsEmpty()) - mPoolGameobjectGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, db_guid); + mPoolGameobjectGroups[pool_id].SpawnObject(spawnedPoolData, mPoolTemplate[pool_id].MaxLimit, db_guid); break; case "Pool": if (mPoolPoolGroups.ContainsKey(pool_id) && !mPoolPoolGroups[pool_id].IsEmpty()) - mPoolPoolGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, db_guid); + mPoolPoolGroups[pool_id].SpawnObject(spawnedPoolData, mPoolTemplate[pool_id].MaxLimit, db_guid); break; } } - public void SpawnPool(uint pool_id) + public void SpawnPool(SpawnedPoolData spawnedPoolData, uint pool_id) { - SpawnPool(pool_id, 0); - SpawnPool(pool_id, 0); - SpawnPool(pool_id, 0); + SpawnPool(spawnedPoolData, pool_id, 0); + SpawnPool(spawnedPoolData, pool_id, 0); + SpawnPool(spawnedPoolData, pool_id, 0); } - public void DespawnPool(uint pool_id, bool alwaysDeleteRespawnTime = false) + public void DespawnPool(SpawnedPoolData spawnedPoolData, uint pool_id, bool alwaysDeleteRespawnTime = false) { if (mPoolCreatureGroups.ContainsKey(pool_id) && !mPoolCreatureGroups[pool_id].IsEmpty()) - mPoolCreatureGroups[pool_id].DespawnObject(mSpawnedData, 0, alwaysDeleteRespawnTime); + mPoolCreatureGroups[pool_id].DespawnObject(spawnedPoolData, 0, alwaysDeleteRespawnTime); if (mPoolGameobjectGroups.ContainsKey(pool_id) && !mPoolGameobjectGroups[pool_id].IsEmpty()) - mPoolGameobjectGroups[pool_id].DespawnObject(mSpawnedData, 0, alwaysDeleteRespawnTime); + mPoolGameobjectGroups[pool_id].DespawnObject(spawnedPoolData, 0, alwaysDeleteRespawnTime); if (mPoolPoolGroups.ContainsKey(pool_id) && !mPoolPoolGroups[pool_id].IsEmpty()) - mPoolPoolGroups[pool_id].DespawnObject(mSpawnedData, 0, alwaysDeleteRespawnTime); + mPoolPoolGroups[pool_id].DespawnObject(spawnedPoolData, 0, alwaysDeleteRespawnTime); + } + + public bool IsEmpty(uint pool_id) + { + if (mPoolGameobjectGroups.TryGetValue(pool_id, out PoolGroup gameobjectPool) && !gameobjectPool.IsEmptyDeepCheck()) + return false; + + if (mPoolCreatureGroups.TryGetValue(pool_id, out PoolGroup creaturePool) && !creaturePool.IsEmptyDeepCheck()) + return false; + + if (mPoolPoolGroups.TryGetValue(pool_id, out PoolGroup pool) && !pool.IsEmptyDeepCheck()) + return false; + + return true; } public bool CheckPool(uint pool_id) @@ -351,28 +398,44 @@ namespace Game return true; } - public void UpdatePool(uint pool_id, ulong db_guid_or_pool_id) + public void UpdatePool(SpawnedPoolData spawnedPoolData, uint pool_id, ulong db_guid_or_pool_id) { uint motherpoolid = IsPartOfAPool(pool_id); if (motherpoolid != 0) - SpawnPool(motherpoolid, pool_id); + SpawnPool(spawnedPoolData, motherpoolid, pool_id); else - SpawnPool(pool_id, db_guid_or_pool_id); + SpawnPool(spawnedPoolData, pool_id, db_guid_or_pool_id); } - public void UpdatePool(uint pool_id, SpawnObjectType type, ulong spawnId) + public void UpdatePool(SpawnedPoolData spawnedPoolData, uint pool_id, SpawnObjectType type, ulong spawnId) { switch (type) { case SpawnObjectType.Creature: - UpdatePool(pool_id, spawnId); + UpdatePool(spawnedPoolData, pool_id, spawnId); break; case SpawnObjectType.GameObject: - UpdatePool(pool_id, spawnId); + UpdatePool(spawnedPoolData, pool_id, spawnId); break; } } + public SpawnedPoolData InitPoolsForMap(Map map) + { + SpawnedPoolData spawnedPoolData = new(map); + var poolIds = mAutoSpawnPoolsPerMap.LookupByKey(spawnedPoolData.GetMap().GetId()); + if (poolIds != null) + foreach (uint poolId in poolIds) + SpawnPool(spawnedPoolData, poolId); + + return spawnedPoolData; + } + + public PoolTemplateData GetPoolTemplate(uint pool_id) + { + return mPoolTemplate.LookupByKey(pool_id); + } + public uint IsPartOfAPool(ulong db_guid) { switch (typeof(T).Name) @@ -403,7 +466,7 @@ namespace Game return 0; } } - + public enum QuestTypes { None = 0, @@ -411,8 +474,19 @@ namespace Game Weekly = 2 } - public bool IsSpawnedObject(ulong db_guid_or_pool_id) { return mSpawnedData.IsActiveObject(db_guid_or_pool_id); } - + public bool IsSpawnedObject(ulong db_guid_or_pool_id) + { + switch (typeof(T).Name) + { + case "Creature": + return mCreatureSearchMap.ContainsKey(db_guid_or_pool_id); + case "GameObject": + return mGameobjectSearchMap.ContainsKey(db_guid_or_pool_id); + case "Pool": + return mPoolSearchMap.ContainsKey(db_guid_or_pool_id); + } + return false; + } public MultiMap mQuestCreatureRelation = new(); public MultiMap mQuestGORelation = new(); @@ -425,8 +499,7 @@ namespace Game Dictionary mGameobjectSearchMap = new(); Dictionary mPoolSearchMap = new(); - // dynamic data - ActivePoolData mSpawnedData = new(); + MultiMap mAutoSpawnPoolsPerMap = new(); } public class PoolGroup @@ -436,6 +509,22 @@ namespace Game poolId = 0; } + public bool IsEmptyDeepCheck() + { + if (typeof(T).Name != "Pool") + return IsEmpty(); + + foreach (PoolObject explicitlyChanced in ExplicitlyChanced) + if (!Global.PoolMgr.IsEmpty((uint)explicitlyChanced.guid)) + return false; + + foreach (PoolObject equalChanced in EqualChanced) + if (!Global.PoolMgr.IsEmpty((uint)equalChanced.guid)) + return false; + + return true; + } + public void AddEntry(PoolObject poolitem, uint maxentries) { if (poolitem.chance != 0 && maxentries == 1) @@ -457,95 +546,79 @@ namespace Game return true; } - public void DespawnObject(ActivePoolData spawns, ulong guid = 0, bool alwaysDeleteRespawnTime = false) + public void DespawnObject(SpawnedPoolData spawns, ulong guid = 0, bool alwaysDeleteRespawnTime = false) { for (int i = 0; i < EqualChanced.Count; ++i) { // if spawned - if (spawns.IsActiveObject(EqualChanced[i].guid)) + if (spawns.IsSpawnedObject(EqualChanced[i].guid)) { if (guid == 0 || EqualChanced[i].guid == guid) { - Despawn1Object(EqualChanced[i].guid, alwaysDeleteRespawnTime); - spawns.RemoveObject(EqualChanced[i].guid, poolId); + Despawn1Object(spawns, EqualChanced[i].guid, alwaysDeleteRespawnTime); + spawns.RemoveSpawn(EqualChanced[i].guid, poolId); } } else if (alwaysDeleteRespawnTime) - RemoveRespawnTimeFromDB(EqualChanced[i].guid); + RemoveRespawnTimeFromDB(spawns, EqualChanced[i].guid); } for (int i = 0; i < ExplicitlyChanced.Count; ++i) { // spawned - if (spawns.IsActiveObject(ExplicitlyChanced[i].guid)) + if (spawns.IsSpawnedObject(ExplicitlyChanced[i].guid)) { if (guid == 0 || ExplicitlyChanced[i].guid == guid) { - Despawn1Object(ExplicitlyChanced[i].guid, alwaysDeleteRespawnTime); - spawns.RemoveObject(ExplicitlyChanced[i].guid, poolId); + Despawn1Object(spawns, ExplicitlyChanced[i].guid, alwaysDeleteRespawnTime); + spawns.RemoveSpawn(ExplicitlyChanced[i].guid, poolId); } } else if (alwaysDeleteRespawnTime) - RemoveRespawnTimeFromDB(ExplicitlyChanced[i].guid); + RemoveRespawnTimeFromDB(spawns, ExplicitlyChanced[i].guid); } } - void Despawn1Object(ulong guid, bool alwaysDeleteRespawnTime = false, bool saveRespawnTime = true) + void Despawn1Object(SpawnedPoolData spawns, ulong guid, bool alwaysDeleteRespawnTime = false, bool saveRespawnTime = true) { switch (typeof(T).Name) { case "Creature": + { + var creatureBounds = spawns.GetMap().GetCreatureBySpawnIdStore().LookupByKey(guid); + foreach (var creature in creatureBounds) { - var data = Global.ObjectMgr.GetCreatureData(guid); - if (data != null) - { - Global.ObjectMgr.RemoveCreatureFromGrid(data); - Map map = Global.MapMgr.FindMap(data.MapId, 0); - if (map != null && !map.Instanceable()) - { - var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(guid); - foreach (var creature in creatureBounds) - { - // For dynamic spawns, save respawn time here - if (saveRespawnTime && !creature.GetRespawnCompatibilityMode()) - creature.SaveRespawnTime(); + // For dynamic spawns, save respawn time here + if (saveRespawnTime && !creature.GetRespawnCompatibilityMode()) + creature.SaveRespawnTime(); - creature.AddObjectToRemoveList(); - } - - if (alwaysDeleteRespawnTime) - map.RemoveRespawnTime(SpawnObjectType.Creature, guid, null, true); - } - } - break; + creature.AddObjectToRemoveList(); } + + if (alwaysDeleteRespawnTime) + spawns.GetMap().RemoveRespawnTime(SpawnObjectType.Creature, guid, null, true); + + break; + } case "GameObject": + { + var gameobjectBounds = spawns.GetMap().GetGameObjectBySpawnIdStore().LookupByKey(guid); + foreach (var go in gameobjectBounds) { - var data = Global.ObjectMgr.GetGameObjectData(guid); - if (data != null) - { - Global.ObjectMgr.RemoveGameObjectFromGrid(data); - Map map = Global.MapMgr.FindMap(data.MapId, 0); - if (map != null && !map.Instanceable()) - { - var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid); - foreach (var go in gameobjectBounds) - { - // For dynamic spawns, save respawn time here - if (saveRespawnTime && !go.GetRespawnCompatibilityMode()) - go.SaveRespawnTime(); + // For dynamic spawns, save respawn time here + if (saveRespawnTime && !go.GetRespawnCompatibilityMode()) + go.SaveRespawnTime(); - go.AddObjectToRemoveList(); - } - - if (alwaysDeleteRespawnTime) - map.RemoveRespawnTime(SpawnObjectType.GameObject, guid, null, true); - } - } - break; + go.AddObjectToRemoveList(); } + + if (alwaysDeleteRespawnTime) + spawns.GetMap().RemoveRespawnTime(SpawnObjectType.GameObject, guid, null, true); + + break; + } case "Pool": - Global.PoolMgr.DespawnPool((uint)guid, alwaysDeleteRespawnTime); + Global.PoolMgr.DespawnPool(spawns, (uint)guid, alwaysDeleteRespawnTime); break; } } @@ -573,9 +646,9 @@ namespace Game } } - public void SpawnObject(ActivePoolData spawns, uint limit, ulong triggerFrom) + public void SpawnObject(SpawnedPoolData spawns, uint limit, ulong triggerFrom) { - int count = (int)(limit - spawns.GetActiveObjectCount(poolId)); + int count = (int)(limit - spawns.GetSpawnedObjects(poolId)); // If triggered from some object respawn this object is still marked as spawned // and also counted into m_SpawnedPoolAmount so we need increase count to be @@ -598,17 +671,17 @@ namespace Game roll -= obj.chance; // Triggering object is marked as spawned at this time and can be also rolled (respawn case) // so this need explicit check for this case - if (roll < 0 && (obj.guid == triggerFrom || !spawns.IsActiveObject(obj.guid))) + if (roll < 0 && (obj.guid == triggerFrom || !spawns.IsSpawnedObject(obj.guid))) { rolledObjects.Add(obj); break; } } } - + if (!EqualChanced.Empty() && rolledObjects.Empty()) { - rolledObjects.AddRange(EqualChanced.Where(obj => obj.guid == triggerFrom || !spawns.IsActiveObject(obj.guid))); + rolledObjects.AddRange(EqualChanced.Where(obj => obj.guid == triggerFrom || !spawns.IsSpawnedObject(obj.guid))); rolledObjects.RandomResize((uint)count); } @@ -617,13 +690,13 @@ namespace Game { if (obj.guid == triggerFrom) { - ReSpawn1Object(obj); + ReSpawn1Object(spawns, obj); triggerFrom = 0; } else { - spawns.ActivateObject(obj.guid, poolId); - Spawn1Object(obj); + spawns.AddSpawn(obj.guid, poolId); + Spawn1Object(spawns, obj); } } } @@ -633,95 +706,67 @@ namespace Game DespawnObject(spawns, triggerFrom); } - void Spawn1Object(PoolObject obj) - { - switch (typeof(T).Name) - { - case "Creature": - { - CreatureData data = Global.ObjectMgr.GetCreatureData(obj.guid); - if (data != null) - { - Global.ObjectMgr.AddCreatureToGrid(data); - - // Spawn if necessary (loaded grids only) - Map map = Global.MapMgr.FindMap(data.MapId, 0); - // We use spawn coords to spawn - if (map != null && !map.Instanceable() && map.IsGridLoaded(data.SpawnPoint)) - Creature.CreateCreatureFromDB(obj.guid, map); - } - } - break; - case "GameObject": - { - GameObjectData data = Global.ObjectMgr.GetGameObjectData(obj.guid); - if (data != null) - { - Global.ObjectMgr.AddGameObjectToGrid(data); - // Spawn if necessary (loaded grids only) - // this base map checked as non-instanced and then only existed - Map map = Global.MapMgr.FindMap(data.MapId, 0); - // We use current coords to unspawn, not spawn coords since creature can have changed grid - if (map != null && !map.Instanceable() && map.IsGridLoaded(data.SpawnPoint)) - { - GameObject go = GameObject.CreateGameObjectFromDB(obj.guid, map, false); - if (go) - { - if (go.IsSpawnedByDefault()) - map.AddToMap(go); - } - } - } - } - break; - case "Pool": - Global.PoolMgr.SpawnPool((uint)obj.guid); - break; - } - } - - void ReSpawn1Object(PoolObject obj) - { - switch (typeof(T).Name) - { - case "Creature": - case "GameObject": - Despawn1Object(obj.guid, false, false); - Spawn1Object(obj); - break; - } - } - - void RemoveRespawnTimeFromDB(ulong guid) + void Spawn1Object(SpawnedPoolData spawns, PoolObject obj) { switch (typeof(T).Name) { case "Creature": { - CreatureData data = Global.ObjectMgr.GetCreatureData(guid); + CreatureData data = Global.ObjectMgr.GetCreatureData(obj.guid); if (data != null) { - Map map = Global.MapMgr.CreateBaseMap(data.MapId); - if (!map.Instanceable()) - { - map.RemoveRespawnTime(SpawnObjectType.Creature, guid, null, true); - } + // Spawn if necessary (loaded grids only) + // We use spawn coords to spawn + if (spawns.GetMap().IsGridLoaded(data.SpawnPoint)) + Creature.CreateCreatureFromDB(obj.guid, spawns.GetMap()); } } break; case "GameObject": { - GameObjectData data = Global.ObjectMgr.GetGameObjectData(guid); + GameObjectData data = Global.ObjectMgr.GetGameObjectData(obj.guid); if (data != null) { - Map map = Global.MapMgr.CreateBaseMap(data.MapId); - if (!map.Instanceable()) + // Spawn if necessary (loaded grids only) + // We use current coords to unspawn, not spawn coords since creature can have changed grid + if (spawns.GetMap().IsGridLoaded(data.SpawnPoint)) { - map.RemoveRespawnTime(SpawnObjectType.GameObject, guid, null, true); + GameObject go = GameObject.CreateGameObjectFromDB(obj.guid, spawns.GetMap(), false); + if (go && go.IsSpawnedByDefault()) + if (!spawns.GetMap().AddToMap(go)) + go.Dispose(); } } - break; } + break; + case "Pool": + Global.PoolMgr.SpawnPool(spawns, (uint)obj.guid); + break; + } + } + + void ReSpawn1Object(SpawnedPoolData spawns, PoolObject obj) + { + switch (typeof(T).Name) + { + case "Creature": + case "GameObject": + Despawn1Object(spawns, obj.guid, false, false); + Spawn1Object(spawns, obj); + break; + } + } + + void RemoveRespawnTimeFromDB(SpawnedPoolData spawns, ulong guid) + { + switch (typeof(T).Name) + { + case "Creature": + spawns.GetMap().RemoveRespawnTime(SpawnObjectType.Creature, guid, null, true); + break; + case "GameObject": + spawns.GetMap().RemoveRespawnTime(SpawnObjectType.GameObject, guid, null, true); + break; } } @@ -729,12 +774,6 @@ namespace Game public bool IsEmpty() { return ExplicitlyChanced.Empty() && EqualChanced.Empty(); } - public ulong GetFirstEqualChancedObjectId() - { - if (EqualChanced.Empty()) - return 0; - return EqualChanced.FirstOrDefault().guid; - } public uint GetPoolId() { return poolId; } uint poolId; @@ -742,14 +781,24 @@ namespace Game List EqualChanced = new(); } - public class ActivePoolData + public class SpawnedPoolData { - public uint GetActiveObjectCount(uint pool_id) + Map mOwner; + List mSpawnedCreatures = new(); + List mSpawnedGameobjects = new(); + Dictionary mSpawnedPools = new(); + + public SpawnedPoolData(Map owner) + { + mOwner = owner; + } + + public uint GetSpawnedObjects(uint pool_id) { return mSpawnedPools.LookupByKey(pool_id); } - public bool IsActiveObject(ulong db_guid) + public bool IsSpawnedObject(ulong db_guid) { switch (typeof(T).Name) { @@ -761,10 +810,24 @@ namespace Game return mSpawnedPools.ContainsKey(db_guid); default: return false; - } + } } - public void ActivateObject(ulong db_guid, uint pool_id) + public bool IsSpawnedObject(SpawnObjectType type, ulong db_guid_or_pool_id) + { + switch (type) + { + case SpawnObjectType.Creature: + return mSpawnedCreatures.Contains(db_guid_or_pool_id); + case SpawnObjectType.GameObject: + return mSpawnedGameobjects.Contains(db_guid_or_pool_id); + default: + Log.outFatal(LogFilter.Misc, $"Invalid spawn type {type} passed to SpawnedPoolData::IsSpawnedObject (with spawnId {db_guid_or_pool_id})"); + return false; + } + } + + public void AddSpawn(ulong db_guid, uint pool_id) { switch (typeof(T).Name) { @@ -786,7 +849,7 @@ namespace Game ++mSpawnedPools[pool_id]; } - public void RemoveObject(ulong db_guid, uint pool_id) + public void RemoveSpawn(ulong db_guid, uint pool_id) { switch (typeof(T).Name) { @@ -807,9 +870,7 @@ namespace Game --mSpawnedPools[pool_id]; } - List mSpawnedCreatures = new(); - List mSpawnedGameobjects = new(); - Dictionary mSpawnedPools = new(); + public Map GetMap() { return mOwner; } } public class PoolObject @@ -824,9 +885,10 @@ namespace Game public float chance; } - public struct PoolTemplateData + public class PoolTemplateData { public uint MaxLimit; + public int MapId; } public class Pool { } // for Pool of Pool case