fix: Prepares for IPooledEnumerable removal (#1548)
### Summary - Removes `IPooledEnumerable` (non-generic) - Changes `IPooledEnumerable<T>` so that `Free()` is replaced with the `IDisposable` pattern
This commit is contained in:
parent
c2d6578955
commit
d57f1fecc1
92 changed files with 644 additions and 907 deletions
|
|
@ -1,10 +1,24 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2023 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: Map.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;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Server.Buffers;
|
||||
using Server.Collections;
|
||||
using Server.Items;
|
||||
using Server.Logging;
|
||||
using Server.Network;
|
||||
|
|
@ -24,295 +38,6 @@ public enum MapRules
|
|||
FeluccaRules = None
|
||||
}
|
||||
|
||||
public interface IPooledEnumerable : IEnumerable
|
||||
{
|
||||
void Free();
|
||||
}
|
||||
|
||||
public interface IPooledEnumerable<T> : IPooledEnumerable, IEnumerable<T>
|
||||
{
|
||||
}
|
||||
|
||||
public static class PooledEnumeration
|
||||
{
|
||||
public delegate IEnumerable<T> Selector<out T>(Sector sector, Rectangle2D bounds);
|
||||
|
||||
static PooledEnumeration()
|
||||
{
|
||||
ClientSelector = SelectClients;
|
||||
EntitySelector = SelectEntities;
|
||||
MobileSelector = SelectMobiles<Mobile>;
|
||||
ItemSelector = SelectItems<Item>;
|
||||
MultiSelector = SelectMultis;
|
||||
MultiTileSelector = SelectMultiTiles;
|
||||
}
|
||||
|
||||
public static Selector<NetState> ClientSelector { get; set; }
|
||||
public static Selector<IEntity> EntitySelector { 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<StaticTile[]> MultiTileSelector { get; set; }
|
||||
|
||||
public static IEnumerable<NetState> SelectClients(Sector s, Rectangle2D bounds)
|
||||
{
|
||||
var clients = new List<NetState>(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<IEntity> SelectEntities(Sector s, Rectangle2D bounds)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (j >= 0)
|
||||
{
|
||||
Item item = s.Items[j];
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> SelectMobiles<T>(Sector s, Rectangle2D bounds) where T : Mobile
|
||||
{
|
||||
var entities = new List<T>(s.Mobiles.Count);
|
||||
for (int i = s.Mobiles.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (s.Mobiles[i] is T { Deleted: false } mob && bounds.Contains(mob.Location))
|
||||
{
|
||||
entities.Add(mob);
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> SelectItems<T>(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(Sector s, Rectangle2D bounds)
|
||||
{
|
||||
var entities = new List<BaseMulti>(s.Multis.Count);
|
||||
for (int i = s.Multis.Count - 1; i >= 0; --i)
|
||||
{
|
||||
BaseMulti multi = s.Multis[i];
|
||||
if (multi is { Deleted: false } && bounds.Contains(multi.Location))
|
||||
{
|
||||
entities.Add(multi);
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
public static IEnumerable<StaticTile[]> SelectMultiTiles(Sector s, Rectangle2D bounds)
|
||||
{
|
||||
for (int l = s.Multis.Count - 1; l >= 0; --l)
|
||||
{
|
||||
BaseMulti o = s.Multis[l];
|
||||
if (o?.Deleted != false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MultiComponentList c = o.Components;
|
||||
|
||||
int x, y, xo, yo;
|
||||
StaticTile[] t, r;
|
||||
|
||||
for (x = bounds.Start.X; x < bounds.End.X; x++)
|
||||
{
|
||||
xo = x - (o.X + c.Min.X);
|
||||
|
||||
if (xo < 0 || xo >= c.Width)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (y = bounds.Start.Y; y < bounds.End.Y; y++)
|
||||
{
|
||||
yo = y - (o.Y + c.Min.Y);
|
||||
|
||||
if (yo < 0 || yo >= c.Height)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
t = c.Tiles[xo][yo];
|
||||
|
||||
if (t.Length <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
r = new StaticTile[t.Length];
|
||||
|
||||
for (var i = 0; i < t.Length; i++)
|
||||
{
|
||||
r[i] = t[i];
|
||||
r[i].Z += o.Z;
|
||||
}
|
||||
|
||||
yield return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map.PooledEnumerable<NetState> GetClients(Map map, Rectangle2D bounds) =>
|
||||
Map.PooledEnumerable<NetState>.Instantiate(map, bounds, ClientSelector ?? SelectClients);
|
||||
|
||||
public static Map.PooledEnumerable<IEntity> GetEntities(Map map, Rectangle2D bounds) =>
|
||||
Map.PooledEnumerable<IEntity>.Instantiate(map, bounds, EntitySelector ?? SelectEntities);
|
||||
|
||||
public static Map.PooledEnumerable<Mobile> GetMobiles(Map map, Rectangle2D bounds) =>
|
||||
GetMobiles<Mobile>(map, bounds);
|
||||
|
||||
public static Map.PooledEnumerable<T> GetMobiles<T>(Map map, Rectangle2D bounds) where T : Mobile =>
|
||||
Map.PooledEnumerable<T>.Instantiate(map, bounds, SelectMobiles<T>);
|
||||
|
||||
public static Map.PooledEnumerable<T> GetItems<T>(Map map, Rectangle2D bounds) where T : Item =>
|
||||
Map.PooledEnumerable<T>.Instantiate(map, bounds, SelectItems<T>);
|
||||
|
||||
public static Map.PooledEnumerable<BaseMulti> GetMultis(Map map, Rectangle2D bounds) =>
|
||||
Map.PooledEnumerable<BaseMulti>.Instantiate(map, bounds, MultiSelector ?? SelectMultis);
|
||||
|
||||
public static Map.PooledEnumerable<StaticTile[]> GetMultiTiles(Map map, Rectangle2D bounds) =>
|
||||
Map.PooledEnumerable<StaticTile[]>.Instantiate(map, bounds, MultiTileSelector ?? SelectMultiTiles);
|
||||
|
||||
public static IEnumerable<Sector> EnumerateSectors(Map map, Rectangle2D bounds)
|
||||
{
|
||||
if (map == null || map == Map.Internal)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var x1 = bounds.Start.X;
|
||||
var y1 = bounds.Start.Y;
|
||||
var x2 = bounds.End.X;
|
||||
var y2 = bounds.End.Y;
|
||||
|
||||
if (!Bound(map, ref x1, ref y1, ref x2, ref y2, out var xSector, out var ySector))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
|
||||
while (NextSector(map, x1, y1, x2, y2, ref index, ref xSector, ref ySector, out var s))
|
||||
{
|
||||
yield return s;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Bound(
|
||||
Map map,
|
||||
ref int x1,
|
||||
ref int y1,
|
||||
ref int x2,
|
||||
ref int y2,
|
||||
out int xSector,
|
||||
out int ySector
|
||||
)
|
||||
{
|
||||
if (map == null || map == Map.Internal)
|
||||
{
|
||||
xSector = ySector = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
map.Bound(x1, y1, out x1, out y1);
|
||||
map.Bound(x2 - 1, y2 - 1, out x2, out y2);
|
||||
|
||||
x1 >>= Map.SectorShift;
|
||||
y1 >>= Map.SectorShift;
|
||||
x2 >>= Map.SectorShift;
|
||||
y2 >>= Map.SectorShift;
|
||||
|
||||
xSector = x1;
|
||||
ySector = y1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool NextSector(
|
||||
Map map,
|
||||
int x1,
|
||||
int y1,
|
||||
int x2,
|
||||
int y2,
|
||||
ref int index,
|
||||
ref int xSector,
|
||||
ref int ySector,
|
||||
out Sector s
|
||||
)
|
||||
{
|
||||
if (map == null)
|
||||
{
|
||||
s = null;
|
||||
xSector = ySector = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (map == Map.Internal)
|
||||
{
|
||||
s = map.InvalidSector;
|
||||
xSector = ySector = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index++ > 0)
|
||||
{
|
||||
if (++ySector > y2)
|
||||
{
|
||||
ySector = y1;
|
||||
|
||||
if (++xSector > x2)
|
||||
{
|
||||
xSector = x1;
|
||||
|
||||
s = map.InvalidSector;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s = map.GetRealSector(xSector, ySector);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
|
||||
{
|
||||
public const int SectorSize = 16;
|
||||
|
|
@ -501,7 +226,6 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString() => Name;
|
||||
|
||||
public string ToString(string format, IFormatProvider formatProvider)
|
||||
|
|
@ -598,8 +322,6 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
|
|||
}
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
Array.Sort(pool, 0, length, ZComparer.Default);
|
||||
}
|
||||
|
||||
|
|
@ -1429,8 +1151,6 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
|
|||
}
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
if (contains)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -1510,11 +1230,9 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
|
|||
continue;
|
||||
}
|
||||
|
||||
area.Free();
|
||||
return false;
|
||||
}
|
||||
|
||||
area.Free();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1598,88 +1316,6 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
|
|||
return loc;
|
||||
}
|
||||
|
||||
public class NullEnumerable<T> : IPooledEnumerable<T>
|
||||
{
|
||||
public static readonly NullEnumerable<T> Instance = new();
|
||||
|
||||
private readonly IEnumerable<T> m_Empty = Enumerable.Empty<T>();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => m_Empty.GetEnumerator();
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => m_Empty.GetEnumerator();
|
||||
|
||||
public void Free()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PooledEnumerable<T> : IPooledEnumerable<T>, IDisposable
|
||||
{
|
||||
private static readonly Queue<PooledEnumerable<T>> _Buffer = new(0x400);
|
||||
|
||||
private bool m_IsDisposed;
|
||||
|
||||
private List<T> m_Pool = new(0x40);
|
||||
|
||||
public PooledEnumerable(IEnumerable<T> pool)
|
||||
{
|
||||
m_Pool.AddRange(pool);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_IsDisposed = true;
|
||||
|
||||
m_Pool.Clear();
|
||||
m_Pool.TrimExcess();
|
||||
m_Pool = null;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => m_Pool.GetEnumerator();
|
||||
|
||||
public IEnumerator<T> GetEnumerator() => m_Pool.GetEnumerator();
|
||||
|
||||
public void Free()
|
||||
{
|
||||
if (m_IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Pool.Clear();
|
||||
m_Pool.Capacity = Math.Max(m_Pool.Capacity, 0x100);
|
||||
|
||||
lock (((ICollection)_Buffer).SyncRoot)
|
||||
{
|
||||
_Buffer.Enqueue(this);
|
||||
}
|
||||
}
|
||||
#pragma warning disable CA1000 // Do not declare static members on generic types
|
||||
public static PooledEnumerable<T> Instantiate(
|
||||
Map map, Rectangle2D bounds, PooledEnumeration.Selector<T> selector
|
||||
)
|
||||
{
|
||||
PooledEnumerable<T> e = null;
|
||||
|
||||
lock (((ICollection)_Buffer).SyncRoot)
|
||||
{
|
||||
if (_Buffer.Count > 0)
|
||||
{
|
||||
e = _Buffer.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
var pool = PooledEnumeration.EnumerateSectors(map, bounds).SelectMany(s => selector(s, bounds));
|
||||
|
||||
if (e == null)
|
||||
{
|
||||
return new PooledEnumerable<T>(pool);
|
||||
}
|
||||
|
||||
e.m_Pool.AddRange(pool);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1000 // Do not declare static members on generic types
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Map Parse(string s) => Parse(s, null);
|
||||
|
|
@ -1775,4 +1411,212 @@ public sealed class Map : IComparable<Map>, ISpanFormattable, ISpanParsable<Map>
|
|||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public class RegionRect : IComparable<RegionRect>
|
||||
{
|
||||
private Rectangle3D m_Rect;
|
||||
|
||||
public RegionRect(Region region, Rectangle3D rect)
|
||||
{
|
||||
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
|
||||
{
|
||||
// TODO: Can we avoid this?
|
||||
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<BaseMulti> m_DefaultMultiList = new();
|
||||
private static readonly List<RegionRect> m_DefaultRectList = new();
|
||||
private bool m_Active;
|
||||
private List<NetState> m_Clients;
|
||||
private List<Item> m_Items;
|
||||
private List<Mobile> m_Mobiles;
|
||||
private List<BaseMulti> m_Multis;
|
||||
private List<RegionRect> m_RegionRects;
|
||||
|
||||
public Sector(int x, int y, Map owner)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Owner = owner;
|
||||
m_Active = false;
|
||||
}
|
||||
|
||||
public List<RegionRect> RegionRects => m_RegionRects ?? m_DefaultRectList;
|
||||
|
||||
public List<BaseMulti> Multis => m_Multis ?? m_DefaultMultiList;
|
||||
|
||||
public List<Mobile> Mobiles => m_Mobiles ?? m_DefaultMobileList;
|
||||
|
||||
public List<Item> Items => m_Items ?? m_DefaultItemList;
|
||||
|
||||
public List<NetState> Clients => m_Clients ?? m_DefaultClientList;
|
||||
|
||||
public bool Active => m_Active && Owner != Map.Internal;
|
||||
|
||||
public Map Owner { get; }
|
||||
|
||||
public int X { get; }
|
||||
|
||||
public int Y { get; }
|
||||
|
||||
public void OnClientChange(NetState oldState, NetState newState)
|
||||
{
|
||||
Utility.Replace(ref m_Clients, oldState, newState);
|
||||
}
|
||||
|
||||
public void OnEnter(Item item)
|
||||
{
|
||||
Utility.Add(ref m_Items, item);
|
||||
}
|
||||
|
||||
public void OnLeave(Item item)
|
||||
{
|
||||
Utility.Remove(ref m_Items, item);
|
||||
}
|
||||
|
||||
public void OnEnter(Mobile mob)
|
||||
{
|
||||
Utility.Add(ref m_Mobiles, mob);
|
||||
|
||||
if (mob.NetState != null)
|
||||
{
|
||||
Utility.Add(ref m_Clients, mob.NetState);
|
||||
|
||||
Owner.ActivateSectors(X, Y);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnLeave(Mobile mob)
|
||||
{
|
||||
Utility.Remove(ref m_Mobiles, mob);
|
||||
|
||||
if (mob.NetState != null)
|
||||
{
|
||||
Utility.Remove(ref m_Clients, mob.NetState);
|
||||
|
||||
Owner.DeactivateSectors(X, Y);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEnter(Region region, Rectangle3D rect)
|
||||
{
|
||||
Utility.Add(ref m_RegionRects, new RegionRect(region, rect));
|
||||
|
||||
m_RegionRects.Sort();
|
||||
|
||||
UpdateMobileRegions();
|
||||
}
|
||||
|
||||
public void OnLeave(Region region)
|
||||
{
|
||||
if (m_RegionRects != null)
|
||||
{
|
||||
for (var i = m_RegionRects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var regRect = m_RegionRects[i];
|
||||
|
||||
if (regRect.Region == region)
|
||||
{
|
||||
m_RegionRects.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_RegionRects.Count == 0)
|
||||
{
|
||||
m_RegionRects = null;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateMobileRegions();
|
||||
}
|
||||
|
||||
private void UpdateMobileRegions()
|
||||
{
|
||||
if (m_Mobiles != null)
|
||||
{
|
||||
using var queue = PooledRefQueue<Mobile>.Create(m_Mobiles.Count);
|
||||
foreach (var mob in m_Mobiles)
|
||||
{
|
||||
queue.Enqueue(mob);
|
||||
}
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
queue.Dequeue().UpdateRegion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMultiEnter(BaseMulti multi)
|
||||
{
|
||||
Utility.Add(ref m_Multis, multi);
|
||||
}
|
||||
|
||||
public void OnMultiLeave(BaseMulti multi)
|
||||
{
|
||||
Utility.Remove(ref m_Multis, multi);
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (!Active)
|
||||
{
|
||||
if (m_Items != null)
|
||||
{
|
||||
foreach (var item in m_Items)
|
||||
{
|
||||
item.OnSectorActivate();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Mobiles != null)
|
||||
{
|
||||
foreach (var mob in m_Mobiles)
|
||||
{
|
||||
mob.OnSectorActivate();
|
||||
}
|
||||
}
|
||||
|
||||
m_Active = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
if (Active)
|
||||
{
|
||||
if (m_Items != null)
|
||||
{
|
||||
foreach (var item in m_Items)
|
||||
{
|
||||
item.OnSectorDeactivate();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Mobiles != null)
|
||||
{
|
||||
foreach (var mob in m_Mobiles)
|
||||
{
|
||||
mob.OnSectorDeactivate();
|
||||
}
|
||||
}
|
||||
|
||||
m_Active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue