From c2d65789558232fbece90a79a91313158394b1bc Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sat, 14 Oct 2023 19:43:10 -0700 Subject: [PATCH] feat: Adds Next/Prev for future link list support (#1546) --- .../Items/BaseMulti.SectorMultiLinkList.cs | 444 ++++++++++++++++++ Projects/Server/Items/BaseMulti.cs | 17 +- Projects/Server/Items/Item.cs | 8 +- Projects/Server/Mobiles/Mobile.cs | 8 +- Projects/Server/Network/NetState/NetState.cs | 8 +- Projects/Server/Regions/Region.cs | 13 +- 6 files changed, 493 insertions(+), 5 deletions(-) create mode 100644 Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs diff --git a/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs b/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs new file mode 100644 index 000000000..9a5c79ca8 --- /dev/null +++ b/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs @@ -0,0 +1,444 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: BaseMulti.SectorMultiLinkList.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; + +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 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; } +} diff --git a/Projects/Server/Items/BaseMulti.cs b/Projects/Server/Items/BaseMulti.cs index a6d8de065..4de43e03d 100644 --- a/Projects/Server/Items/BaseMulti.cs +++ b/Projects/Server/Items/BaseMulti.cs @@ -1,9 +1,24 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: BaseMulti.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; namespace Server.Items; -public abstract class BaseMulti : Item +public abstract partial class BaseMulti : Item { public BaseMulti(int itemID) : base(itemID) => Movable = false; diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 14984747a..03a8b35d9 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using Server.Collections; using Server.ContextMenus; using Server.Items; using Server.Logging; @@ -175,7 +176,7 @@ public enum ExpandFlag Spawner = 0x100 } -public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEntity +public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEntity, IValueLinkListNode { private static readonly ILogger logger = LogFactory.GetLogger(typeof(Item)); @@ -253,6 +254,11 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt } } + // Sectors + public Item Next { get; set; } + public Item Previous { get; set; } + public bool OnLinkList { get; set; } + /// /// The who is currently holding this item. /// diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index f8e3489fc..5a8940dd3 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using Server.Accounting; +using Server.Collections; using Server.ContextMenus; using Server.Guilds; using Server.Gumps; @@ -180,7 +181,7 @@ public delegate int AOSStatusHandler(Mobile from, int index); /// /// Base class representing players, npcs, and creatures. /// -public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPropertyListEntity +public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPropertyListEntity, IValueLinkListNode { // Allow four warmode changes in 0.5 seconds, any more will be delay for two seconds private const int WarmodeCatchCount = 4; @@ -362,6 +363,11 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro DamageEntries = new List(); } + // Sectors + public Mobile Next { get; set; } + public Mobile Previous { get; set; } + public bool OnLinkList { get; set; } + public static bool DragEffects { get; set; } = true; [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs index 1901df707..eb4895c11 100755 --- a/Projects/Server/Network/NetState/NetState.cs +++ b/Projects/Server/Network/NetState/NetState.cs @@ -23,6 +23,7 @@ using System.Network; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Server.Accounting; +using Server.Collections; using Server.Diagnostics; using Server.Gumps; using Server.HuePickers; @@ -37,7 +38,7 @@ public delegate void NetStateCreatedCallback(NetState ns); public delegate void DecodePacket(Span buffer, ref int length); public delegate int EncodePacket(ReadOnlySpan inputBuffer, Span outputBuffer); -public partial class NetState : IComparable +public partial class NetState : IComparable, IValueLinkListNode { private static readonly ILogger logger = LogFactory.GetLogger(typeof(NetState)); @@ -155,6 +156,11 @@ public partial class NetState : IComparable CreatedCallback?.Invoke(this); } + // Sectors + public NetState Next { get; set; } + public NetState Previous { get; set; } + public bool OnLinkList { get; set; } + // Only use this for debugging. This will make your server very slow! public bool PacketLogging { diff --git a/Projects/Server/Regions/Region.cs b/Projects/Server/Regions/Region.cs index f36b895cb..07dd9f390 100644 --- a/Projects/Server/Regions/Region.cs +++ b/Projects/Server/Regions/Region.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Server.Collections; using Server.Json; using Server.Logging; using Server.Network; @@ -117,7 +118,7 @@ public enum MusicName NoMusic = 0x1FFF } -public class Region : IComparable +public class Region : IComparable, IValueLinkListNode { private static readonly ILogger logger = LogFactory.GetLogger(typeof(Region)); @@ -175,6 +176,16 @@ public class Region : IComparable } } + // Sectors + [JsonIgnore] + public Region Next { get; set; } + + [JsonIgnore] + public Region Previous { get; set; } + + [JsonIgnore] + public bool OnLinkList { get; set; } + // Used during deserialization only public Expansion MinExpansion { get; set; } = Expansion.None;