From 68f3e15e5b9c4f11ed1cec0d311064213b437210 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:20:16 -0700 Subject: [PATCH] fix: Cleans up doors and house placement (#1560) --- Projects/Server/Maps/Map.ItemEnumerator.cs | 51 ++++++- Projects/Server/Maps/Map.cs | 1 + .../Items/Construction/Doors/BaseDoor.cs | 140 ++++++++++-------- .../UOContent/Multis/Houses/HousePlacement.cs | 17 +-- 4 files changed, 136 insertions(+), 73 deletions(-) diff --git a/Projects/Server/Maps/Map.ItemEnumerator.cs b/Projects/Server/Maps/Map.ItemEnumerator.cs index ba1147bc7..5be8c34a7 100644 --- a/Projects/Server/Maps/Map.ItemEnumerator.cs +++ b/Projects/Server/Maps/Map.ItemEnumerator.cs @@ -21,7 +21,7 @@ namespace Server; public partial class Map { private int _iteratingItems; - private List<(MapAction, Point3D, Item)> _delayedItemActions = new(); + private readonly List<(MapAction, Point3D, Item)> _delayedItemActions = new(); public bool IsIteratingItems { @@ -29,15 +29,58 @@ public partial class Map get => _iteratingItems > 0; } - public ItemEnumerable GetItemsInRange(Point3D p) => GetItemsInRange(p, Core.GlobalMaxUpdateRange); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsAt(Point3D p) => GetItemsInRange(p, 0); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsAt(Point3D p) where T : Item => GetItemsInRange(p, 0); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsInRange(Point3D p) => GetItemsInRange(p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ItemEnumerable GetItemsInRange(Point3D p, int range) => GetItemsInRange(p, range); - public ItemEnumerable GetItemsInRange(Point3D p, int range) where T : Item => - GetItemsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsInRange(Point3D p) where T : Item => GetItemsInRange(p, Core.GlobalMaxUpdateRange); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsInRange(Point3D p, int range) where T : Item => + GetItemsInRange(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsAt(Point2D p) => GetItemsInRange(p, 0); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsAt(Point2D p) where T : Item => GetItemsInRange(p, 0); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsInRange(Point2D p) => GetItemsInRange(p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsInRange(Point2D p, int range) => GetItemsInRange(p, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsInRange(Point2D p) where T : Item => GetItemsInRange(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsInRange(Point2D p, int range) where T : Item => + GetItemsInRange(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsAt(int x, int y) => GetItemsAt(x, y); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsAt(int x, int y) where T : Item => GetItemsInRange(x, y, 0); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemEnumerable GetItemsInRange(int x, int y, int range) where T : Item => + GetItemsInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ItemEnumerable GetItemsInBounds(Rectangle2D bounds) => GetItemsInBounds(bounds); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ItemEnumerable GetItemsInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : Item => new(this, bounds, makeBoundsInclusive); diff --git a/Projects/Server/Maps/Map.cs b/Projects/Server/Maps/Map.cs index 30ccecc61..1c63469fa 100644 --- a/Projects/Server/Maps/Map.cs +++ b/Projects/Server/Maps/Map.cs @@ -535,6 +535,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return new Point2D(x, y); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CalculateSectors( Rectangle2D bounds, out int sectorStartX, out int sectorStartY, diff --git a/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs b/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs index d9b6ab406..bae1dc5ef 100644 --- a/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs +++ b/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using ModernUO.Serialization; using Server.Targeting; @@ -226,12 +227,13 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable private static void EventSink_OpenDoorMacroUsed(Mobile m) { - if (m.Map == null) + if (m.Map == null || !m.CheckAlive()) { return; } - int x = m.X, y = m.Y; + int x = m.X; + int y = m.Y; switch (m.Direction & Direction.Mask) { @@ -281,18 +283,13 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable } } - var sector = m.Map.GetSector(x, y); - - foreach (var item in sector.Items) + foreach (var item in m.Map.GetItemsAt(x, y)) { - if (item.Location.X == x && item.Location.Y == y && item.Z + item.ItemData.Height > m.Z && + if (item.Z + item.ItemData.Height > m.Z && m.Z + 16 > item.Z && item is BaseDoor && m.CanSee(item) && m.InLOS(item)) { - if (m.CheckAlive()) - { - m.SendLocalizedMessage(500024); // Opening door... - item.OnDoubleClick(m); - } + m.SendLocalizedMessage(500024); // Opening door... + item.OnDoubleClick(m); break; } @@ -320,7 +317,7 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable return CheckFit(map, p, 16); } - private bool CheckFit(Map map, Point3D p, int height) + private static bool CheckFit(Map map, Point3D p, int height) { if (map == Map.Internal) { @@ -331,13 +328,9 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable var y = p.Y; var z = p.Z; - var sector = map.GetSector(x, y); - var mobs = sector.Mobiles; - - foreach (var item in sector.Items) + foreach (var item in map.GetItemsAt(x, y)) { - if (item is not BaseMulti && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(x, y) && - item is not BaseDoor) + if (item.ItemID <= TileData.MaxItemValue && item is not (BaseMulti or BaseDoor)) { var id = item.ItemData; var surface = id.Surface; @@ -350,6 +343,8 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable } } + var mobs = map.GetSector(x, y).Mobiles; + for (var i = 0; i < mobs.Count; ++i) { var m = mobs[i]; @@ -376,19 +371,7 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable return true; } - public List GetChain() - { - var list = new List(); - var c = this; - - do - { - list.Add(c); - c = c.Link; - } while (c?.Deleted == false && !list.Contains(c)); - - return list; - } + public ChainEnumerable GetChain() => new(this); public bool IsFreeToClose() { @@ -397,16 +380,15 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable return CanClose(); } - var list = GetChain(); - - var freeToClose = true; - - for (var i = 0; freeToClose && i < list.Count; ++i) + foreach (var link in GetChain()) { - freeToClose = list[i].CanClose(); + if (!link.CanClose()) + { + return false; + } } - return freeToClose; + return true; } public virtual bool IsInside(Mobile from) => false; @@ -419,28 +401,18 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable { if (from.AccessLevel >= AccessLevel.GameMaster) { - from.LocalOverheadMessage( - MessageType.Regular, - 0x3B2, - 502502 - ); // That is locked, but you open it with your godly powers. - // from.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, 0x3B2, 3, 502502, "", "" ) ); // That is locked, but you open it with your godly powers. + // That is locked, but you open it with your godly powers. + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 502502); } else if (Key.ContainsKey(from.Backpack, KeyValue)) { - from.LocalOverheadMessage( - MessageType.Regular, - 0x3B2, - 501282 - ); // You quickly unlock, open, and relock the door + // You quickly unlock, open, and relock the door + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 501282); } else if (IsInside(from)) { - from.LocalOverheadMessage( - MessageType.Regular, - 0x3B2, - 501280 - ); // That is locked, but is usable from the inside. + // That is locked, but is usable from the inside. + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 501280); } else { @@ -475,11 +447,9 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable { var open = !_open; - var list = GetChain(); - - for (var i = 0; i < list.Count; ++i) + foreach (var link in GetChain()) { - list[i].Open = open; + link.Open = open; } } else @@ -540,4 +510,58 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable } } } + + public ref struct ChainEnumerable + { + private readonly BaseDoor _door; + + public ChainEnumerable(BaseDoor door) => _door = door; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ChainEnumerator GetEnumerator() => new(_door); + } + + public ref struct ChainEnumerator + { + private BaseDoor _door; + private BaseDoor _current; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ChainEnumerator(BaseDoor door) + { + _door = door; + _current = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + bool valid; + + if (_current == null) + { + _current = _door; + valid = _current != null; + } + else + { + _current = _current.Link; + valid = _current?.Deleted == false && _current != _door; + } + + if (!valid) + { + _door = null; + _current = null; + } + + return valid; + } + + public BaseDoor Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + } } diff --git a/Projects/UOContent/Multis/Houses/HousePlacement.cs b/Projects/UOContent/Multis/Houses/HousePlacement.cs index 9144cabaa..206a3cd62 100644 --- a/Projects/UOContent/Multis/Houses/HousePlacement.cs +++ b/Projects/UOContent/Multis/Houses/HousePlacement.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Server.Collections; using Server.Regions; using Server.Spells; @@ -80,7 +81,7 @@ namespace Server.Multis var start = new Point3D(center.X + mcl.Min.X, center.Y + mcl.Min.Y, center.Z); // These are storage lists. They hold items and mobiles found in the map for further processing - var items = new List(); + using var items = PooledRefList.Create(); var mobiles = new List(); // These are also storage lists. They hold location values indicating the yard and border locations. @@ -138,11 +139,9 @@ namespace Server.Multis var oldTiles = map.Tiles.GetStaticTiles(tileX, tileY, true); - var sector = map.GetSector(tileX, tileY); - items.Clear(); - foreach (var item in sector.Items) + foreach (var item in map.GetItemsAt(tileX, tileY)) { if (item.Visible && item.X == tileX && item.Y == tileY) { @@ -152,6 +151,8 @@ namespace Server.Multis mobiles.Clear(); + var sector = map.GetSector(tileX, tileY); + for (var i = 0; i < sector.Mobiles.Count; ++i) { var m = sector.Mobiles[i]; @@ -220,9 +221,8 @@ namespace Server.Multis hasSurface = true;*/ } - for (var j = 0; j < items.Count; ++j) + foreach (var item in items) { - var item = items[j]; var id = item.ItemData; if (addTileTop > item.Z && item.Z + id.CalcHeight > addTileZ) @@ -236,11 +236,6 @@ namespace Server.Multis return HousePlacementResult.BadItem; // Broke rule #2 } } - - /*else if (isFoundation && !hasSurface && (id.Flags & TileFlag.Surface) != 0 && (item.Z + id.CalcHeight) == center.Z) - { - hasSurface = true; - }*/ } if (isFoundation && !hasSurface)