fix: Changes Map.Sector.Items to link list. (#1547)

### Summary

Eliminates `IPooledEnumerable<T>` and `eable.Free()` from `Map` for items. This drastically simplifies code that iterates in range, for example:

```cs
foreach (var item in m.GetItemsInRange(5))
{
}
```
The code above no longer requires an eable and calling `Free()`.
This commit is contained in:
Kamron Batman 2023-10-16 20:51:02 -07:00 • committed by GitHub
parent 1330c4a830
commit 24858f3989
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 577 additions and 419 deletions

View file

@ -22,6 +22,8 @@ namespace Server;
[PropertyObject] [PropertyObject]
public struct Rectangle2D : IEquatable<Rectangle2D>, ISpanFormattable, ISpanParsable<Rectangle2D> public struct Rectangle2D : IEquatable<Rectangle2D>, ISpanFormattable, ISpanParsable<Rectangle2D>
{ {
public static Rectangle2D Empty => new();
private Point2D _start; private Point2D _start;
private Point2D _end; private Point2D _end;

View file

@ -2473,13 +2473,13 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
: map.GetObjectsInRange(m_Parent == null ? m_Location : GetWorldLocation(), range); : map.GetObjectsInRange(m_Parent == null ? m_Location : GetWorldLocation(), range);
} }
public IPooledEnumerable<Item> GetItemsInRange(int range) [MethodImpl(MethodImplOptions.AggressiveInlining)]
{ public Map.ItemEnumerable<Item> GetItemsInRange(int range) =>
var map = m_Map; m_Map == null ? Map.ItemEnumerable<Item>.Empty : m_Map.GetItemsInRange(m_Parent == null ? m_Location : GetWorldLocation(), range);
return map?.GetItemsInRange(m_Parent == null ? m_Location : GetWorldLocation(), range) [MethodImpl(MethodImplOptions.AggressiveInlining)]
?? PooledEnumeration.NullEnumerable<Item>.Instance; public Map.ItemEnumerable<T> GetItemsInRange<T>(int range) where T : Item =>
} m_Map == null ? Map.ItemEnumerable<T>.Empty : m_Map.GetItemsInRange<T>(m_Parent == null ? m_Location : GetWorldLocation(), range);
public IPooledEnumerable<Mobile> GetMobilesInRange(int range) public IPooledEnumerable<Mobile> GetMobilesInRange(int range)
{ {
@ -3547,10 +3547,8 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
z = top; z = top;
} }
var eable = map.GetItemsInRange(p, 0); using var items = PooledRefList<Item>.Create();
foreach (var item in map.GetItemsInRange(p, 0))
var items = new List<Item>();
foreach (var item in eable)
{ {
if (item is BaseMulti || item.ItemID > TileData.MaxItemValue) if (item is BaseMulti || item.ItemID > TileData.MaxItemValue)
{ {
@ -3611,7 +3609,7 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
m_OpenSlots &= ~(((1 << bitCount) - 1) << zStart); m_OpenSlots &= ~(((1 << bitCount) - 1) << zStart);
} }
for (var i = 0; i < items.Count; ++i) for (var i = 0; i < items.Count; i++)
{ {
var item = items[i]; var item = items[i];
var id = item.ItemData; var id = item.ItemData;

View file

@ -0,0 +1,222 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Map.Enumerators.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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Server;
public partial class Map
{
private int _iteratingItems;
private List<(MapAction, Point3D, Item)> _delayedItemActions = new();
public bool IsIteratingItems
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _iteratingItems > 0;
}
public ItemEnumerable<Item> GetItemsInRange(Point3D p) => GetItemsInRange(p, Core.GlobalMaxUpdateRange);
public ItemEnumerable<Item> GetItemsInRange(Point3D p, int range) => GetItemsInRange<Item>(p, range);
public ItemEnumerable<T> GetItemsInRange<T>(Point3D p, int range) where T : Item =>
GetItemsInBounds<T>(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1));
public ItemEnumerable<Item> GetItemsInBounds(Rectangle2D bounds) => GetItemsInBounds<Item>(bounds);
public ItemEnumerable<T> GetItemsInBounds<T>(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : Item =>
new(this, bounds, makeBoundsInclusive);
private void BeginIteratingItems()
{
#if THREADGUARD
if (Thread.CurrentThread != Core.Thread)
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine($"Iterating through items on {this} from an invalid thread!");
Console.WriteLine(new StackTrace());
Utility.PopColor();
return;
}
#endif
_iteratingItems++;
}
private void EndIteratingItems()
{
#if THREADGUARD
if (Thread.CurrentThread != Core.Thread)
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine($"Iterating through items on {this} from an invalid thread!");
Console.WriteLine(new StackTrace());
Utility.PopColor();
return;
}
#endif
_iteratingItems--;
// Finished iterating, check for deferred actions
if (_iteratingItems == 0 && _delayedItemActions.Count > 0)
{
foreach (var (a, p, i) in _delayedItemActions)
{
switch (a)
{
case MapAction.Enter:
{
OnEnter(p, i);
break;
}
case MapAction.Leave:
{
OnLeave(p, i);
break;
}
case MapAction.Move:
{
OnMove(p, i);
break;
}
}
}
_delayedItemActions.Clear();
}
}
public ref struct ItemEnumerable<T> where T : Item
{
public static ItemEnumerable<T> Empty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(null, Rectangle2D.Empty, false);
}
private Map _map;
private Rectangle2D _bounds;
private bool _makeBoundsInclusive;
public ItemEnumerable(Map map, Rectangle2D bounds, bool makeBoundsInclusive)
{
_map = map;
_bounds = bounds;
_makeBoundsInclusive = makeBoundsInclusive;
}
// The enumerator MUST be disposed. Not disposing it will damage the sector irreparably.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ItemEnumerator<T> GetEnumerator() => new(_map, _bounds, _makeBoundsInclusive);
}
public ref struct ItemEnumerator<T> where T : Item
{
private Map _map;
private int _sectorStartX;
private int _sectorEndX;
private int _sectorEndY;
private Rectangle2D _bounds;
private int _currentSectorX;
private int _currentSectorY;
private T _current;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ItemEnumerator(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;
_map.BeginIteratingItems();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
var map = _map;
if (map == null)
{
return false;
}
Item 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;
}
current = map.GetRealSector(currentSectorX, currentSectorY).Items.First;
}
if (current is T { Deleted: false, Parent: null } o && bounds.Contains(o.Location))
{
_current = o;
return true;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() => _map.EndIteratingItems();
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
}

View file

@ -38,7 +38,7 @@ public enum MapRules
FeluccaRules = None FeluccaRules = None
} }
public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map> public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
{ {
public const int SectorSize = 16; public const int SectorSize = 16;
public const int SectorShift = 4; public const int SectorShift = 4;
@ -297,9 +297,6 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
return v / 2; return v / 2;
} }
public IPooledEnumerable<StaticTile[]> GetMultiTilesAt(int x, int y) =>
PooledEnumeration.GetMultiTiles(this, new Rectangle2D(x, y, 1, 1));
private static void AcquireFixItems(Map map, int x, int y, Item[] pool, out int length) private static void AcquireFixItems(Map map, int x, int y, Item[] pool, out int length)
{ {
length = 0; length = 0;
@ -308,8 +305,8 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
return; return;
} }
var eable = map.GetItemsInRange(new Point3D(x, y, 0), 0); var p = new Point3D(x, y, 0);
foreach (var item in eable) foreach (var item in map.GetItemsInRange(p, 0))
{ {
if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue) if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue)
{ {
@ -489,28 +486,28 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
var sector = GetSector(p.X, p.Y); var sector = GetSector(p.X, p.Y);
for (var i = 0; i < sector.Items.Count; i++) foreach (var item in sector.Items)
{ {
var item = sector.Items[i]; if (item is BaseMulti || item.ItemID > TileData.MaxItemValue || !item.AtWorldPoint(p.X, p.Y) ||
item.Movable)
if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(p.X, p.Y) &&
!item.Movable)
{ {
var id = item.ItemData; continue;
}
if (id.Surface || id.Wet) var id = item.ItemData;
if (id.Surface || id.Wet)
{
var itemZ = item.Z + id.CalcHeight;
if (itemZ > surfaceZ && itemZ <= p.Z)
{ {
var itemZ = item.Z + id.CalcHeight; surface = item;
surfaceZ = itemZ;
if (itemZ > surfaceZ && itemZ <= p.Z) if (surfaceZ == p.Z)
{ {
surface = item; return surface;
surfaceZ = itemZ;
if (surfaceZ == p.Z)
{
return surface;
}
} }
} }
} }
@ -538,6 +535,29 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
return new Point2D(x, y); return new Point2D(x, y);
} }
private void CalculateSectors(
Rectangle2D bounds,
out int sectorStartX, out int sectorStartY,
out int sectorEndX, out int sectorEndY)
{
int left = bounds.Start.X;
int top = bounds.Start.Y;
int right = bounds.End.X;
int bottom = bounds.End.Y;
// Limit the coordinates to inside the valid map region
Bound(left, top, out left, out top);
Bound(right - 1, bottom - 1, out right, out bottom);
// Calculate the top left sector
sectorStartX = left >> SectorShift;
sectorStartY = top >> SectorShift;
// Calculate the bottom right sector.
sectorEndX = right >> SectorShift;
sectorEndY = bottom >> SectorShift;
}
public void ActivateSectors(int cx, int cy) public void ActivateSectors(int cx, int cy)
{ {
for (var x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x) for (var x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x)
@ -593,22 +613,36 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
} }
} }
public void OnEnter(Mobile m) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnEnter(Mobile m) => OnEnter(m.Location, m);
public void OnEnter(Point3D p, Mobile m)
{ {
if (this != Internal) if (this != Internal)
{ {
GetSector(m.Location).OnEnter(m); GetSector(p).OnEnter(m);
} }
} }
public void OnEnter(Item item) public void OnEnter(Item item)
{
if (IsIteratingItems)
{
_delayedItemActions.Add((MapAction.Enter, item.Location, item));
return;
}
OnEnter(item.Location, item);
}
public void OnEnter(Point3D p, Item item)
{ {
if (this == Internal) if (this == Internal)
{ {
return; return;
} }
GetSector(item.Location).OnEnter(item); GetSector(p).OnEnter(item);
if (item is BaseMulti m) if (item is BaseMulti m)
{ {
@ -621,22 +655,36 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
} }
} }
public void OnLeave(Mobile m) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnLeave(Mobile m) => OnLeave(m.Location, m);
public void OnLeave(Point3D p, Mobile m)
{ {
if (this != Internal) if (this != Internal)
{ {
GetSector(m.Location).OnLeave(m); GetSector(p).OnLeave(m);
} }
} }
public void OnLeave(Item item) public void OnLeave(Item item)
{
if (IsIteratingItems)
{
_delayedItemActions.Add((MapAction.Leave, item.Location, item));
return;
}
OnLeave(item.Location, item);
}
public void OnLeave(Point3D p, Item item)
{ {
if (this == Internal) if (this == Internal)
{ {
return; return;
} }
GetSector(item.Location).OnLeave(item); GetSector(p).OnLeave(item);
if (item is BaseMulti m) if (item is BaseMulti m)
{ {
@ -716,6 +764,12 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
if (oldSector != newSector) if (oldSector != newSector)
{ {
if (IsIteratingItems)
{
_delayedItemActions.Add((MapAction.Move, item.Location, item));
return;
}
oldSector.OnLeave(item); oldSector.OnLeave(item);
newSector.OnEnter(item); newSector.OnEnter(item);
} }
@ -732,6 +786,12 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
if (oldStart != start || oldEnd != end) if (oldStart != start || oldEnd != end)
{ {
if (IsIteratingItems)
{
_delayedItemActions.Add((MapAction.Enter, oldLocation, item));
return;
}
RemoveMulti(m, oldStart, oldEnd); RemoveMulti(m, oldStart, oldEnd);
AddMulti(m, start, end); AddMulti(m, start, end);
} }
@ -821,6 +881,9 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
return p; return p;
} }
public IPooledEnumerable<StaticTile[]> GetMultiTilesAt(int x, int y) =>
PooledEnumeration.GetMultiTiles(this, new Rectangle2D(x, y, 1, 1));
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p) => GetObjectsInRange(p, Core.GlobalMaxUpdateRange); public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p) => GetObjectsInRange(p, Core.GlobalMaxUpdateRange);
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p, int range) => public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p, int range) =>
@ -837,18 +900,6 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
public IPooledEnumerable<NetState> GetClientsInBounds(Rectangle2D bounds) => public IPooledEnumerable<NetState> GetClientsInBounds(Rectangle2D bounds) =>
PooledEnumeration.GetClients(this, bounds); PooledEnumeration.GetClients(this, bounds);
public IPooledEnumerable<Item> GetItemsInRange(Point3D p) => GetItemsInRange(p, Core.GlobalMaxUpdateRange);
public IPooledEnumerable<Item> GetItemsInRange(Point3D p, int range) => GetItemsInRange<Item>(p, range);
public IPooledEnumerable<T> GetItemsInRange<T>(Point3D p, int range) where T : Item =>
GetItemsInBounds<T>(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1));
public IPooledEnumerable<Item> GetItemsInBounds(Rectangle2D bounds) => GetItemsInBounds<Item>(bounds);
public IPooledEnumerable<T> GetItemsInBounds<T>(Rectangle2D bounds) where T : Item =>
PooledEnumeration.GetItems<T>(this, bounds);
public IPooledEnumerable<Mobile> GetMobilesInRange(Point3D p) => GetMobilesInRange(p, Core.GlobalMaxUpdateRange); public IPooledEnumerable<Mobile> GetMobilesInRange(Point3D p) => GetMobilesInRange(p, Core.GlobalMaxUpdateRange);
public IPooledEnumerable<Mobile> GetMobilesInRange(Point3D p, int range) => GetMobilesInRange<Mobile>(p, range); public IPooledEnumerable<Mobile> GetMobilesInRange(Point3D p, int range) => GetMobilesInRange<Mobile>(p, range);
@ -926,29 +977,28 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
} }
var sector = GetSector(x, y); var sector = GetSector(x, y);
var items = sector.Items;
var mobs = sector.Mobiles; var mobs = sector.Mobiles;
for (var i = 0; i < items.Count; ++i) foreach (var item in sector.Items)
{ {
var item = items[i]; if (item is BaseMulti || item.ItemID > TileData.MaxItemValue || !item.AtWorldPoint(x, y))
if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(x, y))
{ {
var id = item.ItemData; continue;
surface = id.Surface; }
impassable = id.Impassable;
if ((surface || impassable || checkBlocksFit && item.BlocksFit) && item.Z + id.CalcHeight > z && var id = item.ItemData;
z + height > item.Z) surface = id.Surface;
{ impassable = id.Impassable;
return false;
}
if (surface && !impassable && !item.Movable && z == item.Z + id.CalcHeight) if ((surface || impassable || checkBlocksFit && item.BlocksFit) && item.Z + id.CalcHeight > z &&
{ z + height > item.Z)
hasSurface = true; {
} return false;
}
if (surface && !impassable && !item.Movable && z == item.Z + id.CalcHeight)
{
hasSurface = true;
} }
} }
@ -1140,9 +1190,7 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
if (contains && statics.Length == 0) if (contains && statics.Length == 0)
{ {
var eable = GetItemsInRange(point, 0); foreach (Item item in GetItemsInRange(point, 0))
foreach (Item item in eable)
{ {
if (item.Visible) if (item.Visible)
{ {
@ -1180,9 +1228,7 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
var rect = new Rectangle2D(pTop.m_X, pTop.m_Y, pBottom.m_X - pTop.m_X + 1, pBottom.m_Y - pTop.m_Y + 1); var rect = new Rectangle2D(pTop.m_X, pTop.m_Y, pBottom.m_X - pTop.m_X + 1, pBottom.m_Y - pTop.m_Y + 1);
var area = GetItemsInBounds(rect); foreach (var i in GetItemsInBounds(rect))
foreach (var i in area)
{ {
if (!i.Visible) if (!i.Visible)
{ {
@ -1412,39 +1458,27 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
return false; return false;
} }
public class RegionRect : IComparable<RegionRect> private enum MapAction
{ {
private Rectangle3D m_Rect; None,
Enter,
public RegionRect(Region region, Rectangle3D rect) Leave,
{ Move
Region = region;
m_Rect = rect;
}
public Region Region { get; }
public Rectangle3D Rect => m_Rect;
public int CompareTo(RegionRect regRect) => regRect == null ? 1 : Region.CompareTo(regRect.Region);
public bool Contains(Point3D loc) => m_Rect.Contains(loc);
} }
public class Sector public class Sector
{ {
// TODO: Can we avoid this? // TODO: Can we avoid this?
private static readonly List<Mobile> m_DefaultMobileList = new(); private static readonly List<Mobile> m_DefaultMobileList = new();
private static readonly List<Item> m_DefaultItemList = new();
private static readonly List<NetState> m_DefaultClientList = new(); private static readonly List<NetState> m_DefaultClientList = new();
private static readonly List<BaseMulti> m_DefaultMultiList = new(); private static readonly List<BaseMulti> m_DefaultMultiList = new();
private static readonly List<RegionRect> m_DefaultRectList = new(); private static readonly List<Region> m_DefaultRectList = new();
private bool m_Active; private bool m_Active;
private List<NetState> m_Clients; private List<NetState> _clients;
private List<Item> m_Items; private ValueLinkList<Item> _items;
private List<Mobile> m_Mobiles; private List<Mobile> _mobiles;
private List<BaseMulti> m_Multis; private List<BaseMulti> _multis;
private List<RegionRect> m_RegionRects; private List<Region> _regions;
public Sector(int x, int y, Map owner) public Sector(int x, int y, Map owner)
{ {
@ -1454,17 +1488,17 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
m_Active = false; m_Active = false;
} }
public List<RegionRect> RegionRects => m_RegionRects ?? m_DefaultRectList; public List<Region> Regions => _regions ?? m_DefaultRectList;
public List<BaseMulti> Multis => m_Multis ?? m_DefaultMultiList; public List<BaseMulti> Multis => _multis ?? m_DefaultMultiList;
public List<Mobile> Mobiles => m_Mobiles ?? m_DefaultMobileList; public List<Mobile> Mobiles => _mobiles ?? m_DefaultMobileList;
public List<Item> Items => m_Items ?? m_DefaultItemList; public ref ValueLinkList<Item> Items => ref _items;
public List<NetState> Clients => m_Clients ?? m_DefaultClientList; public List<NetState> Clients => _clients ?? m_DefaultClientList;
public bool Active => m_Active && Owner != Map.Internal; public bool Active => m_Active && Owner != Internal;
public Map Owner { get; } public Map Owner { get; }
@ -1474,26 +1508,26 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
public void OnClientChange(NetState oldState, NetState newState) public void OnClientChange(NetState oldState, NetState newState)
{ {
Utility.Replace(ref m_Clients, oldState, newState); Utility.Replace(ref _clients, oldState, newState);
} }
public void OnEnter(Item item) public void OnEnter(Item item)
{ {
Utility.Add(ref m_Items, item); _items.AddLast(item);
} }
public void OnLeave(Item item) public void OnLeave(Item item)
{ {
Utility.Remove(ref m_Items, item); _items.Remove(item);
} }
public void OnEnter(Mobile mob) public void OnEnter(Mobile mob)
{ {
Utility.Add(ref m_Mobiles, mob); Utility.Add(ref _mobiles, mob);
if (mob.NetState != null) if (mob.NetState != null)
{ {
Utility.Add(ref m_Clients, mob.NetState); Utility.Add(ref _clients, mob.NetState);
Owner.ActivateSectors(X, Y); Owner.ActivateSectors(X, Y);
} }
@ -1501,11 +1535,11 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
public void OnLeave(Mobile mob) public void OnLeave(Mobile mob)
{ {
Utility.Remove(ref m_Mobiles, mob); Utility.Remove(ref _mobiles, mob);
if (mob.NetState != null) if (mob.NetState != null)
{ {
Utility.Remove(ref m_Clients, mob.NetState); Utility.Remove(ref _clients, mob.NetState);
Owner.DeactivateSectors(X, Y); Owner.DeactivateSectors(X, Y);
} }
@ -1513,31 +1547,31 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
public void OnEnter(Region region, Rectangle3D rect) public void OnEnter(Region region, Rectangle3D rect)
{ {
Utility.Add(ref m_RegionRects, new RegionRect(region, rect)); Utility.Add(ref _regions, region);
m_RegionRects.Sort(); _regions.Sort();
UpdateMobileRegions(); UpdateMobileRegions();
} }
public void OnLeave(Region region) public void OnLeave(Region region)
{ {
if (m_RegionRects != null) if (_regions != null)
{ {
for (var i = m_RegionRects.Count - 1; i >= 0; i--) for (var i = _regions.Count - 1; i >= 0; i--)
{ {
var regRect = m_RegionRects[i]; var r = _regions[i];
if (regRect.Region == region) if (r == region)
{ {
m_RegionRects.RemoveAt(i); _regions.RemoveAt(i);
break; break;
} }
} }
if (m_RegionRects.Count == 0) if (_regions.Count == 0)
{ {
m_RegionRects = null; _regions = null;
} }
} }
@ -1546,10 +1580,10 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
private void UpdateMobileRegions() private void UpdateMobileRegions()
{ {
if (m_Mobiles != null) if (_mobiles != null)
{ {
using var queue = PooledRefQueue<Mobile>.Create(m_Mobiles.Count); using var queue = PooledRefQueue<Mobile>.Create(_mobiles.Count);
foreach (var mob in m_Mobiles) foreach (var mob in _mobiles)
{ {
queue.Enqueue(mob); queue.Enqueue(mob);
} }
@ -1563,29 +1597,26 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
public void OnMultiEnter(BaseMulti multi) public void OnMultiEnter(BaseMulti multi)
{ {
Utility.Add(ref m_Multis, multi); Utility.Add(ref _multis, multi);
} }
public void OnMultiLeave(BaseMulti multi) public void OnMultiLeave(BaseMulti multi)
{ {
Utility.Remove(ref m_Multis, multi); Utility.Remove(ref _multis, multi);
} }
public void Activate() public void Activate()
{ {
if (!Active) if (!Active)
{ {
if (m_Items != null) foreach (var item in _items)
{ {
foreach (var item in m_Items) item.OnSectorActivate();
{
item.OnSectorActivate();
}
} }
if (m_Mobiles != null) if (_mobiles != null)
{ {
foreach (var mob in m_Mobiles) foreach (var mob in _mobiles)
{ {
mob.OnSectorActivate(); mob.OnSectorActivate();
} }
@ -1599,17 +1630,14 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
{ {
if (Active) if (Active)
{ {
if (m_Items != null) foreach (var item in _items)
{ {
foreach (var item in m_Items) item.OnSectorDeactivate();
{
item.OnSectorDeactivate();
}
} }
if (m_Mobiles != null) if (_mobiles != null)
{ {
foreach (var mob in m_Mobiles) foreach (var mob in _mobiles)
{ {
mob.OnSectorDeactivate(); mob.OnSectorDeactivate();
} }

View file

@ -35,7 +35,6 @@ public static class PooledEnumeration
ClientSelector = SelectClients; ClientSelector = SelectClients;
EntitySelector = SelectEntities; EntitySelector = SelectEntities;
MobileSelector = SelectMobiles<Mobile>; MobileSelector = SelectMobiles<Mobile>;
ItemSelector = SelectItems<Item>;
MultiSelector = SelectMultis; MultiSelector = SelectMultis;
MultiTileSelector = SelectMultiTiles; MultiTileSelector = SelectMultiTiles;
} }
@ -43,7 +42,6 @@ public static class PooledEnumeration
public static Selector<NetState> ClientSelector { get; set; } public static Selector<NetState> ClientSelector { get; set; }
public static Selector<IEntity> EntitySelector { get; set; } public static Selector<IEntity> EntitySelector { get; set; }
public static Selector<Mobile> MobileSelector { get; set; } public static Selector<Mobile> MobileSelector { get; set; }
public static Selector<Item> ItemSelector { get; set; }
public static Selector<BaseMulti> MultiSelector { get; set; } public static Selector<BaseMulti> MultiSelector { get; set; }
public static Selector<StaticTile[]> MultiTileSelector { get; set; } public static Selector<StaticTile[]> MultiTileSelector { get; set; }
@ -66,26 +64,20 @@ public static class PooledEnumeration
public static IEnumerable<IEntity> SelectEntities(Map.Sector s, Rectangle2D bounds) public static IEnumerable<IEntity> SelectEntities(Map.Sector s, Rectangle2D bounds)
{ {
var entities = new List<IEntity>(s.Mobiles.Count + s.Items.Count); var entities = new List<IEntity>(s.Mobiles.Count + s.Items.Count);
for (int i = s.Mobiles.Count - 1, j = s.Items.Count - 1; i >= 0 || j >= 0; --i, --j) for (int i = s.Mobiles.Count - 1; i >= 0; --i)
{ {
if (j >= 0) Mobile mob = s.Mobiles[i];
if (mob is { Deleted: false } && bounds.Contains(mob.Location))
{ {
Item item = s.Items[j]; entities.Add(mob);
if (item is { Deleted: false, Parent: null } && bounds.Contains(item.Location))
{
entities.Add(item);
}
}
if (i >= 0)
{
Mobile mob = s.Mobiles[i];
if (mob is { Deleted: false } && bounds.Contains(mob.Location))
{
entities.Add(mob);
}
} }
} }
foreach (var item in s.Items)
{
entities.Add(item);
}
return entities; return entities;
} }
@ -102,19 +94,6 @@ public static class PooledEnumeration
return entities; return entities;
} }
public static IEnumerable<T> SelectItems<T>(Map.Sector s, Rectangle2D bounds) where T : Item
{
var entities = new List<T>(s.Items.Count);
for (int i = s.Items.Count - 1; i >= 0; --i)
{
if (s.Items[i] is T { Deleted: false, Parent: null } item && bounds.Contains(item.Location))
{
entities.Add(item);
}
}
return entities;
}
public static IEnumerable<BaseMulti> SelectMultis(Map.Sector s, Rectangle2D bounds) public static IEnumerable<BaseMulti> SelectMultis(Map.Sector s, Rectangle2D bounds)
{ {
var entities = new List<BaseMulti>(s.Multis.Count); var entities = new List<BaseMulti>(s.Multis.Count);
@ -195,9 +174,6 @@ public static class PooledEnumeration
public static PooledEnumerable<T> GetMobiles<T>(Map map, Rectangle2D bounds) where T : Mobile => public static PooledEnumerable<T> GetMobiles<T>(Map map, Rectangle2D bounds) where T : Mobile =>
PooledEnumerable<T>.Instantiate(map, bounds, SelectMobiles<T>); PooledEnumerable<T>.Instantiate(map, bounds, SelectMobiles<T>);
public static PooledEnumerable<T> GetItems<T>(Map map, Rectangle2D bounds) where T : Item =>
PooledEnumerable<T>.Instantiate(map, bounds, SelectItems<T>);
public static PooledEnumerable<BaseMulti> GetMultis(Map map, Rectangle2D bounds) => public static PooledEnumerable<BaseMulti> GetMultis(Map map, Rectangle2D bounds) =>
PooledEnumerable<BaseMulti>.Instantiate(map, bounds, MultiSelector ?? SelectMultis); PooledEnumerable<BaseMulti>.Instantiate(map, bounds, MultiSelector ?? SelectMultis);
@ -363,7 +339,7 @@ public static class PooledEnumeration
#pragma warning disable CA1000 // Do not declare static members on generic types #pragma warning disable CA1000 // Do not declare static members on generic types
public static PooledEnumerable<T> Instantiate( public static PooledEnumerable<T> Instantiate(
Map map, Rectangle2D bounds, PooledEnumeration.Selector<T> selector Map map, Rectangle2D bounds, Selector<T> selector
) )
{ {
PooledEnumerable<T> e = null; PooledEnumerable<T> e = null;
@ -376,7 +352,7 @@ public static class PooledEnumeration
} }
} }
var pool = PooledEnumeration.EnumerateSectors(map, bounds).SelectMany(s => selector(s, bounds)); var pool = EnumerateSectors(map, bounds).SelectMany(s => selector(s, bounds));
if (e == null) if (e == null)
{ {

View file

@ -4165,10 +4165,8 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
} }
} }
for (var i = 0; i < oldSector.Items.Count; ++i) foreach (var item in oldSector.Items)
{ {
var item = oldSector.Items[i];
if (item.AtWorldPoint(oldX, oldY) && if (item.AtWorldPoint(oldX, oldY) &&
(item.Z == oldZ || item.Z + item.ItemData.Height > oldZ && oldZ + 15 > item.Z) && (item.Z == oldZ || item.Z + item.ItemData.Height > oldZ && oldZ + 15 > item.Z) &&
!item.OnMoveOff(this)) !item.OnMoveOff(this))
@ -4187,10 +4185,8 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
} }
} }
for (var i = 0; i < newSector.Items.Count; ++i) foreach (var item in newSector.Items)
{ {
var item = newSector.Items[i];
if (item.AtWorldPoint(x, y) && if (item.AtWorldPoint(x, y) &&
(item.Z == newZ || item.Z + item.ItemData.Height > newZ && newZ + 15 > item.Z) && (item.Z == newZ || item.Z + item.ItemData.Height > newZ && newZ + 15 > item.Z) &&
!item.OnMoveOver(this)) !item.OnMoveOver(this))
@ -4217,10 +4213,8 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
} }
} }
for (var i = 0; i < oldSector.Items.Count; ++i) foreach (var item in oldSector.Items)
{ {
var item = oldSector.Items[i];
if (item.AtWorldPoint(oldX, oldY) && if (item.AtWorldPoint(oldX, oldY) &&
(item.Z == oldZ || item.Z + item.ItemData.Height > oldZ && oldZ + 15 > item.Z) && (item.Z == oldZ || item.Z + item.ItemData.Height > oldZ && oldZ + 15 > item.Z) &&
!item.OnMoveOff(this)) !item.OnMoveOff(this))
@ -8064,10 +8058,12 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
return -1; return -1;
} }
public IPooledEnumerable<Item> GetItemsInRange(int range) => GetItemsInRange<Item>(range); [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.ItemEnumerable<Item> GetItemsInRange(int range) => GetItemsInRange<Item>(range);
public IPooledEnumerable<T> GetItemsInRange<T>(int range) where T : Item => [MethodImpl(MethodImplOptions.AggressiveInlining)]
m_Map?.GetItemsInRange<T>(m_Location, range) ?? PooledEnumeration.NullEnumerable<T>.Instance; public Map.ItemEnumerable<T> GetItemsInRange<T>(int range) where T : Item =>
m_Map == null ? Map.ItemEnumerable<T>.Empty : m_Map.GetItemsInRange<T>(m_Location, range);
public IPooledEnumerable<IEntity> GetObjectsInRange(int range) => public IPooledEnumerable<IEntity> GetObjectsInRange(int range) =>
m_Map?.GetObjectsInRange(m_Location, range) ?? PooledEnumeration.NullEnumerable<IEntity>.Instance; m_Map?.GetObjectsInRange(m_Location, range) ?? PooledEnumeration.NullEnumerable<IEntity>.Instance;

View file

@ -295,15 +295,15 @@ public class Region : IComparable<Region>, IValueLinkListNode<Region>
} }
var sector = map.GetSector(p); var sector = map.GetSector(p);
var list = sector.RegionRects; var list = sector.Regions;
for (var i = 0; i < list.Count; ++i) for (var i = 0; i < list.Count; ++i)
{ {
var regRect = list[i]; var region = list[i];
if (regRect.Contains(p)) if (region.Contains(p))
{ {
return regRect.Region; return region;
} }
} }

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Server; using Server;

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Microsoft.Toolkit.HighPerformance; using Microsoft.Toolkit.HighPerformance;
using Server.Collections;
using Server.Engines.Quests.Haven; using Server.Engines.Quests.Haven;
using Server.Engines.Quests.Necro; using Server.Engines.Quests.Necro;
using Server.Engines.Spawners; using Server.Engines.Spawners;
@ -78,8 +79,6 @@ namespace Server.Commands
private static readonly Type typeofCannon = typeof(Cannon); private static readonly Type typeofCannon = typeof(Cannon);
private static readonly Type typeofSerpentPillar = typeof(SerpentPillar); private static readonly Type typeofSerpentPillar = typeof(SerpentPillar);
private static readonly Queue<Item> m_DeleteQueue = new();
private static readonly string[] m_EmptyParams = Array.Empty<string>(); private static readonly string[] m_EmptyParams = Array.Empty<string>();
private List<DecorationEntry> m_Entries; private List<DecorationEntry> m_Entries;
private int m_ItemID; private int m_ItemID;
@ -1037,16 +1036,17 @@ namespace Server.Commands
private static bool FindItem(int x, int y, int z, Map map, Item srcItem) private static bool FindItem(int x, int y, int z, Map map, Item srcItem)
{ {
var itemID = srcItem.ItemID; var itemID = srcItem.ItemID;
var lt = srcItem.Light;
var srcName = srcItem.ItemData.Name;
var type = srcItem.GetType();
var res = false; var res = false;
IPooledEnumerable<Item> eable; using var queue = PooledRefQueue<Item>.Create();
if (srcItem is BaseDoor) foreach (var item in map.GetItemsInRange(new Point3D(x, y, z), 1))
{ {
eable = map.GetItemsInRange(new Point3D(x, y, z), 1); if (srcItem is BaseDoor)
foreach (var item in eable)
{ {
if (!(item is BaseDoor)) if (!(item is BaseDoor))
{ {
@ -1079,18 +1079,10 @@ namespace Server.Commands
} }
else if ((item.Z - z).Abs() < 8) else if ((item.Z - z).Abs() < 8)
{ {
m_DeleteQueue.Enqueue(item); queue.Enqueue(item);
} }
} }
} else if (TileData.ItemTable[itemID & TileData.MaxItemValue].LightSource)
else if (TileData.ItemTable[itemID & TileData.MaxItemValue].LightSource)
{
eable = map.GetItemsInRange(new Point3D(x, y, z), 0);
var lt = srcItem.Light;
var srcName = srcItem.ItemData.Name;
foreach (var item in eable)
{ {
if (item.Z == z) if (item.Z == z)
{ {
@ -1098,7 +1090,7 @@ namespace Server.Commands
{ {
if (item.Light != lt) if (item.Light != lt)
{ {
m_DeleteQueue.Enqueue(item); queue.Enqueue(item);
} }
else else
{ {
@ -1107,24 +1099,17 @@ namespace Server.Commands
} }
else if (item.ItemData.LightSource && item.ItemData.Name == srcName) else if (item.ItemData.LightSource && item.ItemData.Name == srcName)
{ {
m_DeleteQueue.Enqueue(item); queue.Enqueue(item);
} }
} }
} }
} else if (srcItem is Teleporter or FillableContainer or BaseBook)
else if (srcItem is Teleporter or FillableContainer or BaseBook)
{
eable = map.GetItemsInRange(new Point3D(x, y, z), 0);
var type = srcItem.GetType();
foreach (var item in eable)
{ {
if (item.Z == z && item.ItemID == itemID) if (item.Z == z && item.ItemID == itemID)
{ {
if (item.GetType() != type) if (item.GetType() != type)
{ {
m_DeleteQueue.Enqueue(item); queue.Enqueue(item);
} }
else else
{ {
@ -1132,12 +1117,7 @@ namespace Server.Commands
} }
} }
} }
} else
else
{
eable = map.GetItemsInRange(new Point3D(x, y, z), 0);
foreach (var item in eable)
{ {
if (item.Z == z && item.ItemID == itemID) if (item.Z == z && item.ItemID == itemID)
{ {
@ -1146,9 +1126,9 @@ namespace Server.Commands
} }
} }
while (m_DeleteQueue.Count > 0) while (queue.Count > 0)
{ {
m_DeleteQueue.Dequeue().Delete(); queue.Dequeue().Delete();
} }
return res; return res;
@ -1196,7 +1176,6 @@ namespace Server.Commands
if (item is BaseDoor door) if (item is BaseDoor door)
{ {
var eable = maps[j].GetItemsInRange<BaseDoor>(loc, 1); var eable = maps[j].GetItemsInRange<BaseDoor>(loc, 1);
var itemType = door.GetType(); var itemType = door.GetType();
foreach (var link in eable) foreach (var link in eable)

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Microsoft.Toolkit.HighPerformance; using Microsoft.Toolkit.HighPerformance;
using Server.Collections;
using Server.Engines.Quests.Haven; using Server.Engines.Quests.Haven;
using Server.Engines.Quests.Necro; using Server.Engines.Quests.Necro;
using Server.Engines.Spawners; using Server.Engines.Spawners;
@ -74,8 +75,6 @@ namespace Server.Commands
private static readonly Type typeofCannon = typeof(Cannon); private static readonly Type typeofCannon = typeof(Cannon);
private static readonly Type typeofSerpentPillar = typeof(SerpentPillar); private static readonly Type typeofSerpentPillar = typeof(SerpentPillar);
private static readonly Queue<Item> m_DeleteQueue = new();
private static readonly string[] m_EmptyParams = Array.Empty<string>(); private static readonly string[] m_EmptyParams = Array.Empty<string>();
private List<DecorationEntryMag> m_Entries; private List<DecorationEntryMag> m_Entries;
private int m_ItemID; private int m_ItemID;
@ -1033,16 +1032,16 @@ namespace Server.Commands
private static bool FindItem(int x, int y, int z, Map map, Item srcItem) private static bool FindItem(int x, int y, int z, Map map, Item srcItem)
{ {
var itemID = srcItem.ItemID; var itemID = srcItem.ItemID;
var lt = srcItem.Light;
var srcName = srcItem.ItemData.Name;
var type = srcItem.GetType();
var res = false; var res = false;
IPooledEnumerable<Item> eable; using var queue = PooledRefQueue<Item>.Create();
foreach (var item in map.GetItemsInRange(new Point3D(x, y, z), 1))
if (srcItem is BaseDoor)
{ {
eable = map.GetItemsInRange(new Point3D(x, y, z), 1); if (srcItem is BaseDoor)
foreach (var item in eable)
{ {
if (!(item is BaseDoor)) if (!(item is BaseDoor))
{ {
@ -1075,18 +1074,10 @@ namespace Server.Commands
} }
else if ((item.Z - z).Abs() < 8) else if ((item.Z - z).Abs() < 8)
{ {
m_DeleteQueue.Enqueue(item); queue.Enqueue(item);
} }
} }
} else if (TileData.ItemTable[itemID & TileData.MaxItemValue].LightSource)
else if (TileData.ItemTable[itemID & TileData.MaxItemValue].LightSource)
{
eable = map.GetItemsInRange(new Point3D(x, y, z), 0);
var lt = srcItem.Light;
var srcName = srcItem.ItemData.Name;
foreach (var item in eable)
{ {
if (item.Z == z) if (item.Z == z)
{ {
@ -1094,7 +1085,7 @@ namespace Server.Commands
{ {
if (item.Light != lt) if (item.Light != lt)
{ {
m_DeleteQueue.Enqueue(item); queue.Enqueue(item);
} }
else else
{ {
@ -1103,24 +1094,17 @@ namespace Server.Commands
} }
else if (item.ItemData.LightSource && item.ItemData.Name == srcName) else if (item.ItemData.LightSource && item.ItemData.Name == srcName)
{ {
m_DeleteQueue.Enqueue(item); queue.Enqueue(item);
} }
} }
} }
} else if (srcItem is Teleporter or FillableContainer or BaseBook)
else if (srcItem is Teleporter or FillableContainer or BaseBook)
{
eable = map.GetItemsInRange(new Point3D(x, y, z), 0);
var type = srcItem.GetType();
foreach (var item in eable)
{ {
if (item.Z == z && item.ItemID == itemID) if (item.Z == z && item.ItemID == itemID)
{ {
if (item.GetType() != type) if (item.GetType() != type)
{ {
m_DeleteQueue.Enqueue(item); queue.Enqueue(item);
} }
else else
{ {
@ -1128,12 +1112,7 @@ namespace Server.Commands
} }
} }
} }
} else
else
{
eable = map.GetItemsInRange(new Point3D(x, y, z), 0);
foreach (var item in eable)
{ {
if (item.Z == z && item.ItemID == itemID) if (item.Z == z && item.ItemID == itemID)
{ {
@ -1142,9 +1121,9 @@ namespace Server.Commands
} }
} }
while (m_DeleteQueue.Count > 0) while (queue.Count > 0)
{ {
m_DeleteQueue.Dequeue()?.Delete(); queue.Dequeue()?.Delete();
} }
return res; return res;
@ -1181,11 +1160,9 @@ namespace Server.Commands
if (item is BaseDoor door) if (item is BaseDoor door)
{ {
var eable = maps[j].GetItemsInRange<BaseDoor>(loc, 1);
var itemType = door.GetType(); var itemType = door.GetType();
foreach (var link in eable) foreach (var link in maps[j].GetItemsInRange<BaseDoor>(loc, 1))
{ {
if (link != item && link.Z == door.Z && link.GetType() == itemType) if (link != item && link.Z == door.Z && link.GetType() == itemType)
{ {

View file

@ -121,17 +121,16 @@ namespace Server.Commands
public static int DeleteTeleporters(WorldLocation worldLocation) public static int DeleteTeleporters(WorldLocation worldLocation)
{ {
var eable = worldLocation.Map.GetItemsInRange<Teleporter>(worldLocation, 0);
var count = 0; var count = 0;
foreach (var item in eable) foreach (var item in worldLocation.Map.GetItemsInRange<Teleporter>(worldLocation, 0))
{ {
if (!(item is KeywordTeleporter or SkillTeleporter) && IsWithinZ(item.Z - worldLocation.Z)) if (item is not (KeywordTeleporter or SkillTeleporter) && IsWithinZ(item.Z - worldLocation.Z))
{ {
count++; count++;
item.Delete(); item.Delete();
} }
} }
return count; return count;
} }

View file

@ -91,9 +91,7 @@ namespace Server.Commands
public static void Add_Static(int itemID, Point3D location, Map map, string name) public static void Add_Static(int itemID, Point3D location, Map map, string name)
{ {
var eable = map.GetItemsInRange(location, 0); foreach (var item in map.GetItemsInRange(location, 0))
foreach (var item in eable)
{ {
if (item is Sign && item.Z == location.Z && item.ItemID == itemID) if (item is Sign && item.Z == location.Z && item.ItemID == itemID)
{ {

View file

@ -467,10 +467,8 @@ namespace Server.Engines.ConPVP
if (landTile.ID == 0x244 && statics.Length == 0) // 0x244 = invalid land tile if (landTile.ID == 0x244 && statics.Length == 0) // 0x244 = invalid land tile
{ {
var eable = Map.GetItemsInRange(point, 0);
var empty = true; var empty = true;
foreach (var item in eable) foreach (var item in Map.GetItemsInRange(point, 0))
{ {
if (item != this) if (item != this)
{ {
@ -513,8 +511,7 @@ namespace Server.Engines.ConPVP
var rect = new Rectangle2D(pTop.X, pTop.Y, pBottom.X - pTop.X + 1, pBottom.Y - pTop.Y + 1); var rect = new Rectangle2D(pTop.X, pTop.Y, pBottom.X - pTop.X + 1, pBottom.Y - pTop.Y + 1);
var area = Map.GetItemsInBounds(rect); foreach (var i in Map.GetItemsInBounds(rect))
foreach (var i in area)
{ {
if (i == this || i.ItemID >= 0x4000) if (i == this || i.ItemID >= 0x4000)
{ {
@ -567,6 +564,7 @@ namespace Server.Engines.ConPVP
{ {
continue; continue;
} }
if (i is BRGoal goal) if (i is BRGoal goal)
{ {
var oldLoc = new Point3D(GetWorldLocation()); var oldLoc = new Point3D(GetWorldLocation());
@ -662,8 +660,7 @@ namespace Server.Engines.ConPVP
} }
} }
var eable = GetItemsInRange(0); foreach (var item in GetItemsInRange(0))
foreach (var item in eable)
{ {
if (item.Visible && item != this) if (item.Visible && item != this)
{ {

View file

@ -387,8 +387,7 @@ namespace Server.Engines.Craft
return false; return false;
} }
var eable = map.GetItemsInRange(from.Location, 2); foreach (var item in map.GetItemsInRange(from.Location, 2))
foreach (var item in eable)
{ {
if (item.Z + 16 > item.Z && item.Z + 16 > item.Z && Find(item.ItemID, itemIDs)) if (item.Z + 16 > item.Z && item.Z + 16 > item.Z && Find(item.ItemID, itemIDs))
{ {

View file

@ -39,9 +39,7 @@ public class DefBlacksmithy : CraftSystem
return; return;
} }
var eable = map.GetItemsInRange(from.Location, range); foreach (var item in map.GetItemsInRange(from.Location, range))
foreach (var item in eable)
{ {
var type = item.GetType(); var type = item.GetType();

View file

@ -197,9 +197,7 @@ public partial class LeverPuzzleController : Item
[Usage("GenLeverPuzzle"), Description("Generates lamp room and lever puzzle in doom.")] [Usage("GenLeverPuzzle"), Description("Generates lamp room and lever puzzle in doom.")]
public static void GenLampPuzzle_OnCommand(CommandEventArgs e) public static void GenLampPuzzle_OnCommand(CommandEventArgs e)
{ {
var eable = Map.Malas.GetItemsInRange(lp_Center, 0); foreach (var item in Map.Malas.GetItemsInRange(lp_Center, 0))
foreach (var item in eable)
{ {
if (item is LeverPuzzleController) if (item is LeverPuzzleController)
{ {

View file

@ -155,10 +155,9 @@ namespace Server.Ethics
continue; continue;
} }
var eable = e.Mobile.GetItemsInRange(2);
var found = false; var found = false;
foreach (var item in eable) foreach (var item in e.Mobile.GetItemsInRange(2))
{ {
if (item is AnkhNorth or AnkhWest) if (item is AnkhNorth or AnkhWest)
{ {

View file

@ -78,14 +78,14 @@ namespace Server.Factions
private static bool CheckExistence(Point3D loc, Map facet, Type type) private static bool CheckExistence(Point3D loc, Map facet, Type type)
{ {
var eable = facet.GetItemsInRange(loc, 0); foreach (var item in facet.GetItemsInRange(loc, 0))
foreach (var item in eable)
{ {
if (type.IsInstanceOfType(item)) if (type.IsInstanceOfType(item))
{ {
return true; return true;
} }
} }
return false; return false;
} }
} }

View file

@ -307,8 +307,7 @@ namespace Server.Engines.Harvest
return false; return false;
} }
var eable = m.GetItemsInRange(0); foreach (var i in m.GetItemsInRange(0))
foreach (var i in eable)
{ {
if (i.StackWith(m, i, false)) if (i.StackWith(m, i, false))
{ {

View file

@ -1,5 +1,4 @@
using System; using System;
using System.Linq;
using Server.Items; using Server.Items;
namespace Server.Commands namespace Server.Commands
@ -15,10 +14,8 @@ namespace Server.Commands
public static bool FindMorphItem(int x, int y, int z, int inactiveItemID, int activeItemID) public static bool FindMorphItem(int x, int y, int z, int inactiveItemID, int activeItemID)
{ {
var eable = Map.Felucca.GetItemsInRange(new Point3D(x, y, z), 0);
var found = false; var found = false;
foreach (var item in eable) foreach (var item in Map.Felucca.GetItemsInRange(new Point3D(x, y, z), 0))
{ {
if (item is MorphItem morphItem && morphItem.Z == z && morphItem.InactiveItemId == inactiveItemID && morphItem.ActiveItemId == activeItemID) if (item is MorphItem morphItem && morphItem.Z == z && morphItem.InactiveItemId == inactiveItemID && morphItem.ActiveItemId == activeItemID)
{ {
@ -26,15 +23,14 @@ namespace Server.Commands
break; break;
} }
} }
return found; return found;
} }
public static bool FindEffectController(int x, int y, int z) public static bool FindEffectController(int x, int y, int z)
{ {
var eable = Map.Felucca.GetItemsInRange(new Point3D(x, y, z), 0);
var found = false; var found = false;
foreach (var item in eable) foreach (var item in Map.Felucca.GetItemsInRange(new Point3D(x, y, z), 0))
{ {
if (item is EffectController && item.Z == z) if (item is EffectController && item.Z == z)
{ {
@ -42,22 +38,20 @@ namespace Server.Commands
break; break;
} }
} }
return found; return found;
} }
public static T TryCreateItem<T>(int x, int y, int z, T srcItem) where T : Item public static T TryCreateItem<T>(int x, int y, int z, T srcItem) where T : Item
{ {
var eable = Map.Felucca.GetItemsInBounds<T>(new Rectangle2D(x, y, 1, 1)); foreach (var item in Map.Felucca.GetItemsInBounds<T>(new Rectangle2D(x, y, 1, 1)))
var t = eable.FirstOrDefault(item => item.GetType() == srcItem.GetType());
if (t != null)
{ {
srcItem.Delete(); srcItem.Delete();
return t; return item;
} }
srcItem.MoveToWorld(new Point3D(x, y, z), Map.Felucca); srcItem.MoveToWorld(new Point3D(x, y, z), Map.Felucca);
m_Count++; m_Count++;
return srcItem; return srcItem;
} }

View file

@ -1,6 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using Server.Collections;
using Server.Engines.MLQuests.Gumps; using Server.Engines.MLQuests.Gumps;
using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Objectives;
using Server.Engines.MLQuests.Rewards; using Server.Engines.MLQuests.Rewards;
@ -266,25 +266,39 @@ namespace Server.Engines.MLQuests
{ {
var name = $"MLQS-{GetType().Name}"; var name = $"MLQS-{GetType().Name}";
var toDelete = map.GetItemsInRange(loc, 0).Where(item => item is BaseSpawner && item.Name == name); using var queue = PooledRefQueue<Item>.Create();
foreach (var item in map.GetItemsInRange(loc, 0))
foreach (var item in toDelete)
{ {
item.Delete(); // This predates GUIDs. Let's build these spawners and export them so we can delete this code!
if (item is BaseSpawner spawner && spawner.Name == name)
{
queue.Enqueue(item);
}
}
while (queue.Count > 0)
{
queue.Dequeue().Delete();
} }
s.Name = name; s.Name = name;
s.MoveToWorld(loc, map); s.MoveToWorld(loc, map);
} }
public void PutDeco(Item deco, Point3D loc, Map map) public static void PutDeco(Item deco, Point3D loc, Map map)
{ {
// Auto cleanup on regeneration using var queue = PooledRefQueue<Item>.Create();
var toDelete = map.GetItemsInRange(loc, 0).Where(item => item.ItemID == deco.ItemID && item.Z == loc.Z); foreach (var item in map.GetItemsInRange(loc, 0))
foreach (var item in toDelete)
{ {
item.Delete(); if (item.ItemID == deco.ItemID && item.Z == loc.Z)
{
queue.Enqueue(item);
}
}
while (queue.Count > 0)
{
queue.Dequeue().Delete();
} }
deco.MoveToWorld(loc, map); deco.MoveToWorld(loc, map);

View file

@ -98,10 +98,8 @@ namespace Server.Movement
foreach (var sector in _sectors) foreach (var sector in _sectors)
{ {
for (var j = 0; j < sector.Items.Count; ++j) foreach (var item in sector.Items)
{ {
var item = sector.Items[j];
if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface) if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface)
{ {
continue; continue;
@ -167,10 +165,8 @@ namespace Server.Movement
if (!sectorStartIsForward) if (!sectorStartIsForward)
{ {
for (var i = 0; i < sectorForward.Items.Count; ++i) foreach (var item in sectorForward.Items)
{ {
var item = sectorForward.Items[i];
if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface) if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface)
{ {
continue; continue;
@ -193,10 +189,8 @@ namespace Server.Movement
} }
} }
for (var i = 0; i < sectorStart.Items.Count; ++i) foreach (var item in sectorStart.Items)
{ {
var item = sectorStart.Items[i];
if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface) if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface)
{ {
continue; continue;

View file

@ -147,8 +147,7 @@ namespace Server.Engines.Spawners
// Delete all spawners at this location. // Delete all spawners at this location.
// Probably shouldn't do this outside of migrations? Is there a better way to find/fix spawners? // Probably shouldn't do this outside of migrations? Is there a better way to find/fix spawners?
var eable = map.GetItemsInRange<BaseSpawner>(location, 0); foreach (var spawner in map.GetItemsInRange<BaseSpawner>(location, 0))
foreach (var spawner in eable)
{ {
if (spawner.GetType() == type) if (spawner.GetType() == type)
{ {

View file

@ -50,9 +50,8 @@ namespace Server.Engines.Events
var rect = m_PumpkinFields[i]; var rect = m_PumpkinFields[i];
var spawncount = rect.Height * rect.Width / 20; var spawncount = rect.Height * rect.Width / 20;
var eable = map.GetItemsInBounds<HalloweenPumpkin>(rect);
var pumpkins = 0; var pumpkins = 0;
foreach (var p in eable) foreach (var _ in map.GetItemsInBounds<HalloweenPumpkin>(rect))
{ {
if (pumpkins++ >= spawncount) if (pumpkins++ >= spawncount)
{ {

View file

@ -1,4 +1,3 @@
using System.Linq;
using ModernUO.Serialization; using ModernUO.Serialization;
using Server.Mobiles; using Server.Mobiles;
@ -268,9 +267,15 @@ namespace Server.Items
public static SHTeleporter FindSHTeleporter(Map map, Point3D p) public static SHTeleporter FindSHTeleporter(Map map, Point3D p)
{ {
var eable = map.GetItemsInRange<SHTeleporter>(p, 0); foreach (var teleporter in map.GetItemsInRange<SHTeleporter>(p, 0))
var teleporter = eable.FirstOrDefault(item => item.Z == p.Z); {
return teleporter; if (teleporter.Z == p.Z)
{
return teleporter;
}
}
return null;
} }
public SHTeleporter AddSHT(Map map, bool ext, int x, int y, int z) public SHTeleporter AddSHT(Map map, bool ext, int x, int y, int z)

View file

@ -332,13 +332,10 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable
var z = p.Z; var z = p.Z;
var sector = map.GetSector(x, y); var sector = map.GetSector(x, y);
var items = sector.Items;
var mobs = sector.Mobiles; var mobs = sector.Mobiles;
for (var i = 0; i < items.Count; ++i) foreach (var item in sector.Items)
{ {
var item = items[i];
if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(x, y) && if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(x, y) &&
item is not BaseDoor) item is not BaseDoor)
{ {

View file

@ -130,15 +130,14 @@ public partial class MarkContainer : LockableContainer
private static bool FindMarkContainer(Point3D p, Map map) private static bool FindMarkContainer(Point3D p, Map map)
{ {
var eable = map.GetItemsInRange<MarkContainer>(p, 0); foreach (var item in map.GetItemsInRange<MarkContainer>(p, 0))
foreach (var item in eable)
{ {
if (item.Z == p.Z) if (item.Z == p.Z)
{ {
return true; return true;
} }
} }
return false; return false;
} }

View file

@ -49,9 +49,8 @@ public partial class OilFlask : Item
if (!from.PlaceInBackpack(emptyFlask)) if (!from.PlaceInBackpack(emptyFlask))
{ {
var didStack = false; var didStack = false;
var eable = from.GetItemsInRange(0);
foreach (var i in eable) foreach (var i in from.GetItemsInRange(0))
{ {
if (i.StackWith(from, this, false)) if (i.StackWith(from, this, false))
{ {

View file

@ -2157,10 +2157,7 @@ public abstract class BaseAI
Movement.Movement.Offset(d, ref x, ref y); Movement.Movement.Offset(d, ref x, ref y);
var destroyables = 0; var destroyables = 0;
foreach (var item in map.GetItemsInRange(new Point3D(x, y, m_Mobile.Location.Z), 1))
var eable = map.GetItemsInRange(new Point3D(x, y, m_Mobile.Location.Z), 1);
foreach (var item in eable)
{ {
if (canOpenDoors && item is BaseDoor door && door.Z + door.ItemData.Height > m_Mobile.Z && if (canOpenDoors && item is BaseDoor door && door.Z + door.ItemData.Height > m_Mobile.Z &&
m_Mobile.Z + 16 > door.Z) m_Mobile.Z + 16 > door.Z)

View file

@ -3638,9 +3638,8 @@ namespace Server.Mobiles
return false; return false;
} }
var eable = GetItemsInRange<Corpse>(2);
Corpse toRummage = null; Corpse toRummage = null;
foreach (var c in eable) foreach (var c in GetItemsInRange<Corpse>(2))
{ {
if (c.Items.Count > 0) if (c.Items.Count > 0)
{ {

View file

@ -74,9 +74,8 @@ public partial class HordeMinionFamiliar : BaseFamiliar
return; return;
} }
var eable = GetItemsInRange(2);
using var queue = PooledRefQueue<Item>.Create(); using var queue = PooledRefQueue<Item>.Create();
foreach (var item in eable) foreach (var item in GetItemsInRange(2))
{ {
if (item.Movable && item.Stackable) if (item.Movable && item.Stackable)
{ {

View file

@ -117,9 +117,13 @@ namespace Server.Mobiles
{ {
p = GetSpawnPosition(2); p = GetSpawnPosition(2);
var eable = Map.GetItemsInRange<StainedOoze>(p, 0); var atLocation = false;
using var enumerator = eable.GetEnumerator(); foreach (var item in Map.GetItemsInRange<StainedOoze>(p, 0))
bool atLocation = enumerator.MoveNext(); {
atLocation = true;
break;
}
if (!atLocation) if (!atLocation)
{ {
break; break;

View file

@ -234,9 +234,13 @@ namespace Server.Mobiles
{ {
p = GetSpawnPosition(2); p = GetSpawnPosition(2);
var eable = Map.GetItemsInRange<StainedOoze>(p, 0); var atLocation = false;
using var enumerator = eable.GetEnumerator(); foreach (var item in Map.GetItemsInRange<StainedOoze>(p, 0))
bool atLocation = enumerator.MoveNext(); {
atLocation = true;
break;
}
if (!atLocation) if (!atLocation)
{ {
break; break;

View file

@ -165,18 +165,13 @@ namespace Server.Mobiles
base.OnThink(); base.OnThink();
// Check to see if we need to devour any corpses // Check to see if we need to devour any corpses
var eable = GetItemsInRange<Corpse>(3); // Get all corpses in range foreach (var item in GetItemsInRange<Corpse>(3)) // Get all corpses in range
foreach (var item in eable)
// Ensure that the corpse was killed by us
{ {
// Ensure that the corpse was killed by us
if (item.Killer == this && item.Owner != null && !item.DevourCorpse() && !item.Devoured) if (item.Killer == this && item.Owner != null && !item.DevourCorpse() && !item.Devoured)
{ {
PublicOverheadMessage( // * The plague beast attempts to absorb the remains, but cannot! *
MessageType.Emote, PublicOverheadMessage(MessageType.Emote, 0x3B2, 1053032);
0x3B2,
1053032
); // * The plague beast attempts to absorb the remains, but cannot! *
} }
} }
} }

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Server.Engines.Spawners; using Server.Engines.Spawners;
using Server.Items; using Server.Items;
@ -1502,31 +1501,32 @@ namespace Server.Multis
} }
} }
var eable = map.GetItemsInBounds( var bounds = new Rectangle2D(
new Rectangle2D( p.X + newComponents.Min.X,
p.X + newComponents.Min.X, p.Y + newComponents.Min.Y,
p.Y + newComponents.Min.Y, newComponents.Width,
newComponents.Width, newComponents.Height
newComponents.Height
)
); );
var canFit = eable.All( foreach (var item in map.GetItemsInBounds(bounds))
item => {
if (item is BaseMulti || item.ItemID > TileData.MaxItemValue || item.Z < p.Z || !item.Visible)
{ {
if (item is BaseMulti || item.ItemID > TileData.MaxItemValue || item.Z < p.Z || !item.Visible) continue;
{
return true;
}
var x = item.X - p.X + newComponents.Min.X;
var y = item.Y - p.Y + newComponents.Min.Y;
return x >= 0 && x < newComponents.Width && y >= 0 && y < newComponents.Height &&
newComponents.Tiles[x][y].Length == 0 || Contains(item);
} }
);
return canFit; var x = item.X - p.X + newComponents.Min.X;
var y = item.Y - p.Y + newComponents.Min.Y;
// Out of bounds, return false - cannot fit
if ((x < 0 || x >= newComponents.Width || y < 0 || y >= newComponents.Height ||
newComponents.Tiles[x][y].Length != 0) && !Contains(item))
{
return false;
}
}
return true;
} }
public Point3D Rotate(Point3D p, int count) public Point3D Rotate(Point3D p, int count)

View file

@ -1141,6 +1141,7 @@ namespace Server.Multis
MovingCrate.DropItem(item); MovingCrate.DropItem(item);
} }
// TODO: Convert to a ref struct enumerator
public List<Item> GetItems() public List<Item> GetItems()
{ {
if (Map == null || Map == Map.Internal) if (Map == null || Map == Map.Internal)
@ -1152,8 +1153,14 @@ namespace Server.Multis
var end = new Point2D(X + Components.Max.X + 1, Y + Components.Max.Y + 1); var end = new Point2D(X + Components.Max.X + 1, Y + Components.Max.Y + 1);
var rect = new Rectangle2D(start, end); var rect = new Rectangle2D(start, end);
var eable = Map.GetItemsInBounds(rect); var list = new List<Item>();
var list = eable.Where(item => item.Movable && IsInside(item)).ToList(); foreach (var item in Map.GetItemsInBounds(rect))
{
if (item.Movable && IsInside(item))
{
list.Add(item);
}
}
return list; return list;
} }
@ -3653,11 +3660,14 @@ namespace Server.Multis
} }
var mcl = Components; var mcl = Components;
var eable = var bounds = new Rectangle2D(X + mcl.Min.X, Y + mcl.Min.Y, mcl.Width, mcl.Height);
map.GetItemsInBounds<Guildstone>(new Rectangle2D(X + mcl.Min.X, Y + mcl.Min.Y, mcl.Width, mcl.Height));
var item = eable.FirstOrDefault(Contains); foreach (var gs in map.GetItemsInBounds<Guildstone>(bounds))
return item; {
return gs;
}
return null;
} }
public void ResetDynamicDecay() public void ResetDynamicDecay()

View file

@ -142,10 +142,8 @@ namespace Server.Multis
items.Clear(); items.Clear();
for (var i = 0; i < sector.Items.Count; ++i) foreach (var item in sector.Items)
{ {
var item = sector.Items[i];
if (item.Visible && item.X == tileX && item.Y == tileY) if (item.Visible && item.X == tileX && item.Y == tileY)
{ {
items.Add(item); items.Add(item);
@ -366,12 +364,9 @@ namespace Server.Multis
} }
var sector = map.GetSector(borderPoint.X, borderPoint.Y); var sector = map.GetSector(borderPoint.X, borderPoint.Y);
var sectorItems = sector.Items;
for (var j = 0; j < sectorItems.Count; ++j) foreach (var item in sector.Items)
{ {
var item = sectorItems[j];
if (item.X != borderPoint.X || item.Y != borderPoint.Y || item.Movable) if (item.X != borderPoint.X || item.Y != borderPoint.Y || item.Movable)
{ {
continue; continue;

View file

@ -83,17 +83,15 @@ namespace Server.SkillHandlers
if (Faction.Find(src) != null) if (Faction.Find(src) != null)
{ {
var itemsInRange = src.Map.GetItemsInRange<BaseFactionTrap>(p, range); foreach (var trap in src.Map.GetItemsInRange<BaseFactionTrap>(p, range))
foreach (var trap in itemsInRange)
{ {
if (src.CheckTargetSkill(SkillName.DetectHidden, trap, 80.0, 100.0)) if (src.CheckTargetSkill(SkillName.DetectHidden, trap, 80.0, 100.0))
{ {
src.SendLocalizedMessage( src.SendLocalizedMessage(
1042712, 1042712, // You reveal a trap placed by a faction:
true, true,
$" {(trap.Faction == null ? "" : trap.Faction.Definition.FriendlyName)}" $" {(trap.Faction == null ? "" : trap.Faction.Definition.FriendlyName)}"
); // You reveal a trap placed by a faction: );
trap.Visible = true; trap.Visible = true;
trap.BeginConceal(); trap.BeginConceal();

View file

@ -124,9 +124,8 @@ namespace Server.SkillHandlers
public override void OnCast() public override void OnCast()
{ {
var eable = Caster.GetItemsInRange<Corpse>(3);
Corpse toChannel = null; Corpse toChannel = null;
foreach (var corpse in eable) foreach (var corpse in Caster.GetItemsInRange<Corpse>(3))
{ {
if (!corpse.Channeled) if (!corpse.Channeled)
{ {

View file

@ -141,9 +141,7 @@ namespace Server.Spells.Seventh
private static bool GateExistsAt(Map map, Point3D loc) private static bool GateExistsAt(Map map, Point3D loc)
{ {
var eable = map.GetItemsInRange(loc, 0); foreach (var item in map.GetItemsInRange(loc, 0))
foreach (var item in eable)
{ {
if (item is Moongate or PublicMoongate) if (item is Moongate or PublicMoongate)
{ {

View file

@ -97,9 +97,7 @@ namespace Server.Spells.Spellweaving
} }
} }
var eable = map.GetItemsInRange(location, 0); foreach (var item in map.GetItemsInRange(location, 0))
foreach (var item in eable)
{ {
if (item.Z + item.ItemData.CalcHeight == location.Z && IsValidTile(item.ItemID)) if (item.Z + item.ItemData.CalcHeight == location.Z && IsValidTile(item.ItemID))
{ {

View file

@ -96,9 +96,7 @@ namespace Server.Spells.Third
m.PlaySound(0x1FE); m.PlaySound(0x1FE);
var eable = m.GetItemsInRange(0); foreach (var item in m.GetItemsInRange(0))
foreach (var item in eable)
{ {
if (item is ParalyzeFieldSpell.InternalItem or if (item is ParalyzeFieldSpell.InternalItem or
PoisonFieldSpell.InternalItem or FireFieldSpell.FireFieldItem) PoisonFieldSpell.InternalItem or FireFieldSpell.FireFieldItem)