diff --git a/Projects/Server/Items/Container.cs b/Projects/Server/Items/Container.cs index ab30aadef..846cea79d 100644 --- a/Projects/Server/Items/Container.cs +++ b/Projects/Server/Items/Container.cs @@ -23,6 +23,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Server.Network; +using Server.Utilities; +using QueuePool = Server.Utilities.RefPool>; namespace Server.Items { @@ -34,6 +36,7 @@ namespace Server.Items public class Container : Item { + private static QueuePool m_QueuePool = new QueuePool(QueueRef.Generate, preGenerateCount: 2, maxRefrenceRetention: 5); private static List m_FindItemsList = new List(); private ContainerData m_ContainerData; @@ -1512,63 +1515,68 @@ namespace Server.Items public List FindItemsByType(Predicate predicate) where T : Item => FindItemsByType(true, predicate); + /// + /// Performs a Breadth-First search through all the s and + /// nested s within this . + /// + /// Type of objects being searched for + /// Optional: If true, the search will recursively + /// check any nested s; otherwise, nested + /// s will not be searched. + /// Optional: A predicate to check if the + /// of type is one of the targets of the search. + /// A list of s of type that matche the optional . public List FindItemsByType(bool recurse = true, Predicate predicate = null) where T : Item { - List list = new List(); - RecurseFindItemsByType(this, recurse, list, predicate); - - return list; - } - - private static void RecurseFindItemsByType(Item current, bool recurse, List list, Predicate predicate) - where T : Item - { - if (current == null || current.Items.Count == 0) - return; - - List items = current.Items; - - for (int i = 0; i < items.Count; ++i) + using (var queue = m_QueuePool.Get()) { - Item item = items[i]; - - if (item is T typedItem) - if (predicate?.Invoke(typedItem) == true) - list.Add(typedItem); - - if (recurse && item is Container) - RecurseFindItemsByType(item, true, list, predicate); + queue.Enqueue(this); + var items = new List(); + while (queue.Count > 0) + { + var container = queue.Dequeue(); + foreach (var item in container.Items) + { + if (item is T typedItem && predicate?.Invoke(typedItem) != false) + items.Add(typedItem); + else if (recurse && item is Container itemContainer) + queue.Enqueue(itemContainer); + } + } + return items; } } - public T FindItemByType(bool recurse = true) where T : Item => RecurseFindItemByType(this, recurse); - private static T RecurseFindItemByType(Item current, bool recurse = true, Predicate predicate = null) where T : Item + /// + /// Performs a Breadth-First search through all the s and + /// nested s within this . + /// + /// Type of object being searched for + /// Optional: If true, the search will recursively + /// check any nested s; otherwise, nested + /// s will not be searched. + /// Optional: A predicate to check if the + /// of type is the target of the search. + /// The first of type that matches the optional . + public T FindItemByType(bool recurse = true, Predicate predicate = null) where T : Item { - if (current == null || current.Items.Count == 0) + using (var queue = m_QueuePool.Get()) + { + queue.Enqueue(this); + while (queue.Count > 0) + { + var container = queue.Dequeue(); + foreach (var item in container.Items) + { + if (item is T typedItem && predicate?.Invoke(typedItem) != false) + return typedItem; + if (recurse && item is Container itemContainer) + queue.Enqueue(itemContainer); + } + } return null; - - List list = current.Items; - - for (int i = 0; i < list.Count; ++i) - { - Item item = list[i]; - - if (item is T typedItem) - { - if (predicate?.Invoke(typedItem) == true) - return typedItem; - } - else if (recurse && item is Container) - { - T check = RecurseFindItemByType(item, true, predicate); - - if (check != null) - return check; - } } - - return null; } #endregion } diff --git a/Projects/Server/Utilities/RefPool.cs b/Projects/Server/Utilities/RefPool.cs new file mode 100644 index 000000000..f9f61cb12 --- /dev/null +++ b/Projects/Server/Utilities/RefPool.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; + +namespace Server.Utilities +{ + /// A resource reference object that can be disposed. + /// + /// Disposing the reference is expected to return itself back into the + /// original pool that created it. + /// + public interface IRef : IDisposable { } + + /// + /// Base implementation of the interface. + /// + /// + /// New implementations of should either derive from, or mirror + /// the functionality of this base implementation. + /// + /// + public abstract class BaseRef : IRef where TDerived : IRef + { + private RefPool m_Pool; + public BaseRef(RefPool pool) + { + m_Pool = pool; + } + protected abstract void OnDispose(); + public void Dispose() + { + OnDispose(); + m_Pool.Return((TDerived)(object)this); + } + } + + /// + /// A resource reference pool that manages a collection of reusable resources. + /// + /// The resource type the pool will contain. + public class RefPool where TRef : IRef + { + public delegate TRef Generator(RefPool targetPool); + public const int DEFAULT_RESOURCE_RETENTION = 10; + + private Stack m_Resources = new Stack(); + private Generator m_Generator; + private int m_MaxRefrenceRetention; + + /// + /// The maximum number of unused resources to hold in the pool. + /// + public int MaxRefrenceRetention + { + get => m_MaxRefrenceRetention; + set + { + m_MaxRefrenceRetention = value; + while (m_Resources.Count > value) m_Resources.Pop(); + } + } + + /// The generator function for creating new resources. + /// An amount of resources that should be pre-generated during initialization of the resource pool. + public RefPool(Generator generator, int preGenerateCount = 0, int maxRefrenceRetention = DEFAULT_RESOURCE_RETENTION) + { + if (generator == null) + throw new ArgumentNullException(nameof(generator)); + if (preGenerateCount > maxRefrenceRetention) + throw new IndexOutOfRangeException($"{nameof(preGenerateCount)} greater than {nameof(maxRefrenceRetention)}"); + m_Generator = generator; + m_MaxRefrenceRetention = maxRefrenceRetention; + while (--preGenerateCount >= 0) m_Resources.Push(generator(this)); + } + /// + /// Retrieves a resource reference that is managed by this . If the pool is has unused resources, + /// it will remove one from the pool and return it; otherwise, a new resource will be generated. + /// + /// Unused resource, or a new resource if no unused resources available. + public TRef Get() => m_Resources.TryPop(out TRef item) ? item : m_Generator(this); + /// + /// Returns a resource reference to the pool of unused resources. + /// + /// Resource to be returned. + public void Return(TRef queueRef) + { + if (m_Resources.Count < MaxRefrenceRetention) + m_Resources.Push(queueRef); + } + } + /// + public class QueueRef : Queue, IRef + { + private RefPool> m_Pool; + private QueueRef(RefPool> pool) { m_Pool = pool; } + /// Clears the queue and returns this resource to its parent resource pool. + public void Dispose() { Clear(); m_Pool.Return(this); } + /// + /// Generator function for creating instances of the resource. + /// + public static RefPool>.Generator Generate = (targetPool) => new QueueRef(targetPool); + } + /// + public class StackRef : Stack, IRef + { + private RefPool> m_Pool; + private StackRef(RefPool> pool) { m_Pool = pool; } + /// Clears the stack and returns this resource to its parent resource pool. + public void Dispose() { Clear(); m_Pool.Return(this); } + /// + /// Generator function for creating instances of the resource. + /// + public static RefPool>.Generator Generate = (targetPool) => new StackRef(targetPool); + } +}