/// <summary> /// Get all (already populated) areas with their objects inside the zone /// </summary> private async Task ServerFillSpawnAreasInZoneAsync( IServerZone zone, IReadOnlyDictionary <Vector2Ushort, SpawnZoneArea> areas, Func <Task> callbackYieldIfOutOfTime) { // this is a heavy method so we will try to yield every 100 objects to reduce the load const int defaultCounterToYieldValue = 100; var counterToYield = defaultCounterToYieldValue; // this check has a problem - it returns only objects strictly inside the zone, // but we also need to consider objects nearby the zone for restriction presets //await zone.PopulateStaticObjectsInZone(tempList, callbackYieldIfOutOfTime); TempListAllStaticWorldObjects.Clear(); await Api.Server.World.GetStaticWorldObjectsAsync(TempListAllStaticWorldObjects); foreach (var staticObject in TempListAllStaticWorldObjects) { await YieldIfOutOfTime(); if (staticObject.IsDestroyed) { continue; } var position = staticObject.TilePosition; var area = GetArea(position, isMobTrackingEnumeration: false); if (area is null) { continue; } var protoStaticWorldObject = staticObject.ProtoStaticWorldObject; if (!(protoStaticWorldObject is ObjectGroundItemsContainer)) { if (protoStaticWorldObject.IsIgnoredBySpawnScripts) { // we don't consider padding to certain objects such as ground decals // (though they still might affect spawn during the tiles check) continue; } // create entry for regular static object var preset = this.FindPreset(protoStaticWorldObject); if (preset != null && preset.Density > 0 && !zone.IsContainsPosition(staticObject.TilePosition)) { // this object is a part of the preset spawn list but it's not present in the zone // don't consider this object // TODO: this might cause a problem if there is a padding to this object check continue; } area.Add(preset, position); continue; } // ground container object var itemsContainer = ObjectGroundItemsContainer.GetPublicState(staticObject).ItemsContainer; foreach (var item in itemsContainer.Items) { // create entry for each item in the ground container var preset = this.FindPreset(item.ProtoItem); if (preset != null && preset.Density > 0 && !zone.IsContainsPosition(staticObject.TilePosition)) { // this object is a part of the preset spawn list but it's not present in the zone // don't consider this object continue; } area.Add(preset, position); } } var mobsTrackingManager = SpawnedMobsTrackingManagersStore.Get(this, zone); foreach (var mob in mobsTrackingManager.EnumerateAll()) { await YieldIfOutOfTime(); var position = mob.TilePosition; var area = GetArea(position, isMobTrackingEnumeration: true); area?.Add(this.FindPreset(mob.ProtoCharacter), position); } return; SpawnZoneArea GetArea(Vector2Ushort tilePosition, bool isMobTrackingEnumeration) { var zoneChunkStartPosition = SpawnZoneArea.CalculateStartPosition(tilePosition); if (areas.TryGetValue(zoneChunkStartPosition, out var area)) { return(area); } if (isMobTrackingEnumeration) { return(null); } return(null); // throw new Exception("No zone area found for " + tilePosition); //var newArea = new SpawnZoneArea(zoneChunkStartPosition, zoneChunk); //areas.Add(newArea); //return newArea; } Task YieldIfOutOfTime() { if (--counterToYield > 0) { return(Task.CompletedTask); } counterToYield = defaultCounterToYieldValue; return(callbackYieldIfOutOfTime()); } }
/// <summary> /// Get all (already populated) areas with their objects inside the zone /// </summary> private async Task ServerFillSpawnAreasInZoneAsync( IServerZone zone, IReadOnlyDictionary <Vector2Ushort, SpawnZoneArea> areas, Func <Task> callbackYieldIfOutOfTime) { // this is a heavy method so we will try to yield every 100 objects to reduce the load const int defaultCounterToYieldValue = 100; var counterToYield = defaultCounterToYieldValue; using (var tempList = Api.Shared.GetTempList <IStaticWorldObject>()) { await zone.PopulateStaticObjectsInZone(tempList, callbackYieldIfOutOfTime); foreach (var staticObject in tempList) { await YieldIfOutOfTime(); if (staticObject.IsDestroyed) { continue; } var position = staticObject.TilePosition; var area = GetArea(position, isMobTrackingEnumeration: false); var protoStaticWorldObject = staticObject.ProtoStaticWorldObject; if (!(protoStaticWorldObject is ObjectGroundItemsContainer)) { if (protoStaticWorldObject.Kind == StaticObjectKind.FloorDecal) { // we don't consider padding to decal objects // (though they still might affect spawn during tile check) continue; } // create entry for regular static object area.Add(this.FindPreset(protoStaticWorldObject), position); continue; } // ground container object var itemsContainer = ObjectGroundItemsContainer.GetPublicState(staticObject).ItemsContainer; foreach (var item in itemsContainer.Items) { // create entry for each item in the ground container area.Add(this.FindPreset(item.ProtoItem), position); } } } var mobsTrackingManager = SpawnedMobsTrackingManagersStore.Get(this, zone); foreach (var mob in mobsTrackingManager.EnumerateAll()) { await YieldIfOutOfTime(); var position = mob.TilePosition; var area = GetArea(position, isMobTrackingEnumeration: true); area?.Add(this.FindPreset(mob.ProtoCharacter), position); } return; SpawnZoneArea GetArea(Vector2Ushort tilePosition, bool isMobTrackingEnumeration) { var zoneChunkStartPosition = SpawnZoneArea.CalculateStartPosition(tilePosition); if (areas.TryGetValue(zoneChunkStartPosition, out var area)) { return(area); } if (isMobTrackingEnumeration) { return(null); } throw new Exception("No zone area found for " + tilePosition); //var newArea = new SpawnZoneArea(zoneChunkStartPosition, zoneChunk); //areas.Add(newArea); //return newArea; } Task YieldIfOutOfTime() { if (--counterToYield > 0) { return(Task.CompletedTask); } counterToYield = defaultCounterToYieldValue; return(callbackYieldIfOutOfTime()); } }