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:
Kamron Batman 2023-11-26 09:39:46 -08:00 committed by GitHub
parent 3b1637b2da
commit f21c9687b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 359 additions and 512 deletions

View file

@ -43,7 +43,6 @@ public class PacketSendProfile : BasePacketProfile
public static IEnumerable<PacketSendProfile> Profiles => _profiles.Values;
[MethodImpl(MethodImplOptions.Synchronized)]
public static PacketSendProfile Acquire(int packetId)
{
if (!_profiles.TryGetValue(packetId, out var prof))
@ -78,7 +77,6 @@ public class PacketReceiveProfile : BasePacketProfile
public static IEnumerable<PacketReceiveProfile> Profiles => _profiles.Values;
[MethodImpl(MethodImplOptions.Synchronized)]
public static PacketReceiveProfile Acquire(int packetId)
{
if (!_profiles.TryGetValue(packetId, out var prof))

View file

@ -3518,11 +3518,8 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
}
}
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)
@ -3576,9 +3573,8 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
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;
@ -3681,9 +3677,8 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
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;

View file

@ -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++];

View file

@ -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;
}
}
}

View 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
};
}
}
}
}

View file

@ -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);

View file

@ -1,3 +1,18 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: MultiData.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.Buffers;
using System.Collections.Generic;
@ -508,13 +523,9 @@ public sealed class MultiComponentList
oldTiles = Tiles[vx][vy];
var newTiles = new StaticTile[oldTiles.Length + 1];
Array.Copy(oldTiles, newTiles, oldTiles.Length);
for (var i = 0; i < oldTiles.Length; ++i)
{
newTiles[i] = oldTiles[i];
}
newTiles[oldTiles.Length] = new StaticTile((ushort)itemID, (sbyte)z);
newTiles[^1] = new StaticTile((ushort)itemID, (sbyte)z);
Tiles[vx][vy] = newTiles;
@ -574,16 +585,8 @@ public sealed class MultiComponentList
if (tile.Z == z && tile.Height >= minHeight)
{
var newTiles = new StaticTile[oldTiles.Length - 1];
for (var j = 0; j < i; ++j)
{
newTiles[j] = oldTiles[j];
}
for (var j = i + 1; j < oldTiles.Length; ++j)
{
newTiles[j - 1] = oldTiles[j];
}
Array.Copy(oldTiles, newTiles, i);
Array.Copy(oldTiles, i + 1, newTiles, i, oldTiles.Length - i - 1);
Tiles[vx][vy] = newTiles;
@ -601,16 +604,8 @@ public sealed class MultiComponentList
TileData.ItemTable[tile.ItemId & TileData.MaxItemValue].Height >= minHeight)
{
var newList = new MultiTileEntry[oldList.Length - 1];
for (var j = 0; j < i; ++j)
{
newList[j] = oldList[j];
}
for (var j = i + 1; j < oldList.Length; ++j)
{
newList[j - 1] = oldList[j];
}
Array.Copy(oldList, newList, i);
Array.Copy(oldList, i + 1, newList, i, oldList.Length - i - 1);
List = newList;
@ -636,16 +631,8 @@ public sealed class MultiComponentList
if (tile.ID == itemID && tile.Z == z)
{
var newTiles = new StaticTile[oldTiles.Length - 1];
for (var j = 0; j < i; ++j)
{
newTiles[j] = oldTiles[j];
}
for (var j = i + 1; j < oldTiles.Length; ++j)
{
newTiles[j - 1] = oldTiles[j];
}
Array.Copy(oldTiles, newTiles, i);
Array.Copy(oldTiles, i + 1, newTiles, i, oldTiles.Length - i - 1);
Tiles[vx][vy] = newTiles;
@ -663,16 +650,8 @@ public sealed class MultiComponentList
tile.OffsetZ == (short)z)
{
var newList = new MultiTileEntry[oldList.Length - 1];
for (var j = 0; j < i; ++j)
{
newList[j] = oldList[j];
}
for (var j = i + 1; j < oldList.Length; ++j)
{
newList[j - 1] = oldList[j];
}
Array.Copy(oldList, newList, i);
Array.Copy(oldList, i + 1, newList, i, oldList.Length - i - 1);
List = newList;

View file

@ -16,6 +16,7 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Server.Buffers;
namespace Server;
@ -34,10 +35,8 @@ public class TileList
}
TryResize(tiles.Length);
for (var i = 0; i < tiles.Length; ++i)
{
_tiles[Count++] = tiles[i];
}
tiles.CopyTo(_tiles.AsSpan(Count));
Count += tiles.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -46,43 +45,42 @@ public class TileList
public void Add(StaticTile tile)
{
TryResize(1);
_tiles[Count] = tile;
++Count;
_tiles[Count++] = tile;
}
public void Add(ushort id, byte x, byte y, sbyte z, short hue = 0)
{
TryResize(1);
ref var tile = ref _tiles[Count];
ref var tile = ref _tiles[Count++];
tile.m_ID = id;
tile.m_X = x;
tile.m_Y = y;
tile.m_Z = z;
tile.m_Hue = hue;
++Count;
}
public void Add(ushort id, sbyte z)
{
TryResize(1);
_tiles[Count].m_ID = id;
_tiles[Count].m_Z = z;
++Count;
ref var tile = ref _tiles[Count++];
tile.m_ID = id;
tile.m_X = 0;
tile.m_Y = 0;
tile.m_Z = z;
tile.m_Hue = 0;
}
private void TryResize(int length)
{
_tiles ??= new StaticTile[length];
_tiles ??= STArrayPool<StaticTile>.Shared.Rent(length);
if (Count + length > _tiles.Length)
var newLength = Count + length;
if (newLength > _tiles.Length)
{
var old = _tiles;
_tiles = new StaticTile[old.Length * 2];
for (var i = 0; i < old.Length; ++i)
{
_tiles[i] = old[i];
}
_tiles = STArrayPool<StaticTile>.Shared.Rent(newLength);
old.CopyTo(_tiles.AsSpan());
STArrayPool<StaticTile>.Shared.Return(old);
}
}
@ -93,9 +91,11 @@ public class TileList
return _emptyTiles;
}
Array.Resize(ref _tiles, Count);
var tiles = _tiles;
var tiles = new StaticTile[Count];
_tiles.AsSpan(0, Count).CopyTo(tiles);
// Cleanup
STArrayPool<StaticTile>.Shared.Return(_tiles);
_tiles = null;
Count = 0;

View file

@ -15,9 +15,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Server.Logging;
namespace Server;
@ -158,7 +160,6 @@ public class TileMatrix
public StaticTile[][][] EmptyStaticBlock => _emptyStaticBlock;
[MethodImpl(MethodImplOptions.Synchronized)]
public void SetStaticBlock(int x, int y, StaticTile[][][] value)
{
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
@ -173,7 +174,6 @@ public class TileMatrix
_staticPatches[x][y >> 5] |= 1 << (y & 0x1F);
}
[MethodImpl(MethodImplOptions.Synchronized)]
public StaticTile[][][] GetStaticBlock(int x, int y)
{
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight || DataStream == null || IndexStream == null)
@ -187,31 +187,28 @@ public class TileMatrix
if (tiles == null)
{
lock (_fileShare)
for (var i = 0; tiles == null && i < _fileShare.Count; ++i)
{
for (var i = 0; tiles == null && i < _fileShare.Count; ++i)
var shared = _fileShare[i];
lock (shared)
{
var shared = _fileShare[i];
lock (shared)
if (x < shared.BlockWidth && y < shared.BlockHeight)
{
if (x < shared.BlockWidth && y < shared.BlockHeight)
var theirTiles = shared._staticTiles[x];
if (theirTiles != null)
{
var theirTiles = shared._staticTiles[x];
tiles = theirTiles[y];
}
if (theirTiles != null)
if (tiles != null)
{
var theirBits = shared._staticPatches[x];
if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0)
{
tiles = theirTiles[y];
}
if (tiles != null)
{
var theirBits = shared._staticPatches[x];
if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0)
{
tiles = null;
}
tiles = null;
}
}
}
@ -226,44 +223,15 @@ public class TileMatrix
return tiles;
}
public StaticTile[] GetStaticTiles(int x, int y)
{
var tiles = GetStaticBlock(x >> 3, y >> 3);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.StaticTileEnumerable GetStaticTiles(int x, int y) => new(_map, new Point2D(x, y), includeMultis: false);
return tiles[x & 0x7][y & 0x7];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.StaticTileEnumerable GetStaticAndMultiTiles(int x, int y) => new(_map, new Point2D(x, y));
private readonly TileList m_TilesList = new();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.StaticTileEnumerable GetMultiTiles(int x, int y) => new(_map, new Point2D(x, y), false);
[MethodImpl(MethodImplOptions.Synchronized)]
public StaticTile[] GetStaticTiles(int x, int y, bool multis)
{
var tiles = GetStaticBlock(x >> 3, y >> 3);
if (!multis)
{
return tiles[x & 0x7][y & 0x7];
}
var any = false;
foreach (var multiTiles in _map.GetMultiTilesAt(x, y))
{
any = true;
m_TilesList.AddRange(multiTiles);
}
if (!any)
{
return tiles[x & 0x7][y & 0x7];
}
m_TilesList.AddRange(tiles[x & 0x7][y & 0x7]);
return m_TilesList.ToArray();
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void SetLandBlock(int x, int y, LandTile[] value)
{
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
@ -278,7 +246,6 @@ public class TileMatrix
_landPatches[x][y >> 5] |= 1 << (y & 0x1F);
}
[MethodImpl(MethodImplOptions.Synchronized)]
public LandTile[] GetLandBlock(int x, int y)
{
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight || MapStream == null)
@ -295,32 +262,26 @@ public class TileMatrix
return tiles;
}
lock (_fileShare)
for (var i = 0; tiles == null && i < _fileShare.Count; ++i)
{
for (var i = 0; tiles == null && i < _fileShare.Count; ++i)
var shared = _fileShare[i];
if (x < shared.BlockWidth && y < shared.BlockHeight)
{
var shared = _fileShare[i];
var theirTiles = shared._landTiles[x];
lock (shared)
if (theirTiles != null)
{
if (x < shared.BlockWidth && y < shared.BlockHeight)
tiles = theirTiles[y];
}
if (tiles != null)
{
var theirBits = shared._landPatches[x];
if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0)
{
var theirTiles = shared._landTiles[x];
if (theirTiles != null)
{
tiles = theirTiles[y];
}
if (tiles != null)
{
var theirBits = shared._landPatches[x];
if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0)
{
tiles = null;
}
}
tiles = null;
}
}
}
@ -344,7 +305,6 @@ public class TileMatrix
private StaticTile[] m_TileBuffer = new StaticTile[128];
[MethodImpl(MethodImplOptions.Synchronized)]
private unsafe StaticTile[][][] ReadStaticBlock(int x, int y)
{
try
@ -440,7 +400,6 @@ public class TileMatrix
throw new Exception("No assemblies were loaded, therefore we cannot load TileMatrix.");
}
[MethodImpl(MethodImplOptions.Synchronized)]
private unsafe LandTile[] ReadLandBlock(int x, int y)
{
try

View file

@ -15,7 +15,6 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
namespace Server;
@ -72,7 +71,6 @@ public class TileMatrixPatch
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
private unsafe int PatchLand(TileMatrix matrix, string dataPath, string indexPath)
{
using var fsData = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read);
@ -100,7 +98,6 @@ public class TileMatrixPatch
return count;
}
[MethodImpl(MethodImplOptions.Synchronized)]
private unsafe int PatchStatics(TileMatrix matrix, string dataPath, string indexPath, string lookupPath)
{
using var fsData = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read);

View file

@ -462,30 +462,10 @@ namespace Server.Engines.ConPVP
return;
}
var statics = Map.Tiles.GetStaticTiles(point.X, point.Y, true);
if (landTile.ID == 0x244 && statics.Length == 0) // 0x244 = invalid land tile
var foundStatics = false;
foreach (var t in Map.Tiles.GetStaticAndMultiTiles(point.X, point.Y))
{
var empty = true;
foreach (var item in Map.GetItemsAt(point))
{
if (item != this)
{
empty = false;
break;
}
}
if (empty)
{
HitObject(point, landTop, 0);
return;
}
}
for (var j = 0; j < statics.Length; j++)
{
var t = statics[j];
foundStatics = true;
var id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
height = id.CalcHeight;
@ -506,6 +486,25 @@ namespace Server.Engines.ConPVP
return;
}
}
if (landTile.ID == 0x244 && foundStatics) // 0x244 = invalid land tile
{
var empty = true;
foreach (var item in Map.GetItemsAt(point))
{
if (item != this)
{
empty = false;
break;
}
}
if (empty)
{
HitObject(point, landTop, 0);
return;
}
}
}
var rect = new Rectangle2D(pTop.X, pTop.Y, pBottom.X - pTop.X + 1, pBottom.Y - pTop.Y + 1);
@ -640,14 +639,10 @@ namespace Server.Engines.ConPVP
var myZ = Map?.GetAverageZ(X, Y) ?? 0;
var statics = Map?.Tiles?.GetStaticTiles(X, Y, true);
if (statics != null)
if (Map?.Tiles != null)
{
for (var j = 0; j < statics.Length; j++)
foreach (var t in Map.Tiles.GetStaticAndMultiTiles(X, Y))
{
var t = statics[j];
var id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
height = id.CalcHeight;

View file

@ -402,12 +402,10 @@ namespace Server.Engines.Craft
var vx = from.X + x;
var vy = from.Y + y;
var tiles = map.Tiles.GetStaticTiles(vx, vy, true);
for (var i = 0; i < tiles.Length; ++i)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(vx, vy))
{
var z = tiles[i].Z;
var id = tiles[i].ID;
var z = tile.Z;
var id = tile.ID;
if (z + 16 > from.Z && from.Z + 16 > z && Find(id, itemIDs))
{

View file

@ -67,19 +67,17 @@ public class DefBlacksmithy : CraftSystem
{
for (var y = -range; (!anvil || !forge) && y <= range; ++y)
{
var tiles = map.Tiles.GetStaticTiles(from.X + x, from.Y + y, true);
for (var i = 0; (!anvil || !forge) && i < tiles.Length; ++i)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(from.X + x, from.Y + y))
{
var id = tiles[i].ID;
var id = tile.ID;
var isAnvil = id is 4015 or 4016 or 11733 or 11734;
var isForge = id is 4017 or >= 6522 and <= 6569 or 11736;
if (isAnvil || isForge)
{
if (from.Z + 16 < tiles[i].Z || tiles[i].Z + 16 < from.Z ||
!from.InLOS(new Point3D(from.X + x, from.Y + y, tiles[i].Z + tiles[i].Height / 2 + 1)))
if (from.Z + 16 < tile.Z || tile.Z + 16 < from.Z ||
!from.InLOS(new Point3D(from.X + x, from.Y + y, tile.Z + tile.Height / 2 + 1)))
{
continue;
}

View file

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Server.Items;
using Server.Mobiles;
@ -177,15 +179,15 @@ namespace Server.Movement
return moveIsOk;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool CheckMovement(Mobile m, Direction d, out int newZ) => CheckMovement(m, m.Map, m.Location, d, out newZ);
private static bool IsOk(
bool ignoreDoors, bool ignoreSpellFields, int ourZ, int ourTop, StaticTile[] tiles, List<Item> items
bool ignoreDoors, bool ignoreSpellFields, int ourZ, int ourTop, Map map, int x, int y, List<Item> items
)
{
for (var i = 0; i < tiles.Length; ++i)
foreach (var check in map.Tiles.GetStaticAndMultiTiles(x, y))
{
var check = tiles[i];
var itemData = TileData.ItemTable[check.ID & TileData.MaxItemValue];
if (itemData.ImpassableSurface)
@ -247,7 +249,6 @@ namespace Server.Movement
var cantWalk = m.CantWalk;
var canSwim = m.CanSwim;
var tiles = map.Tiles.GetStaticTiles(x, y, true);
var landTile = map.Tiles.GetLandTile(x, y);
var flags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags;
var impassable = (flags & TileFlag.Impassable) != 0;
@ -269,9 +270,8 @@ namespace Server.Movement
int testTop;
for (var i = 0; i < tiles.Length; ++i)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y))
{
var tile = tiles[i];
var itemData = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
if (m.Flying && itemData.Name.InsensitiveEquals("hover over"))
@ -348,23 +348,14 @@ namespace Server.Movement
continue;
}
var landCheck = itemZ;
if (itemData.Height >= StepHeight)
{
landCheck += StepHeight;
}
else
{
landCheck += itemData.Height;
}
var landCheck = itemZ + Math.Min(itemData.Height, StepHeight);
if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ)
{
continue;
}
if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items))
if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, map, x, y, items))
{
newZ = ourZ;
moveIsOk = true;
@ -429,23 +420,14 @@ namespace Server.Movement
continue;
}
var landCheck = itemZ;
if (itemData.Height >= StepHeight)
{
landCheck += StepHeight;
}
else
{
landCheck += itemData.Height;
}
var landCheck = itemZ + Math.Min(itemData.Height, StepHeight);
if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ)
{
continue;
}
if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items))
if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, map, x, y, items))
{
newZ = ourZ;
moveIsOk = true;
@ -476,7 +458,7 @@ namespace Server.Movement
}
}
if (shouldCheck && IsOk(ignoreDoors, ignoreSpellFields, landCenter, testTop, tiles, items))
if (shouldCheck && IsOk(ignoreDoors, ignoreSpellFields, landCenter, testTop, map, x, y, items))
{
newZ = landCenter;
moveIsOk = true;
@ -498,6 +480,7 @@ namespace Server.Movement
return moveIsOk;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool CanMoveOver(Mobile m, Mobile t) =>
!t.Alive || !m.Alive || t.IsDeadBondedPet || m.IsDeadBondedPet || t.Hidden && t.AccessLevel > AccessLevel.Player;
@ -529,11 +512,8 @@ namespace Server.Movement
isSet = true;
}
var staticTiles = map.Tiles.GetStaticTiles(xCheck, yCheck, true);
for (var i = 0; i < staticTiles.Length; ++i)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(xCheck, yCheck))
{
var tile = staticTiles[i];
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
var calcTop = tile.Z + id.CalcHeight;
@ -599,38 +579,55 @@ namespace Server.Movement
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Offset(Direction d, ref int x, ref int y)
{
switch (d & Direction.Mask)
{
case Direction.North:
--y;
break;
{
--y;
break;
}
case Direction.South:
++y;
break;
{
++y;
break;
}
case Direction.West:
--x;
break;
{
--x;
break;
}
case Direction.East:
++x;
break;
{
++x;
break;
}
case Direction.Right:
++x;
--y;
break;
{
++x;
--y;
break;
}
case Direction.Left:
--x;
++y;
break;
{
--x;
++y;
break;
}
case Direction.Down:
++x;
++y;
break;
{
++x;
++y;
break;
}
case Direction.Up:
--x;
--y;
break;
{
--x;
--y;
break;
}
}
}
}

View file

@ -61,12 +61,8 @@ namespace Server.Engines.Spawners
return true;
}
var staticTiles = map.Tiles.GetStaticTiles(x, y, true);
for (var i = 0; i < staticTiles.Length; ++i)
foreach (var staticTile in map.Tiles.GetStaticAndMultiTiles(x, y))
{
var staticTile = staticTiles[i];
if (staticTile.Z == z && TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Wet)
{
return true;

View file

@ -217,11 +217,8 @@ namespace Server.Items
return false;
}
var tiles = map.Tiles.GetStaticTiles(x, y, true);
for (var i = 0; i < tiles.Length; ++i)
foreach (var t in map.Tiles.GetStaticAndMultiTiles(x, y))
{
var t = tiles[i];
var id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
if ((id.Flags & TileFlag.Wall) != 0 && z + 16 > t.Z && t.Z + t.Height > z)

View file

@ -328,13 +328,10 @@ public partial class InteriorDecorator : Item
return int.MinValue;
}
var tiles = map.Tiles.GetStaticTiles(item.X, item.Y, true);
var z = int.MinValue;
for (var i = 0; i < tiles.Length; ++i)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(item.X, item.Y))
{
var tile = tiles[i];
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
var top = tile.Z; // Confirmed : no height checks here

View file

@ -431,12 +431,8 @@ namespace Server
public static bool IsEastFrame(int x, int y, int z)
{
var tiles = m_Map.Tiles.GetStaticTiles(x, y);
for (var i = 0; i < tiles.Length; ++i)
foreach (var tile in m_Map.Tiles.GetStaticTiles(x, y))
{
var tile = tiles[i];
if (tile.Z == z && IsEastFrame(tile.ID))
{
return true;
@ -448,12 +444,8 @@ namespace Server
public static bool IsSouthFrame(int x, int y, int z)
{
var tiles = m_Map.Tiles.GetStaticTiles(x, y);
for (var i = 0; i < tiles.Length; ++i)
foreach (var tile in m_Map.Tiles.GetStaticTiles(x, y))
{
var tile = tiles[i];
if (tile.Z == z && IsSouthFrame(tile.ID))
{
return true;
@ -510,12 +502,8 @@ namespace Server
var vx = rx + region.X;
var vy = ry + region.Y;
var tiles = m_Map.Tiles.GetStaticTiles(vx, vy);
for (var i = 0; i < tiles.Length; ++i)
foreach (var tile in m_Map.Tiles.GetStaticTiles(vx, vy))
{
var tile = tiles[i];
var id = tile.ID;
var z = tile.Z;

View file

@ -1462,10 +1462,9 @@ namespace Server.Multis
}
var landTile = map.Tiles.GetLandTile(tx, ty);
var tiles = map.Tiles.GetStaticTiles(tx, ty, true);
var hasWater = landTile.Z == p.Z &&
(landTile.ID >= 168 && landTile.ID <= 171 || landTile.ID >= 310 && landTile.ID <= 311);
landTile.ID is >= 168 and <= 171 or >= 310 and <= 311;
// int z = p.Z;
@ -1476,9 +1475,8 @@ namespace Server.Multis
// if (!landTile.Ignored && top > landZ && landTop > z)
// return false;
for (var i = 0; i < tiles.Length; ++i)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(tx, ty))
{
var tile = tiles[i];
var isWater = tile.ID >= 0x1796 && tile.ID <= 0x17B2;
if (tile.Z == p.Z && isWater)

View file

@ -125,7 +125,7 @@ namespace Server.Multis
SetInitialState();
}
return m_Current.Components;
return m_Current!.Components;
}
}

View file

@ -137,8 +137,6 @@ namespace Server.Multis
var landTile = map.Tiles.GetLandTile(tileX, tileY);
var landID = landTile.ID & TileData.MaxLandValue;
var oldTiles = map.Tiles.GetStaticTiles(tileX, tileY, true);
items.Clear();
foreach (var item in map.GetItemsAt(tileX, tileY))
@ -202,13 +200,12 @@ namespace Server.Multis
hasSurface = true;
}
for (var j = 0; j < oldTiles.Length; ++j)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(tileX, tileY))
{
var oldTile = oldTiles[j];
var id = TileData.ItemTable[oldTile.ID & TileData.MaxItemValue];
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
if ((id.Impassable || id.Surface && !id.Background) &&
addTileTop > oldTile.Z && oldTile.Z + id.CalcHeight > addTileZ)
addTileTop > tile.Z && tile.Z + id.CalcHeight > addTileZ)
{
return HousePlacementResult.BadStatic; // Broke rule #2
}
@ -341,11 +338,8 @@ namespace Server.Multis
}
}
var tiles = map.Tiles.GetStaticTiles(borderPoint.X, borderPoint.Y, true);
for (var j = 0; j < tiles.Length; ++j)
foreach (var tile in map.Tiles.GetStaticAndMultiTiles(borderPoint.X, borderPoint.Y))
{
var tile = tiles[j];
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
if (id.Impassable || id.Surface && !id.Background && tile.Z + id.CalcHeight > center.Z + 2)

View file

@ -83,10 +83,6 @@ public static class IncomingTargetingPackets
}
else
{
var tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis);
var valid = false;
if (state.HighSeas)
{
var id = TileData.ItemTable[graphic & TileData.MaxItemValue];
@ -97,14 +93,19 @@ public static class IncomingTargetingPackets
}
int hue = 0;
var valid = false;
for (var i = 0; !valid && i < tiles.Length; ++i)
var eable = t.DisallowMultis
? map.Tiles.GetStaticTiles(x, y)
: map.Tiles.GetStaticAndMultiTiles(x, y);
foreach (var tile in eable)
{
var tile = tiles[i];
if (tile.Z == z && tile.ID == graphic)
{
valid = true;
hue = tile.Hue;
break;
}
}

View file

@ -77,11 +77,8 @@ namespace Server.Spells.Spellweaving
return true;
}
var tiles = map.Tiles.GetStaticTiles(location.X, location.Y); // Static Tiles
for (var i = 0; i < tiles.Length; ++i)
foreach (var t in map.Tiles.GetStaticTiles(location.X, location.Y))
{
var t = tiles[i];
var id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
var tand = t.ID;