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;