Skip to content

Commit

Permalink
fix: Refactors getting static tiles to use enumerators (#1611)
Browse files Browse the repository at this point in the history
### 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.
  • Loading branch information
kamronbatman authored Nov 26, 2023
1 parent 3b1637b commit f21c968
Show file tree
Hide file tree
Showing 23 changed files with 357 additions and 510 deletions.
2 changes: 0 additions & 2 deletions Projects/Server/Diagnostics/PacketProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public PacketSendProfile(int packetId) : base($"0x{packetId:X2}")

public static IEnumerable<PacketSendProfile> Profiles => _profiles.Values;

[MethodImpl(MethodImplOptions.Synchronized)]
public static PacketSendProfile Acquire(int packetId)
{
if (!_profiles.TryGetValue(packetId, out var prof))
Expand Down Expand Up @@ -78,7 +77,6 @@ public PacketReceiveProfile(int packetId) : base($"0x{packetId:X2}")

public static IEnumerable<PacketReceiveProfile> Profiles => _profiles.Values;

[MethodImpl(MethodImplOptions.Synchronized)]
public static PacketReceiveProfile Acquire(int packetId)
{
if (!_profiles.TryGetValue(packetId, out var prof))
Expand Down
11 changes: 3 additions & 8 deletions Projects/Server/Items/Item.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3518,11 +3518,8 @@ public virtual bool DropToWorld(Mobile from, Point3D p)
}
}

var tiles = map.Tiles.GetStaticTiles(x, y, true);

for (var i = 0; i < tiles.Length; ++i)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y))
{
var tile = tiles[i];
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];

if (!id.Surface)
Expand Down Expand Up @@ -3576,9 +3573,8 @@ public virtual bool DropToWorld(Mobile from, Point3D p)

var surfaceZ = z;

for (var i = 0; i < tiles.Length; ++i)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y))
{
var tile = tiles[i];
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];

var checkZ = tile.Z;
Expand Down Expand Up @@ -3681,9 +3677,8 @@ public virtual bool DropToWorld(Mobile from, Point3D p)
return false;
}

for (var i = 0; i < tiles.Length; ++i)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y))
{
var tile = tiles[i];
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];

var checkZ = tile.Z;
Expand Down
5 changes: 0 additions & 5 deletions Projects/Server/Maps/Map.MultiEnumerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,13 @@ public static MultiSectorEnumerable<T> Empty

public ref struct MultiSectorEnumerator<T> where T : BaseMulti
{
private Point2D _location;
private readonly Span<BaseMulti> _list;
private int _index;
private T _current;

[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);
Expand All @@ -118,8 +115,6 @@ public MultiSectorEnumerator(Map map, Point2D loc)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
ref var loc = ref _location;

while ((uint)_index < (uint)_list.Length)
{
var current = _list[_index++];
Expand Down
121 changes: 0 additions & 121 deletions Projects/Server/Maps/Map.MultiTileEnumerator.cs

This file was deleted.

138 changes: 138 additions & 0 deletions Projects/Server/Maps/Map.StaticTileEnumerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: [email protected] *
* 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
};
}
}
}
}
Loading

0 comments on commit f21c968

Please sign in to comment.