mirror of
https://github.com/Pathoschild/SMAPI.git
synced 2026-10-12 02:08:51 +00:00
reduce allocations in collection watchers
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using StardewValley.Extensions;
|
||||
|
||||
namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers;
|
||||
|
||||
@@ -15,6 +16,9 @@ internal class ComparableListWatcher<TValue> : BaseDisposableWatcher, ICollectio
|
||||
/// <summary>The values during the previous update.</summary>
|
||||
private HashSet<TValue> LastValues;
|
||||
|
||||
/// <summary>A pooled set used to update <see cref="LastValues"/> each update.</summary>
|
||||
private HashSet<TValue> NewValues;
|
||||
|
||||
/// <summary>The pairs added since the last reset.</summary>
|
||||
private readonly List<TValue> AddedImpl = [];
|
||||
|
||||
@@ -50,6 +54,7 @@ internal class ComparableListWatcher<TValue> : BaseDisposableWatcher, ICollectio
|
||||
this.Name = name;
|
||||
this.CurrentValues = values;
|
||||
this.LastValues = new HashSet<TValue>(comparer);
|
||||
this.NewValues = new HashSet<TValue>(comparer);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -68,19 +73,24 @@ internal class ComparableListWatcher<TValue> : BaseDisposableWatcher, ICollectio
|
||||
return;
|
||||
}
|
||||
|
||||
// get new values
|
||||
this.NewValues.Clear();
|
||||
this.NewValues.AddRange(this.CurrentValues);
|
||||
|
||||
// detect changes
|
||||
HashSet<TValue> curValues = new HashSet<TValue>(this.CurrentValues, this.LastValues.Comparer);
|
||||
foreach (TValue value in this.LastValues)
|
||||
{
|
||||
if (!curValues.Contains(value))
|
||||
if (!this.NewValues.Contains(value))
|
||||
this.RemovedImpl.Add(value);
|
||||
}
|
||||
foreach (TValue value in curValues)
|
||||
foreach (TValue value in this.NewValues)
|
||||
{
|
||||
if (!this.LastValues.Contains(value))
|
||||
this.AddedImpl.Add(value);
|
||||
}
|
||||
this.LastValues = curValues;
|
||||
|
||||
// save result
|
||||
(this.LastValues, this.NewValues) = (this.NewValues, this.LastValues); // reuse the now-unused previous 'LastValues' set on the next update
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using StardewModdingAPI.Framework.StateTracking.Comparers;
|
||||
using StardewValley;
|
||||
using StardewValley.Extensions;
|
||||
using StardewValley.Inventories;
|
||||
|
||||
namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers;
|
||||
@@ -15,10 +16,16 @@ internal class InventoryWatcher : BaseDisposableWatcher, ICollectionWatcher<Item
|
||||
private readonly Inventory Inventory;
|
||||
|
||||
/// <summary>The pairs added since the last reset.</summary>
|
||||
private readonly ISet<Item> AddedImpl = new HashSet<Item>(new ObjectReferenceComparer<Item>());
|
||||
private readonly HashSet<Item> AddedImpl = new(new ObjectReferenceComparer<Item>());
|
||||
|
||||
/// <summary>The pairs removed since the last reset.</summary>
|
||||
private readonly ISet<Item> RemovedImpl = new HashSet<Item>(new ObjectReferenceComparer<Item>());
|
||||
private readonly HashSet<Item> RemovedImpl = new(new ObjectReferenceComparer<Item>());
|
||||
|
||||
/// <summary>A pooled set used to track the previous inventory when detecting changes.</summary>
|
||||
private readonly HashSet<Item> PooledOldSet = new(new ObjectReferenceComparer<Item>());
|
||||
|
||||
/// <summary>A pooled set used to track the new inventory when detecting changes.</summary>
|
||||
private readonly HashSet<Item> PooledNewSet = new(new ObjectReferenceComparer<Item>());
|
||||
|
||||
|
||||
/*********
|
||||
@@ -87,17 +94,21 @@ internal class InventoryWatcher : BaseDisposableWatcher, ICollectionWatcher<Item
|
||||
/// <param name="newValues">The new list of values.</param>
|
||||
private void OnInventoryReplaced(Inventory inventory, IList<Item> oldValues, IList<Item> newValues)
|
||||
{
|
||||
ISet<Item> oldSet = new HashSet<Item>(oldValues, new ObjectReferenceComparer<Item>());
|
||||
ISet<Item> changed = new HashSet<Item>(newValues, new ObjectReferenceComparer<Item>());
|
||||
this.PooledOldSet.Clear();
|
||||
this.PooledOldSet.AddRange(oldValues);
|
||||
|
||||
foreach (Item value in oldSet)
|
||||
this.PooledNewSet.Clear();
|
||||
this.PooledNewSet.AddRange(newValues);
|
||||
|
||||
foreach (Item value in this.PooledOldSet)
|
||||
{
|
||||
if (!changed.Contains(value))
|
||||
if (!this.PooledNewSet.Contains(value))
|
||||
this.Remove(value);
|
||||
}
|
||||
foreach (Item value in changed)
|
||||
|
||||
foreach (Item value in this.PooledNewSet)
|
||||
{
|
||||
if (!oldSet.Contains(value))
|
||||
if (!this.PooledOldSet.Contains(value))
|
||||
this.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ internal class PlayerTracker : IDisposable
|
||||
/// <summary>The player's inventory change as of the last update.</summary>
|
||||
private readonly Dictionary<Item, int> CurrentInventory = [];
|
||||
|
||||
/// <summary>A pooled set used to track the added items when detecting changes.</summary>
|
||||
private readonly HashSet<Item> PooledAdded;
|
||||
|
||||
/// <summary>A pooled set used to track the removed items when detecting changes.</summary>
|
||||
private readonly HashSet<Item> PooledRemoved;
|
||||
|
||||
/// <summary>The player's last valid location.</summary>
|
||||
private GameLocation? LastValidLocation;
|
||||
|
||||
@@ -67,6 +73,11 @@ internal class PlayerTracker : IDisposable
|
||||
// track watchers for convenience
|
||||
this.Watchers.Add(this.LocationWatcher);
|
||||
this.Watchers.AddRange(this.SkillWatchers.Values);
|
||||
|
||||
// init pooled sets
|
||||
var comparer = new ObjectReferenceComparer<Item>();
|
||||
this.PooledAdded = new HashSet<Item>(comparer);
|
||||
this.PooledRemoved = new HashSet<Item>(comparer);
|
||||
}
|
||||
|
||||
/// <summary>Update the current values if needed.</summary>
|
||||
@@ -109,22 +120,22 @@ internal class PlayerTracker : IDisposable
|
||||
/// <returns>Returns whether anything changed.</returns>
|
||||
public bool TryGetInventoryChanges([NotNullWhen(true)] out SnapshotItemListDiff? changes)
|
||||
{
|
||||
HashSet<Item> added = new(new ObjectReferenceComparer<Item>());
|
||||
HashSet<Item> removed = new(new ObjectReferenceComparer<Item>());
|
||||
this.PooledAdded.Clear();
|
||||
this.PooledRemoved.Clear();
|
||||
|
||||
foreach (Item item in this.PreviousInventory.Keys)
|
||||
{
|
||||
if (!this.CurrentInventory.ContainsKey(item))
|
||||
removed.Add(item);
|
||||
this.PooledRemoved.Add(item);
|
||||
}
|
||||
|
||||
foreach (Item item in this.CurrentInventory.Keys)
|
||||
{
|
||||
if (!this.PreviousInventory.ContainsKey(item))
|
||||
added.Add(item);
|
||||
this.PooledAdded.Add(item);
|
||||
}
|
||||
|
||||
return SnapshotItemListDiff.TryGetChanges(added: added, removed: removed, stackSizes: this.PreviousInventory, out changes);
|
||||
return SnapshotItemListDiff.TryGetChanges(added: this.PooledAdded, removed: this.PooledRemoved, stackSizes: this.PreviousInventory, out changes);
|
||||
}
|
||||
|
||||
/// <summary>Release watchers and resources.</summary>
|
||||
|
||||
Reference in New Issue
Block a user