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

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