/************************************************************************* * ModernUO * * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Container.Enumerable.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.Collections; namespace Server.Items; public partial class Container { /// /// Performs a breadth-first search through all the s and /// nested s within this . /// /// /// DO NOT consume, delete, or move items while iterating with any FindItemByType or FindItems overloads /// /// /// /// var total = 0; /// /// foreach (var gold in cont.FindItemsByType<Gold>()) /// { /// total += gold.Amount; /// } /// /// /// 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. /// /// /// An enumerator for iterating through s of type that match the optional /// . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public FindItemsByTypeEnumerator FindItemsByType(bool recurse = true, Predicate predicate = null) where T : Item => new(this, recurse, predicate); [MethodImpl(MethodImplOptions.AggressiveInlining)] public FindItemsByTypeEnumerator FindItemsByType(Type type, bool recurse = true) => new(this, recurse, type.IsInstanceOfType); [MethodImpl(MethodImplOptions.AggressiveInlining)] public FindItemsByTypeEnumerator FindItemsByType(Type[] types, bool recurse = true) => new(this, recurse, item => item.InTypeList(types)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public FindItemsByTypeEnumerator FindItems(bool recurse = true, Predicate predicate = null) => new(this, recurse, predicate); /// /// Safely enumerates items using a breadth-first search through all the s and /// nested s within this . /// /// /// Use EnumerateItemsByType for situations where the item might be manipulated, consumed, or moved. /// Note: This method scans through the container before returning the enumerator for iteration and therefore /// incurs a performance penalty from the overhead. /// /// /// /// foreach (var item in cont.EnumerateItemsByType<Item>()) /// { /// if (item.LootType is not LootType.Blessed) /// { /// item.Delete(); /// } /// } /// /// /// 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. /// /// /// An enumerator for iterating through s of type that match the optional /// . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefQueue EnumerateItemsByType(bool recurse = true, Predicate predicate = null) where T : Item { var queue = PooledRefQueue.Create(128); foreach (var item in FindItemsByType(recurse, predicate)) { queue.Enqueue(item); } return queue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefQueue EnumerateItemsByType(Type type, bool recurse = true) { var queue = PooledRefQueue.Create(128); foreach (var item in FindItemsByType(recurse)) { if (type.IsInstanceOfType(item)) { queue.Enqueue(item); } } return queue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefQueue EnumerateItemsByType(Type[] types, bool recurse = true) { var queue = PooledRefQueue.Create(128); foreach (var item in FindItemsByType(recurse)) { if (item.InTypeList(types)) { queue.Enqueue(item); } } return queue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefQueue EnumerateItems(bool recurse = true, Predicate predicate = null) => EnumerateItemsByType(recurse, predicate); public PooledRefList ListItemsByType(bool recurse = true, Predicate predicate = null) where T : Item { var list = PooledRefList.Create(128); foreach (var item in FindItemsByType(recurse, predicate)) { list.Add(item); } return list; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefList ListItemsByType(Type type, bool recurse = true) { var list = PooledRefList.Create(128); foreach (var item in FindItemsByType(recurse)) { if (type.IsInstanceOfType(item)) { list.Add(item); } } return list; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefList ListItemsByType(Type[] types, bool recurse = true) { var list = PooledRefList.Create(128); foreach (var item in FindItemsByType(recurse)) { if (item.InTypeList(types)) { list.Add(item); } } return list; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefList ListItems(bool recurse = true, Predicate predicate = null) => ListItemsByType(recurse, predicate); public ref struct FindItemsByTypeEnumerator where T : Item { private const string InvalidOperation_EnumFailedVersion = "Container was modified after enumerator was instantiated. Use Container.EnumerateItems method instead for safe enumerations."; private PooledRefQueue _containers; private Span _items; private int _index; private T _current; private readonly bool _recurse; private readonly Predicate _predicate; private Container _currentContainer; private int _version; public FindItemsByTypeEnumerator(Container container, bool recurse, Predicate predicate) { _containers = PooledRefQueue.Create(_recurse ? 64 : 0); if (container != null) { if (container.m_Items != null) { _items = CollectionsMarshal.AsSpan(container.m_Items); } _currentContainer = container; _version = container._version; } _current = default; _index = 0; _recurse = recurse; _predicate = predicate; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() => SetNextItem() || _recurse && SetNextContainer(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool SetNextContainer() { while (_containers.TryDequeue(out var c)) { _currentContainer = c; _items = CollectionsMarshal.AsSpan(c.m_Items); _index = 0; _version = c._version; if (SetNextItem()) { return true; } } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool SetNextItem() { if (_version != _currentContainer._version) { throw new InvalidOperationException(InvalidOperation_EnumFailedVersion); } while (_index < _items.Length) { Item item = _items[_index++]; if (_recurse && item is Container { m_Items.Count: > 0 } c) { _containers.Enqueue(c); } if (item is T t && _predicate?.Invoke(t) != false) { if (_version != _currentContainer._version) { throw new InvalidOperationException(InvalidOperation_EnumFailedVersion); } _current = t; return true; } } return false; } public T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _current; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() => _containers.Dispose(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public FindItemsByTypeEnumerator GetEnumerator() => this; } }