/************************************************************************* * 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 { [MethodImpl(MethodImplOptions.AggressiveInlining)] public FindItemsByTypeEnumerator FindItems(bool recurse = true, Predicate predicate = null) => FindItemsByType(recurse, predicate); /// /// Performs a breadth-first search through all the s and /// nested s within this . /// /// /// DO NOT consume, delete, or move items while iterating /// /// /// /// 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 QueuedItemsEnumerator EnumerateItems(bool recurse = true, Predicate predicate = null) => EnumerateItemsByType(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. /// /// /// /// 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 QueuedItemsEnumerator EnumerateItemsByType(bool recurse = true, Predicate predicate = null) where T : Item => new(QueueItemsByType(recurse, predicate)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefQueue QueueItems(bool recurse = true, Predicate predicate = null) => QueueItemsByType(recurse, predicate); public PooledRefQueue QueueItemsByType(bool recurse = true, Predicate predicate = null) where T : Item { var queue = PooledRefQueue.Create(); foreach (var item in FindItemsByType(recurse, predicate)) { queue.Enqueue(item); } return queue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public PooledRefList ListItems(bool recurse = true, Predicate predicate = null) => ListItemsByType(recurse, predicate); public PooledRefList ListItemsByType(bool recurse = true, Predicate predicate = null) where T : Item { var list = PooledRefList.Create(); foreach (var item in FindItemsByType(recurse, predicate)) { list.Add(item); } return list; } public ref struct FindItemsByTypeEnumerator where T : Item { private PooledRefQueue _containers; private Span _items; private int _index; private T _current; private bool _recurse; private Predicate _predicate; public FindItemsByTypeEnumerator(Container container, bool recurse, Predicate predicate) { _containers = PooledRefQueue.Create(); if (container?.m_Items != null) { _items = CollectionsMarshal.AsSpan(container.m_Items); } _current = default; _index = 0; _recurse = recurse; _predicate = predicate; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() => SetNextItem() || _recurse && SetNextContainer(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool SetNextContainer() { if (!_containers.TryDequeue(out var c)) { return false; } _items = CollectionsMarshal.AsSpan(c.m_Items); _index = 0; return SetNextItem(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool SetNextItem() { 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) { _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; } public ref struct QueuedItemsEnumerator where T : Item { private PooledRefQueue _queue; private T _current; public QueuedItemsEnumerator(PooledRefQueue queue) { _queue = queue; _current = default; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (_queue.TryDequeue(out var item)) { _current = item; return true; } return false; } public T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _current; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() => _queue.Dispose(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public QueuedItemsEnumerator GetEnumerator() => this; } }