/************************************************************************* * ModernUO * * Copyright 2019-2026 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Item.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; public partial class Item { /// /// 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); [MethodImpl(MethodImplOptions.AggressiveInlining)] public FindItemsByTypeEnumerator FindItemsByType(ReadOnlySpan types, bool recurse = true) => new(this, recurse, 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. /// /// /// /// using var queue = cont.EnumerateItemsByType<Item>(); /// foreach (var item in queue) /// { /// 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(type, recurse)) { queue.Enqueue(item); } return queue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefQueue EnumerateItemsByType(ReadOnlySpan types, bool recurse = true) { var queue = PooledRefQueue.Create(128); foreach (var item in FindItemsByType(types, recurse)) { 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(type, recurse)) { list.Add(item); } return list; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefList ListItemsByType(ReadOnlySpan types, bool recurse = true) { var list = PooledRefList.Create(128); foreach (var item in FindItemsByType(types, recurse)) { 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 = "Item was modified after enumerator was instantiated. Use Item.EnumerateItems method instead for safe enumerations."; private PooledRefQueue _containers; private Span _items; private int _index; private T _current; private readonly bool _recurse; private Item _currentContainer; private int _version; // Exactly one filter source is used per enumerator instance, depending on // which constructor was called. The unused fields stay at default and the // branches below pick the right path. This avoids the per-call delegate // allocation that the (Type)/(Type[]) factory methods used to incur. private readonly Predicate _predicate; private readonly Type _runtimeType; private readonly ReadOnlySpan _runtimeTypes; public FindItemsByTypeEnumerator(Item container, bool recurse, Predicate predicate) : this(container, recurse) => _predicate = predicate; public FindItemsByTypeEnumerator(Item container, bool recurse, Type runtimeType) : this(container, recurse) => _runtimeType = runtimeType; public FindItemsByTypeEnumerator(Item container, bool recurse, ReadOnlySpan runtimeTypes) : this(container, recurse) => _runtimeTypes = runtimeTypes; private FindItemsByTypeEnumerator(Item container, bool recurse) { _recurse = recurse; _containers = PooledRefQueue.Create(recurse ? 64 : 0); if (container != null) { var items = container.LookupItems(); if (items != null) { _items = CollectionsMarshal.AsSpan(items); } _currentContainer = container; _version = container.LookupContainerVersion(); } } [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.LookupItems()); _index = 0; _version = c.LookupContainerVersion(); if (SetNextItem()) { return true; } } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool Matches(T t) { if (_runtimeType is not null) { return _runtimeType.IsInstanceOfType(t); } if (_runtimeTypes.Length > 0) { return t.GetType().InTypeList(_runtimeTypes); } return _predicate?.Invoke(t) != false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool SetNextItem() { if (_version != _currentContainer.LookupContainerVersion()) { throw new InvalidOperationException(InvalidOperation_EnumFailedVersion); } while (_index < _items.Length) { var item = _items[_index++]; if (_recurse && item.LookupItems() is { Count: > 0 }) { _containers.Enqueue(item); } if (item is T t && Matches(t)) { if (_version != _currentContainer.LookupContainerVersion()) { 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; } }