fix: Moves container enumerables to Item. (#2238)
This commit is contained in:
parent
1e2f4b1171
commit
c2e44a58f6
4 changed files with 87 additions and 67 deletions
|
|
@ -8,12 +8,14 @@ namespace Server.Tests;
|
||||||
[Collection("Sequential Server Tests")]
|
[Collection("Sequential Server Tests")]
|
||||||
public class ContainerTests
|
public class ContainerTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Theory]
|
||||||
public void TestFindItemsByType()
|
[InlineData(typeof(Container))]
|
||||||
|
[InlineData(typeof(Item))]
|
||||||
|
public void TestFindItemsByType(Type itemType)
|
||||||
{
|
{
|
||||||
var staticSerial = (Serial)0x3;
|
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 Item((Serial)0x2));
|
||||||
container.AddItem(new Static(staticSerial));
|
container.AddItem(new Static(staticSerial));
|
||||||
|
|
||||||
|
|
@ -27,18 +29,20 @@ public class ContainerTests
|
||||||
Assert.Equal(staticSerial, staticItem.Serial);
|
Assert.Equal(staticSerial, staticItem.Serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void TestFindItemsByTypeNested()
|
[InlineData(typeof(Container))]
|
||||||
|
[InlineData(typeof(Item))]
|
||||||
|
public void TestFindItemsByTypeNested(Type itemType)
|
||||||
{
|
{
|
||||||
var static1 = new Static((Serial)0x3);
|
var static1 = new Static((Serial)0x3);
|
||||||
var static2 = new Static((Serial)0x6);
|
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));
|
container.AddItem(new Item((Serial)0x2));
|
||||||
var container2 = new Container((Serial)0x4);
|
var container2 = itemType.CreateInstance<Item>((Serial)0x4);
|
||||||
container.AddItem(container2);
|
container.AddItem(container2);
|
||||||
|
|
||||||
var container3 = new Container((Serial)0x5);
|
var container3 = itemType.CreateInstance<Item>((Serial)0x5);
|
||||||
container2.AddItem(container3);
|
container2.AddItem(container3);
|
||||||
container3.AddItem(static2);
|
container3.AddItem(static2);
|
||||||
|
|
||||||
|
|
@ -55,12 +59,14 @@ public class ContainerTests
|
||||||
Assert.Equal(static2, statics[1]);
|
Assert.Equal(static2, statics[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void TestFindItemsByTypeNotMatching()
|
[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));
|
container.AddItem(new Item((Serial)0x2));
|
||||||
var container2 = new Container((Serial)0x4);
|
var container2 = itemType.CreateInstance<Item>((Serial)0x4);
|
||||||
container.AddItem(container2);
|
container.AddItem(container2);
|
||||||
container2.AddItem(new Item((Serial)0x5));
|
container2.AddItem(new Item((Serial)0x5));
|
||||||
|
|
||||||
|
|
@ -73,10 +79,12 @@ public class ContainerTests
|
||||||
Assert.Null(staticItem);
|
Assert.Null(staticItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void TestFindItemsByTypeShouldThrowWhenModified()
|
[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));
|
container.AddItem(new Item((Serial)0x2));
|
||||||
var staticItem = new Static((Serial)0x3);
|
var staticItem = new Static((Serial)0x3);
|
||||||
container.AddItem(staticItem);
|
container.AddItem(staticItem);
|
||||||
|
|
@ -96,10 +104,12 @@ public class ContainerTests
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void TestEnumerateItemsByTypeWhenModified()
|
[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);
|
var item1 = new Item((Serial)0x2);
|
||||||
container.AddItem(item1);
|
container.AddItem(item1);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -25,7 +40,7 @@ public partial class Container : Item
|
||||||
|
|
||||||
private int m_TotalItems;
|
private int m_TotalItems;
|
||||||
private int m_TotalWeight;
|
private int m_TotalWeight;
|
||||||
private int _version;
|
internal int _version;
|
||||||
|
|
||||||
[SerializableField(3)]
|
[SerializableField(3)]
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
|
|
@ -281,16 +296,6 @@ public partial class Container : Item
|
||||||
return true;
|
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]
|
[AfterDeserialization]
|
||||||
private void AfterDeserialization()
|
private void AfterDeserialization()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* ModernUO *
|
* ModernUO *
|
||||||
* Copyright 2019-2023 - ModernUO Development Team *
|
* Copyright 2019-2025 - ModernUO Development Team *
|
||||||
* Email: hi@modernuo.com *
|
* Email: hi@modernuo.com *
|
||||||
* File: Container.Enumerable.cs *
|
* File: Item.Enumerable.cs *
|
||||||
* *
|
* *
|
||||||
* This program is free software: you can redistribute it and/or modify *
|
* 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 *
|
* 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 System.Runtime.InteropServices;
|
||||||
using Server.Collections;
|
using Server.Collections;
|
||||||
|
|
||||||
namespace Server.Items;
|
namespace Server;
|
||||||
|
|
||||||
public partial class Container
|
public partial class Item
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs a breadth-first search through all the <see cref="Item" />s and
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// DO NOT consume, delete, or move items while iterating with any FindItemByType or FindItems overloads
|
/// 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>
|
/// <typeparam name="T">Type of objects being searched for</typeparam>
|
||||||
/// <param name="recurse">
|
/// <param name="recurse">
|
||||||
/// Optional: If true, the search will recursively
|
/// Optional: If true, the search will recursively
|
||||||
/// check any nested <see cref="Container" />s; otherwise, nested
|
/// check any nested <see cref="Item" />s; otherwise, nested
|
||||||
/// <see cref="Container" />s will not be searched.
|
/// <see cref="Item" />s will not be searched.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="predicate">
|
/// <param name="predicate">
|
||||||
/// Optional: A predicate to check if the <see cref="Item" />
|
/// Optional: A predicate to check if the <see cref="Item" />
|
||||||
|
|
@ -71,7 +71,7 @@ public partial class Container
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Safely enumerates items using a breadth-first search through all the <see cref="Item" />s and
|
/// 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>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Use EnumerateItemsByType for situations where the item might be manipulated, consumed, or moved.
|
/// 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>
|
/// <typeparam name="T">Type of objects being searched for</typeparam>
|
||||||
/// <param name="recurse">
|
/// <param name="recurse">
|
||||||
/// Optional: If true, the search will recursively
|
/// Optional: If true, the search will recursively
|
||||||
/// check any nested <see cref="Container" />s; otherwise, nested
|
/// check any nested <see cref="Item" />s; otherwise, nested
|
||||||
/// <see cref="Container" />s will not be searched.
|
/// <see cref="Item" />s will not be searched.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="predicate">
|
/// <param name="predicate">
|
||||||
/// Optional: A predicate to check if the <see cref="Item" />
|
/// 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
|
public ref struct FindItemsByTypeEnumerator<T> where T : Item
|
||||||
{
|
{
|
||||||
private const string InvalidOperation_EnumFailedVersion =
|
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 Span<Item> _items;
|
||||||
private int _index;
|
private int _index;
|
||||||
private T _current;
|
private T _current;
|
||||||
private readonly bool _recurse;
|
private readonly bool _recurse;
|
||||||
private readonly Predicate<T> _predicate;
|
private readonly Predicate<T> _predicate;
|
||||||
private Container _currentContainer;
|
private Item _currentContainer;
|
||||||
private int _version;
|
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 != 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;
|
_currentContainer = container;
|
||||||
_version = container._version;
|
_version = container.LookupContainerVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
_current = default;
|
_current = default;
|
||||||
|
|
@ -244,9 +245,9 @@ public partial class Container
|
||||||
while (_containers.TryDequeue(out var c))
|
while (_containers.TryDequeue(out var c))
|
||||||
{
|
{
|
||||||
_currentContainer = c;
|
_currentContainer = c;
|
||||||
_items = CollectionsMarshal.AsSpan(c.m_Items);
|
_items = CollectionsMarshal.AsSpan(c.LookupItems());
|
||||||
_index = 0;
|
_index = 0;
|
||||||
_version = c._version;
|
_version = c.LookupContainerVersion();
|
||||||
|
|
||||||
if (SetNextItem())
|
if (SetNextItem())
|
||||||
{
|
{
|
||||||
|
|
@ -260,7 +261,7 @@ public partial class Container
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private bool SetNextItem()
|
private bool SetNextItem()
|
||||||
{
|
{
|
||||||
if (_version != _currentContainer._version)
|
if (_version != _currentContainer.LookupContainerVersion())
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(InvalidOperation_EnumFailedVersion);
|
throw new InvalidOperationException(InvalidOperation_EnumFailedVersion);
|
||||||
}
|
}
|
||||||
|
|
@ -268,14 +269,14 @@ public partial class Container
|
||||||
while (_index < _items.Length)
|
while (_index < _items.Length)
|
||||||
{
|
{
|
||||||
Item item = _items[_index++];
|
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 (item is T t && _predicate?.Invoke(t) != false)
|
||||||
{
|
{
|
||||||
if (_version != _currentContainer._version)
|
if (_version != _currentContainer.LookupContainerVersion())
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(InvalidOperation_EnumFailedVersion);
|
throw new InvalidOperationException(InvalidOperation_EnumFailedVersion);
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* ModernUO *
|
* ModernUO *
|
||||||
* Copyright 2019-2024 - ModernUO Development Team *
|
* Copyright 2019-2025 - ModernUO Development Team *
|
||||||
* Email: hi@modernuo.com *
|
* Email: hi@modernuo.com *
|
||||||
* File: Item.cs *
|
* File: Item.cs *
|
||||||
* *
|
* *
|
||||||
|
|
@ -192,7 +192,7 @@ public enum ExpandFlag
|
||||||
Spawner = 0x100
|
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));
|
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 List<Item> Items => LookupItems() ?? EmptyItems;
|
||||||
|
|
||||||
|
public int LookupContainerVersion() => (this as Container)?._version ?? LookupCompactInfo()?.Version ?? 0;
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public IEntity RootParent
|
public IEntity RootParent
|
||||||
{
|
{
|
||||||
|
|
@ -1696,11 +1698,11 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
|
||||||
{
|
{
|
||||||
if (this is Container cont)
|
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();
|
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)
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet) => (flags & toGet) != 0;
|
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;
|
item.Map = m_Map;
|
||||||
|
|
||||||
var items = AcquireItems();
|
var items = AcquireItems();
|
||||||
|
|
||||||
items.Add(item);
|
items.Add(item);
|
||||||
|
|
||||||
|
if (this is not Container)
|
||||||
|
{
|
||||||
|
AcquireCompactInfo().Version++;
|
||||||
|
}
|
||||||
|
|
||||||
if (!item.IsVirtualItem)
|
if (!item.IsVirtualItem)
|
||||||
{
|
{
|
||||||
UpdateTotal(item, TotalType.Gold, item.TotalGold);
|
UpdateTotal(item, TotalType.Gold, item.TotalGold);
|
||||||
|
|
@ -3360,6 +3357,11 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
|
||||||
|
|
||||||
if (items.Remove(item))
|
if (items.Remove(item))
|
||||||
{
|
{
|
||||||
|
if (this is not Container)
|
||||||
|
{
|
||||||
|
AcquireCompactInfo().Version++;
|
||||||
|
}
|
||||||
|
|
||||||
item.SendRemovePacket();
|
item.SendRemovePacket();
|
||||||
|
|
||||||
if (!item.IsVirtualItem)
|
if (!item.IsVirtualItem)
|
||||||
|
|
@ -4321,6 +4323,8 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
|
||||||
public int m_TempFlags;
|
public int m_TempFlags;
|
||||||
|
|
||||||
public double m_Weight = -1;
|
public double m_Weight = -1;
|
||||||
|
|
||||||
|
public int Version;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue