fix: Moves container enumerables to Item. (#2238)

This commit is contained in:
Kamron Batman 2025-07-24 14:26:35 -07:00 committed by GitHub
parent 1e2f4b1171
commit c2e44a58f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 87 additions and 67 deletions

View file

@ -8,12 +8,14 @@ namespace Server.Tests;
[Collection("Sequential Server Tests")]
public class ContainerTests
{
[Fact]
public void TestFindItemsByType()
[Theory]
[InlineData(typeof(Container))]
[InlineData(typeof(Item))]
public void TestFindItemsByType(Type itemType)
{
var staticSerial = (Serial)0x3;
var container = new Container((Serial)0x1);
var container = itemType.CreateInstance<Item>((Serial)0x1);
container.AddItem(new Item((Serial)0x2));
container.AddItem(new Static(staticSerial));
@ -27,18 +29,20 @@ public class ContainerTests
Assert.Equal(staticSerial, staticItem.Serial);
}
[Fact]
public void TestFindItemsByTypeNested()
[Theory]
[InlineData(typeof(Container))]
[InlineData(typeof(Item))]
public void TestFindItemsByTypeNested(Type itemType)
{
var static1 = new Static((Serial)0x3);
var static2 = new Static((Serial)0x6);
var container = new Container((Serial)0x1);
var container = itemType.CreateInstance<Item>((Serial)0x1);
container.AddItem(new Item((Serial)0x2));
var container2 = new Container((Serial)0x4);
var container2 = itemType.CreateInstance<Item>((Serial)0x4);
container.AddItem(container2);
var container3 = new Container((Serial)0x5);
var container3 = itemType.CreateInstance<Item>((Serial)0x5);
container2.AddItem(container3);
container3.AddItem(static2);
@ -55,12 +59,14 @@ public class ContainerTests
Assert.Equal(static2, statics[1]);
}
[Fact]
public void TestFindItemsByTypeNotMatching()
[Theory]
[InlineData(typeof(Container))]
[InlineData(typeof(Item))]
public void TestFindItemsByTypeNotMatching(Type itemType)
{
var container = new Container((Serial)0x1);
var container = itemType.CreateInstance<Item>((Serial)0x1);
container.AddItem(new Item((Serial)0x2));
var container2 = new Container((Serial)0x4);
var container2 = itemType.CreateInstance<Item>((Serial)0x4);
container.AddItem(container2);
container2.AddItem(new Item((Serial)0x5));
@ -73,10 +79,12 @@ public class ContainerTests
Assert.Null(staticItem);
}
[Fact]
public void TestFindItemsByTypeShouldThrowWhenModified()
[Theory]
[InlineData(typeof(Container))]
[InlineData(typeof(Item))]
public void TestFindItemsByTypeShouldThrowWhenModified(Type itemType)
{
var container = new Container((Serial)0x1);
var container = itemType.CreateInstance<Item>((Serial)0x1);
container.AddItem(new Item((Serial)0x2));
var staticItem = new Static((Serial)0x3);
container.AddItem(staticItem);
@ -96,10 +104,12 @@ public class ContainerTests
);
}
[Fact]
public void TestEnumerateItemsByTypeWhenModified()
[Theory]
[InlineData(typeof(Container))]
[InlineData(typeof(Item))]
public void TestEnumerateItemsByTypeWhenModified(Type itemType)
{
var container = new Container((Serial)0x1);
var container = itemType.CreateInstance<Item>((Serial)0x1);
var item1 = new Item((Serial)0x2);
container.AddItem(item1);

View file

@ -1,3 +1,18 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2025 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Container.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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
@ -25,7 +40,7 @@ public partial class Container : Item
private int m_TotalItems;
private int m_TotalWeight;
private int _version;
internal int _version;
[SerializableField(3)]
[SerializedCommandProperty(AccessLevel.GameMaster)]
@ -281,16 +296,6 @@ public partial class Container : Item
return true;
}
private static void SetSaveFlag(ref SaveFlag flags, SaveFlag toSet, bool setIf)
{
if (setIf)
{
flags |= toSet;
}
}
private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet) => (flags & toGet) != 0;
[AfterDeserialization]
private void AfterDeserialization()
{

View file

@ -1,8 +1,8 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Copyright 2019-2025 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Container.Enumerable.cs *
* 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 *
@ -18,13 +18,13 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Server.Collections;
namespace Server.Items;
namespace Server;
public partial class Container
public partial class Item
{
/// <summary>
/// Performs a breadth-first search through all the <see cref="Item" />s and
/// nested <see cref="Container" />s within this <see cref="Container" />.
/// nested <see cref="Item" />s within this <see cref="Item" />.
/// </summary>
/// <remarks>
/// DO NOT consume, delete, or move items while iterating with any FindItemByType or FindItems overloads
@ -42,8 +42,8 @@ public partial class Container
/// <typeparam name="T">Type of objects being searched for</typeparam>
/// <param name="recurse">
/// Optional: If true, the search will recursively
/// check any nested <see cref="Container" />s; otherwise, nested
/// <see cref="Container" />s will not be searched.
/// check any nested <see cref="Item" />s; otherwise, nested
/// <see cref="Item" />s will not be searched.
/// </param>
/// <param name="predicate">
/// Optional: A predicate to check if the <see cref="Item" />
@ -71,7 +71,7 @@ public partial class Container
/// <summary>
/// Safely enumerates items using a breadth-first search through all the <see cref="Item" />s and
/// nested <see cref="Container" />s within this <see cref="Container" />.
/// nested <see cref="Item" />s within this <see cref="Item" />.
/// </summary>
/// <remarks>
/// Use EnumerateItemsByType for situations where the item might be manipulated, consumed, or moved.
@ -92,8 +92,8 @@ public partial class Container
/// <typeparam name="T">Type of objects being searched for</typeparam>
/// <param name="recurse">
/// Optional: If true, the search will recursively
/// check any nested <see cref="Container" />s; otherwise, nested
/// <see cref="Container" />s will not be searched.
/// check any nested <see cref="Item" />s; otherwise, nested
/// <see cref="Item" />s will not be searched.
/// </param>
/// <param name="predicate">
/// Optional: A predicate to check if the <see cref="Item" />
@ -203,30 +203,31 @@ public partial class Container
public ref struct FindItemsByTypeEnumerator<T> where T : Item
{
private const string InvalidOperation_EnumFailedVersion =
"Container was modified after enumerator was instantiated. Use Container.EnumerateItems method instead for safe enumerations.";
"Item was modified after enumerator was instantiated. Use Item.EnumerateItems method instead for safe enumerations.";
private PooledRefQueue<Container> _containers;
private PooledRefQueue<Item> _containers;
private Span<Item> _items;
private int _index;
private T _current;
private readonly bool _recurse;
private readonly Predicate<T> _predicate;
private Container _currentContainer;
private Item _currentContainer;
private int _version;
public FindItemsByTypeEnumerator(Container container, bool recurse, Predicate<T> predicate)
public FindItemsByTypeEnumerator(Item container, bool recurse, Predicate<T> predicate)
{
_containers = PooledRefQueue<Container>.Create(_recurse ? 64 : 0);
_containers = PooledRefQueue<Item>.Create(_recurse ? 64 : 0);
if (container != null)
{
if (container.m_Items != null)
var items = container.LookupItems();
if (items != null)
{
_items = CollectionsMarshal.AsSpan(container.m_Items);
_items = CollectionsMarshal.AsSpan(items);
}
_currentContainer = container;
_version = container._version;
_version = container.LookupContainerVersion();
}
_current = default;
@ -244,9 +245,9 @@ public partial class Container
while (_containers.TryDequeue(out var c))
{
_currentContainer = c;
_items = CollectionsMarshal.AsSpan(c.m_Items);
_items = CollectionsMarshal.AsSpan(c.LookupItems());
_index = 0;
_version = c._version;
_version = c.LookupContainerVersion();
if (SetNextItem())
{
@ -260,7 +261,7 @@ public partial class Container
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool SetNextItem()
{
if (_version != _currentContainer._version)
if (_version != _currentContainer.LookupContainerVersion())
{
throw new InvalidOperationException(InvalidOperation_EnumFailedVersion);
}
@ -268,14 +269,14 @@ public partial class Container
while (_index < _items.Length)
{
Item item = _items[_index++];
if (_recurse && item is Container { m_Items.Count: > 0 } c)
if (_recurse && item.LookupItems() is { Count: > 0 } items)
{
_containers.Enqueue(c);
_containers.Enqueue(item);
}
if (item is T t && _predicate?.Invoke(t) != false)
{
if (_version != _currentContainer._version)
if (_version != _currentContainer.LookupContainerVersion())
{
throw new InvalidOperationException(InvalidOperation_EnumFailedVersion);
}

View file

@ -1,6 +1,6 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Copyright 2019-2025 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Item.cs *
* *
@ -192,7 +192,7 @@ public enum ExpandFlag
Spawner = 0x100
}
public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEntity, IValueLinkListNode<Item>
public partial class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEntity, IValueLinkListNode<Item>
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(Item));
@ -534,6 +534,8 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
public List<Item> Items => LookupItems() ?? EmptyItems;
public int LookupContainerVersion() => (this as Container)?._version ?? LookupCompactInfo()?.Version ?? 0;
[CommandProperty(AccessLevel.GameMaster)]
public IEntity RootParent
{
@ -1696,11 +1698,11 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
{
if (this is Container cont)
{
return cont.m_Items ?? (cont.m_Items = new List<Item>());
return cont.m_Items ??= new List<Item>();
}
var info = AcquireCompactInfo();
return info.m_Items ?? (info.m_Items = new List<Item>());
return info.m_Items ??= new List<Item>();
}
private void SetFlag(ImplFlag flag, bool value)
@ -2471,15 +2473,6 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
{
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetSaveFlag(ref SaveFlag flags, SaveFlag toSet, bool setIf)
{
if (setIf)
{
flags |= toSet;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet) => (flags & toGet) != 0;
@ -3195,9 +3188,13 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
item.Map = m_Map;
var items = AcquireItems();
items.Add(item);
if (this is not Container)
{
AcquireCompactInfo().Version++;
}
if (!item.IsVirtualItem)
{
UpdateTotal(item, TotalType.Gold, item.TotalGold);
@ -3360,6 +3357,11 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
if (items.Remove(item))
{
if (this is not Container)
{
AcquireCompactInfo().Version++;
}
item.SendRemovePacket();
if (!item.IsVirtualItem)
@ -4321,6 +4323,8 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
public int m_TempFlags;
public double m_Weight = -1;
public int Version;
}
[Flags]