diff --git a/Projects/Server/Effects.cs b/Projects/Server/Effects.cs index 787f6ad66..938413701 100644 --- a/Projects/Server/Effects.cs +++ b/Projects/Server/Effects.cs @@ -74,9 +74,7 @@ public static class Effects { Span buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); - var eable = map.GetClientsInRange(new Point3D(p)); - - foreach (var state in eable) + foreach (var state in map.GetClientsInRange(p)) { state.Mobile.ProcessDelta(); OutgoingEffectPackets.CreateSoundEffect(buffer, soundID, p); @@ -100,9 +98,7 @@ public static class Effects Span boltEffect = stackalloc byte[OutgoingEffectPackets.BoltEffectLength].InitializePacket(); Span soundEffect = sound ? stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket() : null; - var eable = map.GetClientsInRange(e.Location); - - foreach (var state in eable) + foreach (var state in map.GetClientsInRange(e.Location)) { if (state.Mobile.CanSee(e)) { @@ -169,9 +165,7 @@ public static class Effects Span regular = itemID != 0 ? stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket() : null; - var eable = map.GetClientsInRange(e.Location); - - foreach (var state in eable) + foreach (var state in map.GetClientsInRange(e.Location)) { state.Mobile.ProcessDelta(); @@ -232,9 +226,7 @@ public static class Effects Span particles = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength].InitializePacket(); Span regular = itemID != 0 ? stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket() : null; - var eable = map.GetClientsInRange(target.Location); - - foreach (var state in eable) + foreach (var state in map.GetClientsInRange(target.Location)) { state.Mobile.ProcessDelta(); @@ -446,9 +438,7 @@ public static class Effects Span particles = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength].InitializePacket(); Span regular = itemID != 0 ? stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket() : null; - var eable = map.GetClientsInRange(from.Location); - - foreach (var state in eable) + foreach (var state in map.GetClientsInRange(from.Location)) { state.Mobile.ProcessDelta(); @@ -479,9 +469,7 @@ public static class Effects return; } - var eable = map.GetClientsInRange(new Point3D(origin)); - - foreach (var state in eable) + foreach (var state in map.GetClientsInRange(origin)) { state.Mobile.ProcessDelta(); state.Send(effectBuffer); diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 267150fe0..58b910dd8 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -332,11 +332,9 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt { var worldLoc = GetWorldLocation(); - var eable = m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange()); - Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange())) { var m = state.Mobile; @@ -1106,9 +1104,7 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt Span saWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); Span hsWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); - var eable = m_Map.GetClientsInRange(m_Location, GetMaxUpdateRange()); - - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location, GetMaxUpdateRange())) { var m = state.Mobile; @@ -1157,15 +1153,11 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt } else if (m_Map != null) { - IPooledEnumerable eable; - if (oldLocation.m_X != 0) { - eable = m_Map.GetClientsInRange(oldLocation, GetMaxUpdateRange()); - Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(oldLocation, GetMaxUpdateRange())) { var m = state.Mobile; @@ -1182,9 +1174,7 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt m_Location = location; OnLocationChange(oldRealLocation); - eable = m_Map.GetClientsInRange(m_Location, GetMaxUpdateRange()); - - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location, GetMaxUpdateRange())) { var m = state.Mobile; @@ -1352,9 +1342,7 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt return; } - var eable = map.GetClientsInRange(worldLoc, GetMaxUpdateRange()); - - foreach (var state in eable) + foreach (var state in map.GetClientsInRange(worldLoc, GetMaxUpdateRange())) { var m = state.Mobile; @@ -1488,15 +1476,11 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt { if (m_Parent == null) { - IPooledEnumerable eable; - if (m_Location.m_X != 0) { - eable = m_Map.GetClientsInRange(oldLocation, GetMaxUpdateRange()); - Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(oldLocation, GetMaxUpdateRange())) { var m = state.Mobile; @@ -1513,9 +1497,7 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt SetLastMoved(); - eable = m_Map.GetClientsInRange(m_Location, GetMaxUpdateRange()); - - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location, GetMaxUpdateRange())) { var m = state.Mobile; @@ -2503,9 +2485,11 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt m_Map == null ? Map.MobileBoundsEnumerable.Empty : m_Map.GetMobilesInRange(m_Parent == null ? m_Location : GetWorldLocation(), range); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IPooledEnumerable GetClientsInRange(int range) => - m_Map.GetClientsInRange(m_Parent == null ? m_Location : GetWorldLocation(), range) - ?? PooledEnumeration.NullEnumerable.Instance; + public Map.ClientAtEnumerable GetClientsAt() => m_Map == null ? Map.ClientAtEnumerable.Empty : Map.GetClientsAt(m_Parent == null ? m_Location : GetWorldLocation()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ClientBoundsEnumerable GetClientsInRange(int range) => + m_Map == null ? Map.ClientBoundsEnumerable.Empty : Map.GetClientsInRange(m_Parent == null ? m_Location : GetWorldLocation(), range); public bool GetTempFlag(int flag) => ((LookupCompactInfo()?.m_TempFlags ?? 0) & flag) != 0; @@ -3276,11 +3260,10 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt } var worldLoc = GetWorldLocation(); - var eable = m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange()); Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange())) { var m = state.Mobile; @@ -3308,11 +3291,10 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt } var worldLoc = GetWorldLocation(); - var eable = m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange()); Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedLength(args)].InitializePacket(); - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange())) { var m = state.Mobile; @@ -3776,11 +3758,9 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt return; } - var eable = m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange()); - Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange())) { var m = state.Mobile; diff --git a/Projects/Server/Maps/Map.ClientEnumerator.cs b/Projects/Server/Maps/Map.ClientEnumerator.cs new file mode 100644 index 000000000..00798e1cf --- /dev/null +++ b/Projects/Server/Maps/Map.ClientEnumerator.cs @@ -0,0 +1,270 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Map.ClientEnumerator.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using Server.Collections; +using Server.Network; + +namespace Server; + +public partial class Map +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientAtEnumerable GetClientsAt(Point3D p) => GetClientsAt(new Point2D(p.X, p.Y)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientAtEnumerable GetClientsAt(int x, int y) => GetClientsAt(new Point2D(x, y)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientAtEnumerable GetClientsAt(Point2D p) => new(this, p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientBoundsEnumerable GetClientsInRange(Point3D p) => GetClientsInRange(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientBoundsEnumerable GetClientsInRange(Point3D p, int range) => + GetClientsInRange(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientBoundsEnumerable GetClientsInRange(Point2D p) => GetClientsInRange(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientBoundsEnumerable GetClientsInRange(Point2D p, int range) => + GetClientsInRange(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientBoundsEnumerable GetClientsInRange(int x, int y, int range) => + GetClientsInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientBoundsEnumerable GetClientsInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) => + new(this, bounds, makeBoundsInclusive); + + public ref struct ClientAtEnumerable + { + public static ClientAtEnumerable Empty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(); + } + + private readonly Map _map; + private readonly Point2D _location; + + public ClientAtEnumerable(Map map, Point2D loc) + { + _map = map; + _location = loc; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientAtEnumerator GetEnumerator() => new(_map, _location); + } + + public ref struct ClientAtEnumerator + { + private bool _started; + private Point2D _location; + private ref readonly ValueLinkList _linkList; + private int _version; + private NetState _current; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientAtEnumerator(Map map, Point2D loc) + { + _started = false; + _location = loc; + _linkList = ref map.GetRealSector(loc.m_X, loc.m_Y).Clients; + _version = 0; + _current = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + ref var loc = ref _location; + NetState current; + Mobile m; + + if (!_started) + { + current = _linkList._first; + _started = true; + _version = _linkList.Version; + + m = current.Mobile; + if (m?.Deleted == false && m.X == loc.m_X && m.Y == loc.m_Y) + { + _current = current; + return true; + } + } + else if (_linkList.Version != _version) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + else + { + current = _current; + } + + while (current != null) + { + current = current.Next; + + m = current.Mobile; + if (m?.Deleted == false && m.X == loc.m_X && m.Y == loc.m_Y) + { + _current = current; + return true; + } + } + + return false; + } + + public NetState Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + } + + public ref struct ClientBoundsEnumerable + { + public static ClientBoundsEnumerable Empty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(null, Rectangle2D.Empty, false); + } + + private readonly Map _map; + private readonly Rectangle2D _bounds; + private readonly bool _makeBoundsInclusive; + + public ClientBoundsEnumerable(Map map, Rectangle2D bounds, bool makeBoundsInclusive) + { + _map = map; + _bounds = bounds; + _makeBoundsInclusive = makeBoundsInclusive; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileEnumerator GetEnumerator() => new(_map, _bounds, _makeBoundsInclusive); + } + + public ref struct MobileEnumerator + { + private readonly Map _map; + private readonly int _sectorStartX; + private readonly int _sectorEndX; + private readonly int _sectorEndY; + private Rectangle2D _bounds; + + private int _currentSectorX; + private int _currentSectorY; + + private ref readonly ValueLinkList _linkList; + private int _currentVersion; + private NetState _current; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive) + { + _map = map; + _bounds = bounds; + + if (makeBoundsInclusive) + { + ++bounds.Width; + ++bounds.Height; + } + + _bounds = bounds; + + map.CalculateSectors(bounds, out _sectorStartX, out var _sectorStartY, out _sectorEndX, out _sectorEndY); + + // We start the X sector one short because it gets incremented immediately in MoveNext() + _currentSectorX = _sectorStartX - 1; + _currentSectorY = _sectorStartY; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + var map = _map; + + if (map == null) + { + return false; + } + + Mobile m; + NetState current = _current; + ref Rectangle2D bounds = ref _bounds; + var currentSectorX = _currentSectorX; + var currentSectorY = _currentSectorY; + var sectorEndX = _sectorEndX; + var sectorEndY = _sectorEndY; + + while (true) + { + current = current?.Next; + + while (current == null) + { + // Move to next sector + if (currentSectorX < sectorEndX) + { + _currentSectorX = ++currentSectorX; + } + else if (currentSectorY < sectorEndY) + { + _currentSectorX = currentSectorX = _sectorStartX; + _currentSectorY = ++currentSectorY; + } + else + { + // Ran out of sectors + return false; + } + + _linkList = ref map.GetRealSector(currentSectorX, currentSectorY).Clients; + _currentVersion = _linkList.Version; + current = _linkList._first; + } + + if (_linkList.Version != _currentVersion) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + + m = current.Mobile; + if (m?.Deleted == false && bounds.Contains(m.Location)) + { + _current = current; + return true; + } + } + } + + public NetState Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + } +} diff --git a/Projects/Server/Maps/Map.ItemEnumerator.cs b/Projects/Server/Maps/Map.ItemEnumerator.cs index 69fa938cf..988668942 100644 --- a/Projects/Server/Maps/Map.ItemEnumerator.cs +++ b/Projects/Server/Maps/Map.ItemEnumerator.cs @@ -84,8 +84,8 @@ public partial class Map get => new(); } - private Map _map; - private Point2D _location; + private readonly Map _map; + private readonly Point2D _location; public ItemAtEnumerable(Map map, Point2D loc) { diff --git a/Projects/Server/Maps/Map.MobileEnumerator.cs b/Projects/Server/Maps/Map.MobileEnumerator.cs index adcb5e40d..18f5469fd 100644 --- a/Projects/Server/Maps/Map.MobileEnumerator.cs +++ b/Projects/Server/Maps/Map.MobileEnumerator.cs @@ -84,8 +84,8 @@ public partial class Map get => new(); } - private Map _map; - private Point2D _location; + private readonly Map _map; + private readonly Point2D _location; public MobileAtEnumerable(Map map, Point2D loc) { diff --git a/Projects/Server/Maps/Map.cs b/Projects/Server/Maps/Map.cs index 8d5439e82..780e0567c 100644 --- a/Projects/Server/Maps/Map.cs +++ b/Projects/Server/Maps/Map.cs @@ -873,14 +873,6 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public IPooledEnumerable GetObjectsInBounds(Rectangle2D bounds) => PooledEnumeration.GetEntities(this, bounds); - public IPooledEnumerable GetClientsInRange(Point3D p) => GetClientsInRange(p, Core.GlobalMaxUpdateRange); - - public IPooledEnumerable GetClientsInRange(Point3D p, int range) => - GetClientsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)); - - public IPooledEnumerable GetClientsInBounds(Rectangle2D bounds) => - PooledEnumeration.GetClients(this, bounds); - public bool CanFit( Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true, bool requireSurface = true @@ -1426,12 +1418,10 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public class Sector { // TODO: Can we avoid this? - private static readonly List m_DefaultMobileList = new(); - private static readonly List m_DefaultClientList = new(); private static readonly List m_DefaultMultiList = new(); private static readonly List m_DefaultRectList = new(); private bool m_Active; - private List _clients; + private ValueLinkList _clients; private ValueLinkList _items; private ValueLinkList _mobiles; private List _multis; @@ -1453,7 +1443,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa internal ref readonly ValueLinkList Items => ref _items; - public List Clients => _clients ?? m_DefaultClientList; + internal ref readonly ValueLinkList Clients => ref _clients; public bool Active => m_Active && Owner != Internal; @@ -1465,7 +1455,26 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public void OnClientChange(NetState oldState, NetState newState) { - Utility.Replace(ref _clients, oldState, newState); + var count = _clients.Count; + + if (oldState != null) + { + _clients.Remove(oldState); + } + + if (newState != null) + { + _clients.AddLast(newState); + } + + if (_clients.Count == 0 && count > 0) + { + Owner.DeactivateSectors(X, Y); + } + else if (count == 0 && _clients.Count > 0) + { + Owner.ActivateSectors(X, Y); + } } public void OnEnter(Item item) @@ -1484,7 +1493,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa if (mob.NetState != null) { - Utility.Add(ref _clients, mob.NetState); + _clients.AddLast(mob.NetState); Owner.ActivateSectors(X, Y); } @@ -1496,7 +1505,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa if (mob.NetState != null) { - Utility.Remove(ref _clients, mob.NetState); + _clients.Remove(mob.NetState); Owner.DeactivateSectors(X, Y); } diff --git a/Projects/Server/Maps/PooledEnumeration.cs b/Projects/Server/Maps/PooledEnumeration.cs index a260d7dc4..cff148cc5 100644 --- a/Projects/Server/Maps/PooledEnumeration.cs +++ b/Projects/Server/Maps/PooledEnumeration.cs @@ -19,7 +19,6 @@ using System.Collections.Generic; using System.Linq; using Server.Collections; using Server.Items; -using Server.Network; namespace Server; @@ -33,34 +32,16 @@ public static class PooledEnumeration static PooledEnumeration() { - ClientSelector = SelectClients; EntitySelector = SelectEntities; MultiSelector = SelectMultis; MultiTileSelector = SelectMultiTiles; } - public static Selector ClientSelector { get; set; } public static Selector EntitySelector { get; set; } public static Selector MobileSelector { get; set; } public static Selector MultiSelector { get; set; } public static Selector MultiTileSelector { get; set; } - public static IEnumerable SelectClients(Map.Sector s, Rectangle2D bounds) - { - var clients = new List(s.Clients.Count); - foreach (var client in s.Clients) - { - var m = client.Mobile; - - if (m?.Deleted == false && bounds.Contains(m.Location)) - { - clients.Add(client); - } - } - - return clients; - } - public static IEnumerable SelectEntities(Map.Sector s, Rectangle2D bounds) { var entities = new List(s.Mobiles.Count + s.Items.Count); @@ -145,9 +126,6 @@ public static class PooledEnumeration } } - public static PooledEnumerable GetClients(Map map, Rectangle2D bounds) => - PooledEnumerable.Instantiate(map, bounds, ClientSelector ?? SelectClients); - public static PooledEnumerable GetEntities(Map map, Rectangle2D bounds) => PooledEnumerable.Instantiate(map, bounds, EntitySelector ?? SelectEntities); diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index cb9f11b3c..9626ace12 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -2864,8 +2864,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var eable = Map.GetClientsInRange(m_Location); - Span statBufferTrue = stackalloc byte[OutgoingMobilePackets.MobileStatusCompactLength].InitializePacket(); Span statBufferFalse = stackalloc byte[OutgoingMobilePackets.MobileStatusCompactLength].InitializePacket(); Span hbpBuffer = stackalloc byte[OutgoingMobilePackets.MobileHealthbarPacketLength].InitializePacket(); @@ -2874,7 +2872,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); Span hitsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributePacketLength].InitializePacket(); - foreach (var state in eable) + foreach (var state in Map.GetClientsInRange(m_Location)) { var beholder = state.Mobile; @@ -4808,13 +4806,12 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro if (m_Map != null) { - var eable = m_Map.GetClientsInRange(m_Location); var corpseSerial = c?.Serial ?? Serial.Zero; Span deathAnimation = stackalloc byte[OutgoingMobilePackets.DeathAnimationPacketLength].InitializePacket(); Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location)) { if (state != m_NetState) { @@ -5104,12 +5101,11 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro if (DragEffects && map != null && root is null or Item) { - var eable = map.GetClientsInRange(from.Location); var rootItem = root as Item; Span buffer = stackalloc byte[OutgoingPlayerPackets.DragEffectPacketLength].InitializePacket(); - foreach (var ns in eable) + foreach (var ns in map.GetClientsInRange(from.Location)) { if (ns.Mobile != from && ns.Mobile.CanSee(from) && ns.Mobile.InLOS(from) && ns.Mobile.CanSee(root)) @@ -5262,11 +5258,9 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var eable = map.GetClientsInRange(m_Location); - Span buffer = stackalloc byte[OutgoingPlayerPackets.DragEffectPacketLength].InitializePacket(); - foreach (var ns in eable) + foreach (var ns in map.GetClientsInRange(m_Location)) { if (ns.StygianAbyss || ns.Mobile == this || !ns.Mobile.CanSee(this) || !ns.Mobile.InLOS(this) || !ns.Mobile.CanSee(root)) @@ -6004,9 +5998,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var eable = map.GetClientsInRange(m_Location); - - foreach (var ns in eable) + foreach (var ns in map.GetClientsInRange(m_Location)) { if (ns.Mobile.CanSee(this)) { @@ -6718,11 +6710,9 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro ProcessDelta(); - var eable = map.GetClientsInRange(m_Location); - Span buffer = stackalloc byte[OutgoingMobilePackets.MobileAnimationPacketLength].InitializePacket(); - foreach (var state in eable) + foreach (var state in map.GetClientsInRange(m_Location)) { if (!state.Mobile.CanSee(this)) { @@ -6799,9 +6789,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro Span buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); - var eable = m_Map.GetClientsInRange(m_Location); - - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location)) { if (state.Mobile.CanSee(this)) { @@ -6849,11 +6837,9 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var eable = m_Map.GetClientsInRange(m_Location); - Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location)) { if (state != m_NetState && (everyone || !state.Mobile.CanSee(this))) { @@ -7080,11 +7066,9 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var eable = m_Map.GetClientsInRange(m_Location); - Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location)) { var m = state.Mobile; if (!m.CanSee(this)) @@ -7309,12 +7293,9 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro if (map != null) { // First, send a remove message to everyone who can no longer see us. (inOldRange && !inNewRange) - - var eable = map.GetClientsInRange(oldLocation); - Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); - foreach (var ns in eable) + foreach (var ns in map.GetClientsInRange(oldLocation)) { if (ns != m_NetState && !Utility.InUpdateRange(newLocation, ns.Mobile.Location)) { @@ -7395,10 +7376,8 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro } else { - eable = map.GetClientsInRange(newLocation); - // We're not attached to a client, so simply send an Incoming - foreach (var ns in eable) + foreach (var ns in map.GetClientsInRange(newLocation)) { var m = ns.Mobile; if ((isTeleport && (!ns.HighSeas || !NoMoveHS) || !Utility.InUpdateRange(oldLocation, m.Location)) && @@ -7478,9 +7457,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var eable = m_Map.GetClientsInRange(m_Location); - - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location)) { var m = state.Mobile; if (m.CanSee(this)) @@ -8140,8 +8117,11 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro m_Map == null ? Map.MobileBoundsEnumerable.Empty : m_Map.GetMobilesInRange(m_Location, range); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IPooledEnumerable GetClientsInRange(int range) => - m_Map?.GetClientsInRange(m_Location, range) ?? PooledEnumeration.NullEnumerable.Instance; + public Map.ClientAtEnumerable GetClientsAt() => m_Map == null ? Map.ClientAtEnumerable.Empty : Map.GetClientsAt(m_Location); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ClientBoundsEnumerable GetClientsInRange(int range) => + m_Map == null ? Map.ClientBoundsEnumerable.Empty : Map.GetClientsInRange(m_Location, range); public void SayTo(Mobile to, bool ascii, string text) => PrivateOverheadMessage(MessageType.Regular, SpeechHue, ascii, text, to.NetState); @@ -8960,9 +8940,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); - var eable = m_Map.GetClientsInRange(m_Location); - - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location)) { if ( state.Mobile.AccessLevel >= accessLevel && @@ -8993,9 +8971,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedLength(args)].InitializePacket(); - var eable = m_Map.GetClientsInRange(m_Location); - - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location)) { if (state.Mobile.CanSee(this) && (noLineOfSight || state.Mobile.InLOS(this))) { @@ -9027,9 +9003,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedAffixLength(affix, args)] .InitializePacket(); - var eable = m_Map.GetClientsInRange(m_Location); - - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location)) { if ( state.Mobile.AccessLevel >= accessLevel && @@ -9077,9 +9051,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedLength(args)].InitializePacket(); - var eable = m_Map.GetClientsInRange(m_Location); - - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location)) { if (state != m_NetState && state.Mobile.CanSee(this)) { @@ -9106,9 +9078,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); - var eable = m_Map.GetClientsInRange(m_Location); - - foreach (var state in eable) + foreach (var state in m_Map.GetClientsInRange(m_Location)) { if (state != m_NetState && state.Mobile.CanSee(this)) { diff --git a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs index 483136a1c..d73e730c3 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs @@ -140,8 +140,7 @@ namespace Server.Engines.ConPVP return; } - var eable = GetClientsInRange(0); - foreach (var ns in eable) + foreach (var ns in GetClientsAt()) { var m = ns.Mobile; @@ -585,8 +584,7 @@ namespace Server.Engines.ConPVP return; } - var clients = Map.GetClientsInBounds(rect); - foreach (var ns in clients) + foreach (var ns in Map.GetClientsInBounds(rect)) { var m = ns.Mobile; diff --git a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs index dc849800e..e2a49373b 100644 --- a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs +++ b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs @@ -505,9 +505,7 @@ public partial class LeverPuzzleController : Item { Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(Msgs[index])].InitializePacket(); - var eable = from.Map.GetClientsInRange(from.Location); - - foreach (var state in eable) + foreach (var state in from.Map.GetClientsInRange(from.Location)) { var length = OutgoingMessagePackets.CreateMessage( buffer, diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs index de948dbc0..0d116dd2a 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs @@ -370,11 +370,9 @@ public partial class CharacterStatue : Mobile, IRewardItem } ProcessDelta(); - - var eable = Map.GetClientsInRange(Location); Span animPacket = stackalloc byte[CharacterStatuePackets.StatueAnimationPacketLength].InitializePacket(); - foreach (var state in eable) + foreach (var state in Map.GetClientsInRange(Location)) { state.Mobile.ProcessDelta(); CharacterStatuePackets.CreateStatueAnimation(animPacket, Serial, 1, m_Animation, m_Frames); diff --git a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs index 1b8bdd2d5..7aa45e4c0 100644 --- a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs +++ b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs @@ -129,9 +129,7 @@ public partial class Campfire : Item } } - var eable = GetClientsInRange(SecureRange); - - foreach (var state in eable) + foreach (var state in GetClientsInRange(SecureRange)) { if (state.Mobile is PlayerMobile pm && GetEntry(pm) == null) { diff --git a/Projects/UOContent/Skills/Snooping.cs b/Projects/UOContent/Skills/Snooping.cs index 4799f6492..ddf2323b4 100644 --- a/Projects/UOContent/Skills/Snooping.cs +++ b/Projects/UOContent/Skills/Snooping.cs @@ -68,9 +68,7 @@ public static class Snooping { var message = $"You notice {from.Name} attempting to peek into {root.Name}'s belongings."; - var eable = map.GetClientsInRange(from.Location, 8); - - foreach (var ns in eable) + foreach (var ns in map.GetClientsInRange(from.Location, 8)) { if (ns.Mobile != from) {