From ca3df9cfa793b7a68169d7645b896950c23bbb49 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 5 Nov 2023 08:17:34 -0800 Subject: [PATCH] fix: Adds multis to map iterators, fixes searching empty nested containers (#1581) ### Summary Eliminates `IPooledEnumerable` and `eable.Free()` from `Map` for multis. This drastically simplifies code that iterates in range, for example: ```cs foreach (var m in m.GetMultisInRange(5)) { } ``` The code above no longer requires an eable and calling `Free()`. ### Bug Fixes - [X] Fixes an issue with searching through nested empty containers. --- .../Items/BaseMulti.SectorMultiLinkList.cs | 893 ++++++++++-------- Projects/Server/Items/Container.Enumerable.cs | 13 +- Projects/Server/Maps/Map.MultiEnumerator.cs | 263 ++++++ .../Server/Maps/Map.MultiTileEnumerator.cs | 121 +++ Projects/Server/Maps/Map.cs | 28 +- Projects/Server/Maps/PooledEnumeration.cs | 298 ------ Projects/Server/TileList.cs | 78 +- Projects/Server/TileMatrix/TileMatrix.cs | 47 +- Projects/UOContent/Multis/Boats/BaseBoat.cs | 6 +- Projects/UOContent/Multis/Houses/BaseHouse.cs | 6 +- .../UOContent/Multis/Houses/HousePlacement.cs | 61 +- Projects/UOContent/Spells/Base/SpellHelper.cs | 6 +- 12 files changed, 960 insertions(+), 860 deletions(-) create mode 100644 Projects/Server/Maps/Map.MultiEnumerator.cs create mode 100644 Projects/Server/Maps/Map.MultiTileEnumerator.cs delete mode 100644 Projects/Server/Maps/PooledEnumeration.cs diff --git a/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs b/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs index 9a5c79ca8..973e1bf4c 100644 --- a/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs +++ b/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs @@ -15,430 +15,489 @@ using System; using System.Runtime.CompilerServices; +using Server.Collections; namespace Server.Items; // Adds support for the specific value link list on sectors for multis, separate from items -public partial class BaseMulti : BaseMulti.ISectorMultiLinkListNode +public partial class BaseMulti { - public interface ISectorMultiLinkListNode where T : class - { - public T SectorMultiNext { get; set; } - public T SectorMultiPrevious { get; set; } - public bool OnSectorMultiLinkList { get; set; } - } - - public struct SectorMultiLinkList where T : class, ISectorMultiLinkListNode - { - public int Count { get; internal set; } - public T First { get; internal set; } - public T Last { get; internal set; } - - public void Remove(T node) - { - if (node == null) - { - return; - } - - if (!node.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to remove a node that is not on the list."); - } - - if (node.SectorMultiPrevious == null) - { - // If previous is null, then it is the first element. - if (First != node) - { - throw new ArgumentException("Attempted to remove a node that is not on the list."); - } - - if (First == Last) - { - Last = null; - First = null; - } - else - { - First = node.SectorMultiNext; - } - - if (node.SectorMultiNext != null) - { - node.SectorMultiNext.SectorMultiPrevious = null; - } - } - else - { - node.SectorMultiPrevious.SectorMultiNext = node.SectorMultiNext; - - // If next is null, then it is the last element. - if (node.SectorMultiNext == null) - { - Last = node.SectorMultiPrevious; - } - else - { - node.SectorMultiNext.SectorMultiPrevious = node.SectorMultiPrevious; - } - } - - node.SectorMultiNext = null; - node.SectorMultiPrevious = null; - node.OnSectorMultiLinkList = false; - Count--; - } - - // Remove all entries before this node, not including this node. - public void RemoveAllBefore(T e) - { - if (e == null) - { - return; - } - - if (!e.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to remove nodes before a node that is not on the list."); - } - - if (e.SectorMultiPrevious == null) - { - return; - } - - var current = e.SectorMultiPrevious; - e.SectorMultiPrevious = null; - - while (current != null) - { - var previous = current.SectorMultiPrevious; - - current.OnSectorMultiLinkList = false; - current.SectorMultiNext = null; - current.SectorMultiPrevious = null; - Count--; - - current = previous; - } - - First = e; - } - - // Remove all entries after this node, not including this node. - public void RemoveAllAfter(T e) - { - if (e == null) - { - return; - } - - if (!e.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to remove nodes after a node that is not on the list."); - } - - if (e.SectorMultiNext == null) - { - return; - } - - var current = e.SectorMultiNext; - e.SectorMultiNext = null; - - while (current != null) - { - var next = current.SectorMultiNext; - - current.OnSectorMultiLinkList = false; - current.SectorMultiNext = null; - current.SectorMultiPrevious = null; - Count--; - - current = next; - } - - Last = e; - } - - public void AddLast(T e) - { - if (e == null) - { - return; - } - - if (e.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to add a node that is already on a list."); - } - - if (Last != null) - { - AddAfter(Last, e); - } - else - { - First = e; - Last = e; - Count++; - } - - e.OnSectorMultiLinkList = true; - } - - public void AddFirst(T e) - { - if (e == null) - { - return; - } - - if (e.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to add a node that is already on a list."); - } - - if (First != null) - { - AddBefore(First, e); - } - else - { - First = e; - Last = e; - Count++; - } - - e.OnSectorMultiLinkList = true; - } - - public void AddBefore(T existing, T node) - { - if (node == null) - { - return; - } - - ArgumentNullException.ThrowIfNull(existing); - - if (!existing.OnSectorMultiLinkList) - { - throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list."); - } - - if (node.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to add a node that is already on a list."); - } - - node.SectorMultiNext = existing; - node.SectorMultiPrevious = existing.SectorMultiPrevious; - - if (existing.SectorMultiPrevious != null) - { - existing.SectorMultiPrevious.SectorMultiNext = node; - } - else - { - First = node; - } - - existing.SectorMultiPrevious = node; - node.OnSectorMultiLinkList = true; - Count++; - } - - public void AddAfter(T existing, T node) - { - if (node == null) - { - return; - } - - ArgumentNullException.ThrowIfNull(existing); - - if (!existing.OnSectorMultiLinkList) - { - throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list."); - } - - if (node.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to add a node that is already on a list."); - } - - node.SectorMultiPrevious = existing; - node.SectorMultiNext = existing.SectorMultiNext; - - if (existing.SectorMultiNext != null) - { - existing.SectorMultiNext.SectorMultiPrevious = node; - } - else - { - Last = node; - } - - existing.SectorMultiNext = node; - node.OnSectorMultiLinkList = true; - Count++; - } - - public void RemoveAll() - { - var current = First; - while (current != null) - { - var next = current.SectorMultiNext; - - current.OnSectorMultiLinkList = false; - current.SectorMultiNext = null; - current.SectorMultiPrevious = null; - current = next; - } - - First = null; - Last = null; - Count = 0; - } - - public void AddLast(ref SectorMultiLinkList otherList, T start, T end) - { - // Should we check if start and end actually exist on the other list? - if (otherList.Count == 0 || otherList.Count == 1 && (start != end || otherList.First != start)) - { - throw new ArgumentException("Attempted to add nodes that are not on the specified linklist."); - } - - if (start.SectorMultiPrevious != null) - { - start.SectorMultiPrevious.SectorMultiNext = end.SectorMultiNext; - } - else - { - // Start is first - otherList.First = end.SectorMultiNext; - } - - if (end.SectorMultiNext != null) - { - end.SectorMultiNext.SectorMultiPrevious = start.SectorMultiPrevious; - } - else - { - otherList.Last = start.SectorMultiPrevious; - } - - var count = 1; - var current = start; - - // Assume start and end are in the right order, or bad things happen (crash). - while (current != end) - { - count++; - current = current.SectorMultiNext; - } - - otherList.Count -= count; - - if (Last != null) - { - Last.SectorMultiNext = start; - start.SectorMultiPrevious = Last; - } - else - { - First = start; - } - - Last = end; - Count += count; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SectorMultiListEnumerator GetEnumerator() => new(First); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public DescendingSectorMultiListEnumerator ByDescending() => new(Last); - - public ref struct SectorMultiListEnumerator - { - private T _head; - private T _current; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SectorMultiListEnumerator(T head) - { - _head = head; - _current = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - if (_current == null) - { - _current = _head; - _head = null; - } - else - { - _current = _current.SectorMultiNext; - } - - return _current != null; - } - - public T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _current; - } - } - - public ref struct DescendingSectorMultiListEnumerator - { - private T _tail; - private T _current; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public DescendingSectorMultiListEnumerator(T head) - { - _tail = head; - _current = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - if (_current == null) - { - _current = _tail; - _tail = null; - } - else - { - _current = _current.SectorMultiPrevious; - } - - return _current != null; - } - - public T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _current; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public DescendingSectorMultiListEnumerator GetEnumerator() => this; - } - } - // Sectors, specifically for multis public BaseMulti SectorMultiNext { get; set; } public BaseMulti SectorMultiPrevious { get; set; } public bool OnSectorMultiLinkList { get; set; } } + +public struct SectorMultiValueLinkList +{ + public int Count { get; internal set; } + internal BaseMulti _first; + internal BaseMulti _last; + + public int Version { get; private set; } + + public void Remove(BaseMulti node) + { + if (node == null) + { + return; + } + + if (!node.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to remove a node that is not on the list."); + } + + if (node.SectorMultiPrevious == null) + { + // If SectorMultiPrevious is null, then it is the first element. + if (_first != node) + { + throw new ArgumentException("Attempted to remove a node that is not on the list."); + } + + if (_first == _last) + { + _last = null; + _first = null; + } + else + { + _first = node.SectorMultiNext; + } + + if (node.SectorMultiNext != null) + { + node.SectorMultiNext.SectorMultiPrevious = null; + } + } + else + { + node.SectorMultiPrevious.SectorMultiNext = node.SectorMultiNext; + + // If SectorMultiNext is null, then it is the last element. + if (node.SectorMultiNext == null) + { + _last = node.SectorMultiPrevious; + } + else + { + node.SectorMultiNext.SectorMultiPrevious = node.SectorMultiPrevious; + } + } + + node.SectorMultiNext = null; + node.SectorMultiPrevious = null; + node.OnSectorMultiLinkList = false; + Count--; + Version++; + + if (Count < 0) + { + throw new Exception("Count is negative!"); + } + } + + // Remove all entries before this node, not including this node. + public void RemoveAllBefore(BaseMulti e) + { + if (e == null) + { + return; + } + + if (!e.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to remove nodes before a node that is not on the list."); + } + + if (e.SectorMultiPrevious == null) + { + return; + } + + var current = e.SectorMultiPrevious; + e.SectorMultiPrevious = null; + + while (current != null) + { + var SectorMultiPrevious = current.SectorMultiPrevious; + + current.OnSectorMultiLinkList = false; + current.SectorMultiNext = null; + current.SectorMultiPrevious = null; + Count--; + + if (Count < 0) + { + throw new Exception("Count is negative!"); + } + + current = SectorMultiPrevious; + } + + _first = e; + Version++; + } + + // Remove all entries after this node, not including this node. + public void RemoveAllAfter(BaseMulti e) + { + if (e == null) + { + return; + } + + if (!e.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to remove nodes after a node that is not on the list."); + } + + if (e.SectorMultiNext == null) + { + return; + } + + var current = e.SectorMultiNext; + e.SectorMultiNext = null; + + while (current != null) + { + var SectorMultiNext = current.SectorMultiNext; + + current.OnSectorMultiLinkList = false; + current.SectorMultiNext = null; + current.SectorMultiPrevious = null; + Count--; + + if (Count < 0) + { + throw new Exception("Count is negative!"); + } + + current = SectorMultiNext; + } + + _last = e; + Version++; + } + + public void AddLast(BaseMulti e) + { + if (e == null) + { + return; + } + + if (e.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + if (_last != null) + { + AddAfter(_last, e); + } + else + { + _first = e; + _last = e; + Count = 1; + Version++; + e.OnSectorMultiLinkList = true; + } + } + + public void AddFirst(BaseMulti e) + { + if (e == null) + { + return; + } + + if (e.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + if (_first != null) + { + AddBefore(_first, e); + } + else + { + _first = e; + _last = e; + Count = 1; + Version++; + e.OnSectorMultiLinkList = true; + } + } + + public void AddBefore(BaseMulti existing, BaseMulti node) + { + if (node == null) + { + return; + } + + ArgumentNullException.ThrowIfNull(existing); + + if (!existing.OnSectorMultiLinkList) + { + throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list."); + } + + if (node.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + node.SectorMultiNext = existing; + node.SectorMultiPrevious = existing.SectorMultiPrevious; + + if (existing.SectorMultiPrevious != null) + { + existing.SectorMultiPrevious.SectorMultiNext = node; + } + else + { + _first = node; + } + + existing.SectorMultiPrevious = node; + node.OnSectorMultiLinkList = true; + Count++; + Version++; + } + + public void AddAfter(BaseMulti existing, BaseMulti node) + { + if (node == null) + { + return; + } + + ArgumentNullException.ThrowIfNull(existing); + + if (!existing.OnSectorMultiLinkList) + { + throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list."); + } + + if (node.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + node.SectorMultiPrevious = existing; + node.SectorMultiNext = existing.SectorMultiNext; + + if (existing.SectorMultiNext != null) + { + existing.SectorMultiNext.SectorMultiPrevious = node; + } + else + { + _last = node; + } + + existing.SectorMultiNext = node; + node.OnSectorMultiLinkList = true; + Count++; + Version++; + } + + public void RemoveAll() + { + var current = _first; + while (current != null) + { + var SectorMultiNext = current.SectorMultiNext; + + current.OnSectorMultiLinkList = false; + current.SectorMultiNext = null; + current.SectorMultiPrevious = null; + current = SectorMultiNext; + } + + _first = null; + _last = null; + Count = 0; + Version++; + } + + public void AddLast(ref SectorMultiValueLinkList otherList, BaseMulti start, BaseMulti end) + { + // Should we check if start and end actually exist on the other list? + if (otherList.Count == 0 || otherList.Count == 1 && (start != end || otherList._first != start)) + { + throw new ArgumentException("Attempted to add nodes that are not on the specified linklist."); + } + + if (start.SectorMultiPrevious != null) + { + start.SectorMultiPrevious.SectorMultiNext = end.SectorMultiNext; + } + else + { + // Start is first + otherList._first = end.SectorMultiNext; + } + + if (end.SectorMultiNext != null) + { + end.SectorMultiNext.SectorMultiPrevious = start.SectorMultiPrevious; + } + else + { + otherList._last = start.SectorMultiPrevious; + } + + var count = 1; + var current = start; + + // Assume start and end are in the right order, or bad things happen (crash). + while (current != end) + { + count++; + current = current.SectorMultiNext; + } + + otherList.Count -= count; + + if (otherList.Count < 0) + { + throw new Exception("Count is negative!"); + } + + if (_last != null) + { + _last.SectorMultiNext = start; + start.SectorMultiPrevious = _last; + } + else + { + _first = start; + } + + _last = end; + Count += count; + Version++; + } + + public BaseMulti[] ToArray() + { + var arr = new BaseMulti[Count]; + + var index = 0; + foreach (var t in this) + { + arr[index++] = t; + } + + return arr; + } + + public ref struct SectorMultiValueListEnumerator + { + private bool _started; + private BaseMulti _current; + private ref readonly SectorMultiValueLinkList _linkList; + private int _version; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SectorMultiValueListEnumerator(in SectorMultiValueLinkList linkList) + { + _linkList = ref linkList; + _started = false; + _current = null; + _version = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + if (!_started) + { + _current = _linkList._first; + _started = true; + _version = _linkList.Version; + } + else if (_linkList.Version != _version) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + else + { + _current = _current.SectorMultiNext; + } + + return _current != null; + } + + public BaseMulti Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + } + + public ref struct DescendingSectorMultiValueListEnumerator + { + private bool _started; + private BaseMulti _current; + private ref readonly SectorMultiValueLinkList _linkList; + private int _version; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public DescendingSectorMultiValueListEnumerator(in SectorMultiValueLinkList linkList) + { + _linkList = ref linkList; + _started = false; + _current = null; + _version = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + if (!_started) + { + _current = _linkList._last; + _started = true; + _version = _linkList.Version; + } + else if (_linkList.Version != _version) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + else + { + _current = _current.SectorMultiPrevious; + } + + return _current != null; + } + + public BaseMulti Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public DescendingSectorMultiValueListEnumerator GetEnumerator() => this; + } +} + +public static class SectorMultiValueLinkListExt +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SectorMultiValueLinkList.SectorMultiValueListEnumerator GetEnumerator(this in SectorMultiValueLinkList linkList) + => new(in linkList); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SectorMultiValueLinkList.DescendingSectorMultiValueListEnumerator ByDescending(this in SectorMultiValueLinkList linkList) + => new(in linkList); +} diff --git a/Projects/Server/Items/Container.Enumerable.cs b/Projects/Server/Items/Container.Enumerable.cs index 6b55a1c11..3aeed3a01 100644 --- a/Projects/Server/Items/Container.Enumerable.cs +++ b/Projects/Server/Items/Container.Enumerable.cs @@ -161,14 +161,17 @@ public partial class Container [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool SetNextContainer() { - if (!_containers.TryDequeue(out var c)) + while (_containers.TryDequeue(out var c)) { - return false; + _items = CollectionsMarshal.AsSpan(c.m_Items); + _index = 0; + if (SetNextItem()) + { + return true; + } } - _items = CollectionsMarshal.AsSpan(c.m_Items); - _index = 0; - return SetNextItem(); + return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/Server/Maps/Map.MultiEnumerator.cs b/Projects/Server/Maps/Map.MultiEnumerator.cs new file mode 100644 index 000000000..9c3499ca7 --- /dev/null +++ b/Projects/Server/Maps/Map.MultiEnumerator.cs @@ -0,0 +1,263 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Map.MultiEnumerator.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 . * + *************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Server.Items; + +namespace Server; + +public partial class Map +{ + private static SectorMultiValueLinkList _emptyMultiLinkList = new(); + public static ref readonly SectorMultiValueLinkList EmptyMultiLinkList => ref _emptyMultiLinkList; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiAtEnumerable GetMultisAt(Point3D p) => GetMultisAt(p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiAtEnumerable GetMultisAt(Point3D p) where T : BaseMulti => GetMultisAt(new Point2D(p.X, p.Y)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiAtEnumerable GetMultisAt(int x, int y) => GetMultisAt(new Point2D(x, y)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiAtEnumerable GetMultisAt(int x, int y) where T : BaseMulti => GetMultisAt(new Point2D(x, y)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiAtEnumerable GetMultisAt(Point2D p) => GetMultisAt(p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiAtEnumerable GetMultisAt(Point2D p) where T : BaseMulti => new(this, p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInRange(Point3D p) => GetMultisInRange(p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInRange(Point3D p, int range) => GetMultisInRange(p, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInRange(Point3D p) where T : BaseMulti => GetMultisInRange(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInRange(Point3D p, int range) where T : BaseMulti => + GetMultisInRange(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInRange(Point2D p) => GetMultisInRange(p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInRange(Point2D p, int range) => GetMultisInRange(p, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInRange(Point2D p) where T : BaseMulti => GetMultisInRange(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInRange(Point2D p, int range) where T : BaseMulti => + GetMultisInRange(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInRange(int x, int y, int range) where T : BaseMulti => + GetMultisInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInBounds(Rectangle2D bounds) => GetMultisInBounds(bounds); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiBoundsEnumerable GetMultisInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : BaseMulti => + new(this, bounds, makeBoundsInclusive); + + public ref struct MultiAtEnumerable where T : BaseMulti + { + public static MultiAtEnumerable Empty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(); + } + + private readonly Map _map; + private readonly Point2D _location; + + public MultiAtEnumerable(Map map, Point2D loc) + { + _map = map; + _location = loc; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiAtEnumerator GetEnumerator() => new(_map, _location); + } + + public ref struct MultiAtEnumerator where T : BaseMulti + { + private Point2D _location; + private readonly Span _list; + private int _index; + private T _current; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiAtEnumerator(Map map, Point2D loc) + { + _location = loc; + _list = map == null + ? Span.Empty + : CollectionsMarshal.AsSpan(map.GetRealSector(loc.m_X, loc.m_Y).Multis); + + _index = 0; + _current = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + ref var loc = ref _location; + + while ((uint)_index < (uint)_list.Length) + { + var current = _list[_index++]; + if (current is T { Deleted: false } o && o.X == loc.m_X && o.Y == loc.m_Y) + { + _current = o; + return true; + } + } + + return false; + } + + public T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + } + + public ref struct MultiBoundsEnumerable where T : BaseMulti + { + public static MultiBoundsEnumerable Empty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(null, Rectangle2D.Empty, false); + } + + private Map _map; + private Rectangle2D _bounds; + private bool _makeBoundsInclusive; + + public MultiBoundsEnumerable(Map map, Rectangle2D bounds, bool makeBoundsInclusive) + { + _map = map; + _bounds = bounds; + _makeBoundsInclusive = makeBoundsInclusive; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiEnumerator GetEnumerator() => new(_map, _bounds, _makeBoundsInclusive); + } + + public ref struct MultiEnumerator where T : BaseMulti + { + private readonly Map _map; + private readonly int _sectorStartX; + private readonly int _sectorEndX; + private readonly int _sectorEndY; + private Rectangle2D _bounds; + + private int _currentSectorX; + private int _currentSectorY; + + private Span _list; + private T _current; + private int _index; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive) + { + _map = map; + _bounds = bounds; + + if (makeBoundsInclusive) + { + ++bounds.Width; + ++bounds.Height; + } + + _bounds = bounds; + + map.CalculateSectors(bounds, out _sectorStartX, out var _sectorStartY, out _sectorEndX, out _sectorEndY); + + // We start the X sector one short because it gets incremented immediately in MoveNext() + _currentSectorX = _sectorStartX - 1; + _currentSectorY = _sectorStartY; + _index = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool GetMulti() + { + ref Rectangle2D bounds = ref _bounds; + + while ((uint)_index < (uint)_list.Length) + { + var current = _list[_index++]; + if (current is T { Deleted: false } o && bounds.Contains(o.Location)) + { + _current = o; + return true; + } + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool GetSector() + { + var currentSectorX = _currentSectorX; + var currentSectorY = _currentSectorY; + var sectorEndX = _sectorEndX; + var sectorEndY = _sectorEndY; + + // Move to next sector + if (currentSectorX < sectorEndX) + { + _currentSectorX = ++currentSectorX; + } + else if (currentSectorY < sectorEndY) + { + _currentSectorX = currentSectorX = _sectorStartX; + _currentSectorY = ++currentSectorY; + } + else + { + // Ran out of sectors + return false; + } + + _list = CollectionsMarshal.AsSpan(_map.GetRealSector(currentSectorX, currentSectorY).Multis); + return GetMulti(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() => _map != null && (GetMulti() || GetSector()); + + public T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + } +} diff --git a/Projects/Server/Maps/Map.MultiTileEnumerator.cs b/Projects/Server/Maps/Map.MultiTileEnumerator.cs new file mode 100644 index 000000000..8d2cda3a2 --- /dev/null +++ b/Projects/Server/Maps/Map.MultiTileEnumerator.cs @@ -0,0 +1,121 @@ +/************************************************************************* + * 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 . * + *************************************************************************/ + +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 MultiAtEnumerator _multis; + private BaseMulti _currentMulti; + private StaticTile[] _current; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiTilesAtEnumerator(Map map, Point2D loc) + { + _multis = (map == null ? MultiAtEnumerable.Empty : map.GetMultisAt(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; + } + } +} diff --git a/Projects/Server/Maps/Map.cs b/Projects/Server/Maps/Map.cs index ef2dbea4a..f2a752c32 100644 --- a/Projects/Server/Maps/Map.cs +++ b/Projects/Server/Maps/Map.cs @@ -69,7 +69,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa m_Name = name; Rules = rules; Regions = new Dictionary(StringComparer.OrdinalIgnoreCase); - InvalidSector = new Sector(0, 0, this); + _invalidSector = new Sector(0, 0, this); m_SectorsWidth = width >> SectorShift; m_SectorsHeight = height >> SectorShift; m_Sectors = new Sector[m_SectorsWidth][]; @@ -123,7 +123,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public MapRules Rules { get; set; } - public Sector InvalidSector { get; } + private readonly Sector _invalidSector; public string Name { @@ -566,7 +566,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa for (var y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y) { var sect = GetRealSector(x, y); - if (sect != InvalidSector) + if (sect != _invalidSector) { sect.Activate(); } @@ -581,7 +581,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa for (var y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y) { var sect = GetRealSector(x, y); - if (sect != InvalidSector && !PlayersInRange(sect, SectorActiveRange)) + if (sect != _invalidSector && !PlayersInRange(sect, SectorActiveRange)) { sect.Deactivate(); } @@ -596,7 +596,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa for (var y = sect.Y - range; y <= sect.Y + range; ++y) { var check = GetRealSector(x, y); - if (check != InvalidSector && check.Clients.Count > 0) + if (check != _invalidSector && check.Clients.Count > 0) { return true; } @@ -690,7 +690,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa } } - public void RemoveMulti(BaseMulti m, Sector start, Sector end) + private void RemoveMulti(BaseMulti m, Sector start, Sector end) { if (this == Internal) { @@ -706,7 +706,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa } } - public void AddMulti(BaseMulti m, Sector start, Sector end) + private void AddMulti(BaseMulti m, Sector start, Sector end) { if (this == Internal) { @@ -862,9 +862,6 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return p; } - public IPooledEnumerable GetMultiTilesAt(int x, int y) => - PooledEnumeration.GetMultiTiles(this, new Rectangle2D(x, y, 1, 1)); - public bool CanFit( Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true, bool requireSurface = true @@ -1011,7 +1008,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return sec; } - return InvalidSector; + return _invalidSector; } public bool LineOfSight(Point3D org, Point3D dest) @@ -1408,13 +1405,12 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public class Sector { // TODO: Can we avoid this? - private static readonly List m_DefaultMultiList = new(); private static readonly List m_DefaultRectList = new(); private bool m_Active; private ValueLinkList _clients; private ValueLinkList _items; private ValueLinkList _mobiles; - private List _multis; + private List _multis = new(); private List _regions; public Sector(int x, int y, Map owner) @@ -1427,7 +1423,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public List Regions => _regions ?? m_DefaultRectList; - public List Multis => _multis ?? m_DefaultMultiList; + internal List Multis => _multis; internal ref ValueLinkList Mobiles => ref _mobiles; @@ -1553,12 +1549,12 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public void OnMultiEnter(BaseMulti multi) { - Utility.Add(ref _multis, multi); + _multis.Add(multi); } public void OnMultiLeave(BaseMulti multi) { - Utility.Remove(ref _multis, multi); + _multis.Remove(multi); } public void Activate() diff --git a/Projects/Server/Maps/PooledEnumeration.cs b/Projects/Server/Maps/PooledEnumeration.cs deleted file mode 100644 index 2ecfbcd1c..000000000 --- a/Projects/Server/Maps/PooledEnumeration.cs +++ /dev/null @@ -1,298 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: PooledEnumeration.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 . * - *************************************************************************/ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Server.Items; - -namespace Server; - -public interface IPooledEnumerable : IEnumerable, IDisposable -{ -} - -public static class PooledEnumeration -{ - public delegate IEnumerable Selector(Map.Sector sector, Rectangle2D bounds); - - static PooledEnumeration() - { - MultiSelector = SelectMultis; - MultiTileSelector = SelectMultiTiles; - } - - public static Selector MultiSelector { get; set; } - public static Selector MultiTileSelector { get; set; } - - public static IEnumerable SelectMultis(Map.Sector s, Rectangle2D bounds) - { - var entities = new List(s.Multis.Count); - for (int i = s.Multis.Count - 1; i >= 0; --i) - { - BaseMulti multi = s.Multis[i]; - if (multi is { Deleted: false } && bounds.Contains(multi.Location)) - { - entities.Add(multi); - } - } - return entities; - } - - public static IEnumerable SelectMultiTiles(Map.Sector s, Rectangle2D bounds) - { - for (int l = s.Multis.Count - 1; l >= 0; --l) - { - BaseMulti o = s.Multis[l]; - if (o?.Deleted != false) - { - continue; - } - - MultiComponentList c = o.Components; - - int x, y, xo, yo; - StaticTile[] t, r; - - for (x = bounds.Start.X; x < bounds.End.X; x++) - { - xo = x - (o.X + c.Min.X); - - if (xo < 0 || xo >= c.Width) - { - continue; - } - - for (y = bounds.Start.Y; y < bounds.End.Y; y++) - { - yo = y - (o.Y + c.Min.Y); - - if (yo < 0 || yo >= c.Height) - { - continue; - } - - t = c.Tiles[xo][yo]; - - if (t.Length <= 0) - { - continue; - } - - r = new StaticTile[t.Length]; - - for (var i = 0; i < t.Length; i++) - { - r[i] = t[i]; - r[i].Z += o.Z; - } - - yield return r; - } - } - } - } - - public static PooledEnumerable GetMultis(Map map, Rectangle2D bounds) => - PooledEnumerable.Instantiate(map, bounds, MultiSelector ?? SelectMultis); - - public static PooledEnumerable GetMultiTiles(Map map, Rectangle2D bounds) => - PooledEnumerable.Instantiate(map, bounds, MultiTileSelector ?? SelectMultiTiles); - - public static IEnumerable EnumerateSectors(Map map, Rectangle2D bounds) - { - if (map == null || map == Map.Internal) - { - yield break; - } - - var x1 = bounds.Start.X; - var y1 = bounds.Start.Y; - var x2 = bounds.End.X; - var y2 = bounds.End.Y; - - if (!Bound(map, ref x1, ref y1, ref x2, ref y2, out var xSector, out var ySector)) - { - yield break; - } - - var index = 0; - - while (NextSector(map, x1, y1, x2, y2, ref index, ref xSector, ref ySector, out var s)) - { - yield return s; - } - } - - public static bool Bound( - Map map, - ref int x1, - ref int y1, - ref int x2, - ref int y2, - out int xSector, - out int ySector - ) - { - if (map == null || map == Map.Internal) - { - xSector = ySector = 0; - return false; - } - - map.Bound(x1, y1, out x1, out y1); - map.Bound(x2 - 1, y2 - 1, out x2, out y2); - - x1 >>= Map.SectorShift; - y1 >>= Map.SectorShift; - x2 >>= Map.SectorShift; - y2 >>= Map.SectorShift; - - xSector = x1; - ySector = y1; - - return true; - } - - private static bool NextSector( - Map map, - int x1, - int y1, - int x2, - int y2, - ref int index, - ref int xSector, - ref int ySector, - out Map.Sector s - ) - { - if (map == null) - { - s = null; - xSector = ySector = 0; - return false; - } - - if (map == Map.Internal) - { - s = map.InvalidSector; - xSector = ySector = 0; - return false; - } - - if (index++ > 0) - { - if (++ySector > y2) - { - ySector = y1; - - if (++xSector > x2) - { - xSector = x1; - - s = map.InvalidSector; - return false; - } - } - } - - s = map.GetRealSector(xSector, ySector); - return true; - } - - public class NullEnumerable : IPooledEnumerable - { - public static readonly NullEnumerable Instance = new(); - - private readonly IEnumerable m_Empty = Enumerable.Empty(); - - IEnumerator IEnumerable.GetEnumerator() => m_Empty.GetEnumerator(); - - public IEnumerator GetEnumerator() => m_Empty.GetEnumerator(); - - public void Dispose() - { - } - } - - public sealed class PooledEnumerable : IPooledEnumerable - { - private static readonly Queue> _Buffer = new(0x400); - - private bool m_IsDisposed; - - private List m_Pool = new(0x40); - - public PooledEnumerable(IEnumerable pool) - { - m_Pool.AddRange(pool); - } - - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - - m_IsDisposed = true; - - m_Pool.Clear(); - m_Pool.Capacity = Math.Max(m_Pool.Capacity, 0x100); - - lock (((ICollection)_Buffer).SyncRoot) - { - _Buffer.Enqueue(this); - } - } - - ~PooledEnumerable() - { - Dispose(); - } - - IEnumerator IEnumerable.GetEnumerator() => m_Pool.GetEnumerator(); - - public IEnumerator GetEnumerator() => m_Pool.GetEnumerator(); - - -#pragma warning disable CA1000 // Do not declare static members on generic types - public static PooledEnumerable Instantiate( - Map map, Rectangle2D bounds, Selector selector - ) - { - PooledEnumerable e = null; - - lock (((ICollection)_Buffer).SyncRoot) - { - if (_Buffer.Count > 0) - { - e = _Buffer.Dequeue(); - } - } - - var pool = EnumerateSectors(map, bounds).SelectMany(s => selector(s, bounds)); - - if (e == null) - { - return new PooledEnumerable(pool); - } - - e.m_Pool.AddRange(pool); - return e; - } - } -} diff --git a/Projects/Server/TileList.cs b/Projects/Server/TileList.cs index 14ab7d7b9..f0c2d5cf2 100644 --- a/Projects/Server/TileList.cs +++ b/Projects/Server/TileList.cs @@ -1,3 +1,18 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: TileList.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 . * + *************************************************************************/ + using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -6,33 +21,17 @@ namespace Server; public class TileList { - private static readonly StaticTile[] m_EmptyTiles = Array.Empty(); - private StaticTile[] m_Tiles; - - public TileList() - { - m_Tiles = new StaticTile[8]; - Count = 0; - } + private static readonly StaticTile[] _emptyTiles = Array.Empty(); + private StaticTile[] _tiles; public int Count { get; private set; } - public void AddRange(StaticTile[] tiles) + public void AddRange(ReadOnlySpan tiles) { - if (Count + tiles.Length > m_Tiles.Length) - { - var old = m_Tiles; - m_Tiles = new StaticTile[(Count + tiles.Length) * 2]; - - for (var i = 0; i < old.Length; ++i) - { - m_Tiles[i] = old[i]; - } - } - + TryResize(tiles.Length); for (var i = 0; i < tiles.Length; ++i) { - m_Tiles[Count++] = tiles[i]; + _tiles[Count++] = tiles[i]; } } @@ -41,15 +40,15 @@ public class TileList public void Add(StaticTile tile) { - TryResize(); - m_Tiles[Count] = tile; + TryResize(1); + _tiles[Count] = tile; ++Count; } public void Add(ushort id, byte x, byte y, sbyte z, short hue = 0) { - TryResize(); - ref var tile = ref m_Tiles[Count]; + TryResize(1); + ref var tile = ref _tiles[Count]; tile.m_ID = id; tile.m_X = x; tile.m_Y = y; @@ -60,22 +59,24 @@ public class TileList public void Add(ushort id, sbyte z) { - TryResize(); - m_Tiles[Count].m_ID = id; - m_Tiles[Count].m_Z = z; + TryResize(1); + _tiles[Count].m_ID = id; + _tiles[Count].m_Z = z; ++Count; } - private void TryResize() + private void TryResize(int length) { - if (Count + 1 > m_Tiles.Length) + _tiles ??= new StaticTile[length]; + + if (Count + length > _tiles.Length) { - var old = m_Tiles; - m_Tiles = new StaticTile[old.Length * 2]; + var old = _tiles; + _tiles = new StaticTile[old.Length * 2]; for (var i = 0; i < old.Length; ++i) { - m_Tiles[i] = old[i]; + _tiles[i] = old[i]; } } } @@ -84,16 +85,13 @@ public class TileList { if (Count == 0) { - return m_EmptyTiles; + return _emptyTiles; } - var tiles = new StaticTile[Count]; - - for (var i = 0; i < Count; ++i) - { - tiles[i] = m_Tiles[i]; - } + Array.Resize(ref _tiles, Count); + var tiles = _tiles; + _tiles = null; Count = 0; return tiles; diff --git a/Projects/Server/TileMatrix/TileMatrix.cs b/Projects/Server/TileMatrix/TileMatrix.cs index e6d4c1e15..1e7ec7a7b 100644 --- a/Projects/Server/TileMatrix/TileMatrix.cs +++ b/Projects/Server/TileMatrix/TileMatrix.cs @@ -240,38 +240,27 @@ public class TileMatrix { var tiles = GetStaticBlock(x >> 3, y >> 3); - if (multis) + if (!multis) { - var eable = _map.GetMultiTilesAt(x, y); - - if (eable == PooledEnumeration.NullEnumerable.Instance) - { - return tiles[x & 0x7][y & 0x7]; - } - - var any = false; - - foreach (var multiTiles in eable) - { - if (!any) - { - 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(); + return tiles[x & 0x7][y & 0x7]; } - 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)] diff --git a/Projects/UOContent/Multis/Boats/BaseBoat.cs b/Projects/UOContent/Multis/Boats/BaseBoat.cs index 2a8a1e81a..d787e166c 100644 --- a/Projects/UOContent/Multis/Boats/BaseBoat.cs +++ b/Projects/UOContent/Multis/Boats/BaseBoat.cs @@ -243,11 +243,9 @@ namespace Server.Multis public static BaseBoat FindBoatAt(Point3D loc, Map map) { - var sector = map.GetSector(loc); - - for (var i = 0; i < sector.Multis.Count; i++) + foreach (var boat in map.GetMultisAt(loc)) { - if (sector.Multis[i] is BaseBoat boat && boat.Contains(loc.X, loc.Y)) + if (boat.Contains(loc.X, loc.Y)) { return boat; } diff --git a/Projects/UOContent/Multis/Houses/BaseHouse.cs b/Projects/UOContent/Multis/Houses/BaseHouse.cs index 40557a251..c97d580e9 100644 --- a/Projects/UOContent/Multis/Houses/BaseHouse.cs +++ b/Projects/UOContent/Multis/Houses/BaseHouse.cs @@ -1401,11 +1401,9 @@ namespace Server.Multis return null; } - var sector = map.GetSector(loc); - - for (var i = 0; i < sector.Multis.Count; ++i) + foreach (var house in map.GetMultisAt(loc)) { - if (sector.Multis[i] is BaseHouse house && house.IsInside(loc, height)) + if (house.IsInside(loc, height)) { return house; } diff --git a/Projects/UOContent/Multis/Houses/HousePlacement.cs b/Projects/UOContent/Multis/Houses/HousePlacement.cs index b78af2a4f..61d568580 100644 --- a/Projects/UOContent/Multis/Houses/HousePlacement.cs +++ b/Projects/UOContent/Multis/Houses/HousePlacement.cs @@ -370,58 +370,35 @@ namespace Server.Multis } } - var _sectors = new List(); - var _houses = new List(); - for (var i = 0; i < yard.Count; i++) { - var sector = map.GetSector(yard[i]); + var yardPoint = yard[i]; - if (!_sectors.Contains(sector)) + foreach (var house in map.GetMultisAt(yardPoint)) { - _sectors.Add(sector); - - for (var j = 0; j < sector.Multis?.Count; j++) - { - if (sector.Multis[j] is BaseHouse) - { - var _house = (BaseHouse)sector.Multis[j]; - if (!_houses.Contains(_house)) - { - _houses.Add(_house); - } - } - } - } - } - - for (var i = 0; i < yard.Count; ++i) - { - foreach (var b in _houses) - { - if (b.Contains(yard[i])) + if (house.Contains(yard[i])) { return HousePlacementResult.BadStatic; // Broke rule #3 } } } - /*Point2D yardPoint = yard[i]; - IPooledEnumerable eable = map.GetMultiTilesAt( yardPoint.X, yardPoint.Y ); - - foreach ( StaticTile[] tile in eable ) - { - for ( int j = 0; j < tile.Length; ++j ) - { - if ((TileData.ItemTable[tile[j].ID & TileData.MaxItemValue].Flags & (TileFlag.Impassable | TileFlag.Surface)) != 0) - { - eable.Free(); - return HousePlacementResult.BadStatic; // Broke rule #3 - } - } - } - - eable.Free();*/ + // TODO: Should we check for MultiTilesAt each yard point? + // for (var i = 0; i < yard.Count; i++) + // { + // var yardPoint = yard[i]; + // + // foreach (var tiles in map.GetMultiTilesAt(yardPoint)) + // { + // for (int j = 0; j < tiles.Length; ++j) + // { + // if ((TileData.ItemTable[tiles[j].ID & TileData.MaxItemValue].Flags & (TileFlag.Impassable | TileFlag.Surface)) != 0) + // { + // return HousePlacementResult.BadStatic; // Broke rule #3 + // } + // } + // } + // } return HousePlacementResult.Valid; } diff --git a/Projects/UOContent/Spells/Base/SpellHelper.cs b/Projects/UOContent/Spells/Base/SpellHelper.cs index 998114717..b0b50912f 100644 --- a/Projects/UOContent/Spells/Base/SpellHelper.cs +++ b/Projects/UOContent/Spells/Base/SpellHelper.cs @@ -158,12 +158,8 @@ namespace Server.Spells return false; } - var sector = map.GetSector(p.X, p.Y); - - for (var i = 0; i < sector.Multis.Count; ++i) + foreach (var multi in map.GetMultisAt(p)) { - var multi = sector.Multis[i]; - if (multi is BaseHouse bh) { if (houses && bh.IsInside(p, 16) || housingrange > 0 && bh.InRange(p, housingrange))