fix: Refactors getting static tiles to use enumerators (#1611)
### Summary
* Refactors getting static/multi tiles to not use allocations.
* `TileList` is now only used during bootstrapping and uses rented buffers to eliminate extra allocations.
* Replaces `StaticTile[] GetStaticTiles` with:
```cs
Map.StaticTileEnumerable GetStaticTiles(int x, int y);
Map.StaticTileEnumerable GetStaticAndMultiTiles(int x, int y);
Map.StaticTileEnumerable GetMultiTiles(int x, int y);
```
* Removes `Synchronized` and `lock` from TileMatrix. It is no longer considered a multi-thread safe system.
This commit is contained in:
parent
3b1637b2da
commit
f21c9687b2
23 changed files with 359 additions and 512 deletions
|
|
@ -97,7 +97,6 @@ public partial class Map
|
|||
|
||||
public ref struct MultiSectorEnumerator<T> where T : BaseMulti
|
||||
{
|
||||
private Point2D _location;
|
||||
private readonly Span<BaseMulti> _list;
|
||||
private int _index;
|
||||
private T _current;
|
||||
|
|
@ -105,8 +104,6 @@ public partial class Map
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public MultiSectorEnumerator(Map map, Point2D loc)
|
||||
{
|
||||
_location = loc;
|
||||
|
||||
_list = map == null
|
||||
? Span<BaseMulti>.Empty
|
||||
: CollectionsMarshal.AsSpan(map.GetSector(loc.m_X, loc.m_Y).Multis);
|
||||
|
|
@ -118,8 +115,6 @@ public partial class Map
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
ref var loc = ref _location;
|
||||
|
||||
while ((uint)_index < (uint)_list.Length)
|
||||
{
|
||||
var current = _list[_index++];
|
||||
|
|
|
|||
|
|
@ -1,121 +0,0 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2023 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: Map.MultiTileEnumerator.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.Runtime.CompilerServices;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public partial class Map
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public MultiTilesAtEnumerable GetMultiTilesAt(int x, int y) => GetMultiTilesAt(new Point2D(x, y));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public MultiTilesAtEnumerable GetMultiTilesAt(Point2D p) => new(this, p);
|
||||
|
||||
public ref struct MultiTilesAtEnumerable
|
||||
{
|
||||
public static MultiTilesAtEnumerable Empty
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new();
|
||||
}
|
||||
|
||||
private readonly Map _map;
|
||||
private readonly Point2D _location;
|
||||
|
||||
public MultiTilesAtEnumerable(Map map, Point2D loc)
|
||||
{
|
||||
_map = map;
|
||||
_location = loc;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public MultiTilesAtEnumerator GetEnumerator() => new(_map, _location);
|
||||
}
|
||||
|
||||
public ref struct MultiTilesAtEnumerator
|
||||
{
|
||||
private Point2D _location;
|
||||
private MultiSectorEnumerator<BaseMulti> _multis;
|
||||
private BaseMulti _currentMulti;
|
||||
private StaticTile[] _current;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public MultiTilesAtEnumerator(Map map, Point2D loc)
|
||||
{
|
||||
_multis = (map == null ? MultiSectorEnumerable<BaseMulti>.Empty : map.GetMultisInSector(loc)).GetEnumerator();
|
||||
|
||||
_current = null;
|
||||
_location = loc;
|
||||
_currentMulti = null;
|
||||
}
|
||||
|
||||
private bool SetStaticTiles()
|
||||
{
|
||||
var mcl = _currentMulti.Components;
|
||||
var x = _location.X;
|
||||
var xo = x - (_currentMulti.X + mcl.Min.X);
|
||||
|
||||
var y = _location.Y;
|
||||
if (xo < 0 || xo >= mcl.Width)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var yo = y - (_currentMulti.Y + mcl.Min.Y);
|
||||
if (yo < 0 || yo >= mcl.Height)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var t = mcl.Tiles[xo][yo];
|
||||
|
||||
// TODO: Remove the allocation.
|
||||
var r = new StaticTile[t.Length];
|
||||
|
||||
for (var i = 0; i < t.Length; i++)
|
||||
{
|
||||
r[i] = t[i];
|
||||
r[i].Z += _currentMulti.Z;
|
||||
}
|
||||
|
||||
_current = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
while (_multis.MoveNext())
|
||||
{
|
||||
_currentMulti = _multis.Current;
|
||||
if (SetStaticTiles())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public StaticTile[] Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _current;
|
||||
}
|
||||
}
|
||||
}
|
||||
138
Projects/Server/Maps/Map.StaticTileEnumerator.cs
Normal file
138
Projects/Server/Maps/Map.StaticTileEnumerator.cs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2023 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: Map.StaticTileEnumerator.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.Runtime.CompilerServices;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public partial class Map
|
||||
{
|
||||
public ref struct StaticTileEnumerable
|
||||
{
|
||||
public static StaticTileEnumerable Empty
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new();
|
||||
}
|
||||
|
||||
private readonly Map _map;
|
||||
private readonly Point2D _location;
|
||||
private readonly bool _includeStatics;
|
||||
private readonly bool _includeMultis;
|
||||
|
||||
public StaticTileEnumerable(Map map, Point2D loc, bool includeStatics = true, bool includeMultis = true)
|
||||
{
|
||||
_map = map;
|
||||
_location = loc;
|
||||
_includeStatics = includeStatics;
|
||||
_includeMultis = includeMultis;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public StaticTileEnumerator GetEnumerator() => new(_map, _location, _includeStatics, _includeMultis);
|
||||
}
|
||||
|
||||
public ref struct StaticTileEnumerator
|
||||
{
|
||||
private readonly Map _map;
|
||||
private readonly Point2D _point;
|
||||
|
||||
private StaticTile[] _tiles;
|
||||
private MultiSectorEnumerator<BaseMulti> _multis;
|
||||
private BaseMulti _currentMulti;
|
||||
|
||||
private int _index;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public StaticTileEnumerator(Map map, Point2D p, bool includeStatics, bool includeMultis)
|
||||
{
|
||||
_map = map;
|
||||
_point = p;
|
||||
|
||||
if (_map == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (includeStatics)
|
||||
{
|
||||
var tiles = map.Tiles.GetStaticBlock(p.X >> SectorShift, p.Y >> SectorShift);
|
||||
_tiles = tiles[p.X & 0x7][p.Y & 0x7];
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
_multis = includeMultis
|
||||
? _map.GetMultisInSector(p).GetEnumerator()
|
||||
: MultiSectorEnumerable<BaseMulti>.Empty.GetEnumerator();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool SetMulti()
|
||||
{
|
||||
ref var multis = ref _multis;
|
||||
ref readonly var p = ref _point;
|
||||
|
||||
if (multis.MoveNext())
|
||||
{
|
||||
var multi = multis.Current;
|
||||
_currentMulti = multi;
|
||||
var components = multi!.Components;
|
||||
var location = multi!.Location;
|
||||
|
||||
int offsetX = p.X - location.X - components.Min.X;
|
||||
int offsetY = p.Y - location.Y - components.Min.Y;
|
||||
|
||||
if (offsetX >= 0 && offsetY >= 0 && offsetX < components.Width && offsetY < components.Height)
|
||||
{
|
||||
_tiles = multi.Components.Tiles[offsetX][offsetY];
|
||||
_index = -1;
|
||||
return SetTile();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool SetTile() => _tiles != null && ++_index < _tiles.Length;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => _map != null && (SetTile() || SetMulti());
|
||||
|
||||
public StaticTile Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if (_currentMulti == null)
|
||||
{
|
||||
return _tiles[_index];
|
||||
}
|
||||
|
||||
var location = _currentMulti.Location;
|
||||
ref readonly var tile = ref _tiles[_index];
|
||||
return new StaticTile
|
||||
{
|
||||
m_ID = tile.m_ID,
|
||||
X = tile.m_X,
|
||||
Y = tile.m_Y,
|
||||
Z = tile.m_Z + location.Z,
|
||||
m_Hue = tile.m_Hue
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,6 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
|
||||
private readonly int m_SectorsWidth;
|
||||
|
||||
private readonly object tileLock = new();
|
||||
private Region m_DefaultRegion;
|
||||
|
||||
private string m_Name;
|
||||
|
|
@ -89,21 +88,7 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
|
||||
public int Season { get; set; }
|
||||
|
||||
public TileMatrix Tiles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Tiles == null)
|
||||
{
|
||||
lock (tileLock)
|
||||
{
|
||||
m_Tiles = new TileMatrix(this, m_FileIndex, MapID, Width, Height);
|
||||
}
|
||||
}
|
||||
|
||||
return m_Tiles;
|
||||
}
|
||||
}
|
||||
public TileMatrix Tiles => m_Tiles ??= new TileMatrix(this, m_FileIndex, MapID, Width, Height);
|
||||
|
||||
public int MapID { get; }
|
||||
|
||||
|
|
@ -325,7 +310,6 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
public void FixColumn(int x, int y)
|
||||
{
|
||||
var landTile = Tiles.GetLandTile(x, y);
|
||||
var tiles = Tiles.GetStaticTiles(x, y, true);
|
||||
|
||||
GetAverageZ(x, y, out _, out var landAvg, out _);
|
||||
|
||||
|
|
@ -349,7 +333,7 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
z = landAvg;
|
||||
}
|
||||
|
||||
foreach (var tile in tiles)
|
||||
foreach (var tile in Tiles.GetStaticAndMultiTiles(x, y))
|
||||
{
|
||||
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
||||
|
||||
|
|
@ -400,33 +384,6 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
STArrayPool<Item>.Shared.Return(items, true);
|
||||
}
|
||||
|
||||
/* This could probably be re-implemented if necessary (perhaps via an ITile interface?).
|
||||
public List<Tile> GetTilesAt( Point2D p, bool items, bool land, bool statics )
|
||||
{
|
||||
List<Tile> list = new List<Tile>();
|
||||
|
||||
if (this == Internal)
|
||||
return list;
|
||||
|
||||
if (land)
|
||||
list.Add( Tiles.GetLandTile( p.m_X, p.m_Y ) );
|
||||
|
||||
if (statics)
|
||||
list.AddRange( Tiles.GetStaticTiles( p.m_X, p.m_Y, true ) );
|
||||
|
||||
if (items)
|
||||
{
|
||||
Sector sector = GetSector( p );
|
||||
|
||||
foreach ( Item item in sector.Items )
|
||||
if (item.AtWorldPoint( p.m_X, p.m_Y ))
|
||||
list.Add( new StaticTile( (ushort)item.ItemID, (sbyte) item.Z ) );
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Gets the highest surface that is lower than <paramref name="p" />.
|
||||
/// </summary>
|
||||
|
|
@ -460,11 +417,8 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
}
|
||||
}
|
||||
|
||||
var staticTiles = Tiles.GetStaticTiles(p.X, p.Y, true);
|
||||
|
||||
for (var i = 0; i < staticTiles.Length; i++)
|
||||
foreach (var tile in Tiles.GetStaticAndMultiTiles(p.X, p.Y))
|
||||
{
|
||||
var tile = staticTiles[i];
|
||||
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
||||
|
||||
if (id.Surface || id.Wet)
|
||||
|
|
@ -862,14 +816,14 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
return p;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool CanFit(
|
||||
Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true,
|
||||
bool requireSurface = true
|
||||
Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true, bool requireSurface = true
|
||||
) => CanFit(p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, checkMobiles, requireSurface);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool CanFit(
|
||||
Point2D p, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true,
|
||||
bool requireSurface = true
|
||||
Point2D p, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true, bool requireSurface = true
|
||||
) => CanFit(p.m_X, p.m_Y, z, height, checkBlocksFit, checkMobiles, requireSurface);
|
||||
|
||||
public bool CanFit(
|
||||
|
|
@ -903,29 +857,26 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
hasSurface = true;
|
||||
}
|
||||
|
||||
var staticTiles = Tiles.GetStaticTiles(x, y, true);
|
||||
|
||||
bool surface, impassable;
|
||||
|
||||
for (var i = 0; i < staticTiles.Length; ++i)
|
||||
foreach (var tile in Tiles.GetStaticAndMultiTiles(x, y))
|
||||
{
|
||||
var id = TileData.ItemTable[staticTiles[i].ID & TileData.MaxItemValue];
|
||||
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
||||
surface = id.Surface;
|
||||
impassable = id.Impassable;
|
||||
|
||||
if ((surface || impassable) && staticTiles[i].Z + id.CalcHeight > z && z + height > staticTiles[i].Z)
|
||||
if ((surface || impassable) && tile.Z + id.CalcHeight > z && z + height > tile.Z)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (surface && !impassable && z == staticTiles[i].Z + id.CalcHeight)
|
||||
if (surface && !impassable && z == tile.Z + id.CalcHeight)
|
||||
{
|
||||
hasSurface = true;
|
||||
}
|
||||
}
|
||||
|
||||
var sector = GetSector(x, y);
|
||||
var mobs = sector.Mobiles;
|
||||
|
||||
foreach (var item in sector.Items)
|
||||
{
|
||||
|
|
@ -1122,8 +1073,6 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
return false;
|
||||
*/
|
||||
|
||||
var statics = Tiles.GetStaticTiles(point.m_X, point.m_Y, true);
|
||||
|
||||
var contains = false;
|
||||
var ltID = landTile.ID;
|
||||
|
||||
|
|
@ -1132,26 +1081,11 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
contains = ltID == InvalidLandTiles[j];
|
||||
}
|
||||
|
||||
if (contains && statics.Length == 0)
|
||||
{
|
||||
foreach (Item item in GetItemsInRange(point, 0))
|
||||
{
|
||||
if (item.Visible)
|
||||
{
|
||||
contains = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool foundStatic = false;
|
||||
|
||||
if (contains)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < statics.Length; ++j)
|
||||
foreach (var t in Tiles.GetStaticAndMultiTiles(point.m_X, point.m_Y))
|
||||
{
|
||||
var t = statics[j];
|
||||
foundStatic = true;
|
||||
|
||||
var id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
|
||||
|
||||
|
|
@ -1168,6 +1102,23 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (contains && !foundStatic)
|
||||
{
|
||||
foreach (Item item in GetItemsAt(point))
|
||||
{
|
||||
if (item.Visible)
|
||||
{
|
||||
contains = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (contains)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var rect = new Rectangle2D(pTop.m_X, pTop.m_Y, pBottom.m_X - pTop.m_X + 1, pBottom.m_Y - pTop.m_Y + 1);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue