From 844efa32d49baa2ca58332cd26bbbe3cc772ad22 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 1 Jan 2020 00:13:59 -0500 Subject: optimize chest watchers using net events --- src/SMAPI/Framework/StateTracking/ChestTracker.cs | 89 ++++++++++++++++------- 1 file changed, 61 insertions(+), 28 deletions(-) (limited to 'src/SMAPI/Framework/StateTracking/ChestTracker.cs') diff --git a/src/SMAPI/Framework/StateTracking/ChestTracker.cs b/src/SMAPI/Framework/StateTracking/ChestTracker.cs index f907b9a5..66dc61eb 100644 --- a/src/SMAPI/Framework/StateTracking/ChestTracker.cs +++ b/src/SMAPI/Framework/StateTracking/ChestTracker.cs @@ -1,6 +1,9 @@ +using System; using System.Collections.Generic; using System.Linq; using StardewModdingAPI.Events; +using StardewModdingAPI.Framework.StateTracking.Comparers; +using StardewModdingAPI.Framework.StateTracking.FieldWatchers; using StardewValley; using StardewValley.Objects; using ChangeType = StardewModdingAPI.Events.ChangeType; @@ -8,16 +11,22 @@ using ChangeType = StardewModdingAPI.Events.ChangeType; namespace StardewModdingAPI.Framework.StateTracking { /// Tracks changes to a chest's items. - internal class ChestTracker + internal class ChestTracker : IDisposable { /********* ** Fields *********/ - /// The chest's inventory as of the last reset. - private IDictionary PreviousInventory; + /// The item stack sizes as of the last update. + private readonly IDictionary StackSizes; - /// The chest's inventory change as of the last update. - private IDictionary CurrentInventory; + /// Items added since the last update. + private readonly HashSet Added = new HashSet(new ObjectReferenceComparer()); + + /// Items removed since the last update. + private readonly HashSet Removed = new HashSet(new ObjectReferenceComparer()); + + /// The underlying inventory watcher. + private readonly ICollectionWatcher InventoryWatcher; /********* @@ -35,50 +44,74 @@ namespace StardewModdingAPI.Framework.StateTracking public ChestTracker(Chest chest) { this.Chest = chest; - this.PreviousInventory = this.GetInventory(); + this.InventoryWatcher = WatcherFactory.ForNetList(chest.items); + + this.StackSizes = this.Chest.items + .Where(n => n != null) + .Distinct() + .ToDictionary(n => n, n => n.Stack); } /// Update the current values if needed. public void Update() { - this.CurrentInventory = this.GetInventory(); + // update watcher + this.InventoryWatcher.Update(); + foreach (Item item in this.InventoryWatcher.Added.Where(p => p != null)) + this.Added.Add(item); + foreach (Item item in this.InventoryWatcher.Removed.Where(p => p != null)) + { + if (!this.Added.Remove(item)) // item didn't change if it was both added and removed, so remove it from both lists + this.Removed.Add(item); + } + + // stop tracking removed stacks + foreach (Item item in this.Removed) + this.StackSizes.Remove(item); } /// Reset all trackers so their current values are the baseline. public void Reset() { - if (this.CurrentInventory != null) - this.PreviousInventory = this.CurrentInventory; + // update stack sizes + foreach (Item item in this.StackSizes.Keys.ToArray().Concat(this.Added)) + this.StackSizes[item] = item.Stack; + + // update watcher + this.InventoryWatcher.Reset(); + this.Added.Clear(); + this.Removed.Clear(); } /// Get the inventory changes since the last update. public IEnumerable GetInventoryChanges() { - IDictionary previous = this.PreviousInventory; - IDictionary current = this.GetInventory(); + // removed + foreach (Item item in this.Removed) + yield return new ItemStackChange { Item = item, StackChange = -item.Stack, ChangeType = ChangeType.Removed }; + + // added + foreach (Item item in this.Added) + yield return new ItemStackChange { Item = item, StackChange = item.Stack, ChangeType = ChangeType.Added }; - foreach (Item item in previous.Keys.Union(current.Keys)) + // stack size changed + foreach (var entry in this.StackSizes) { - if (!previous.TryGetValue(item, out int prevStack)) - yield return new ItemStackChange { Item = item, StackChange = item.Stack, ChangeType = ChangeType.Added }; - else if (!current.TryGetValue(item, out int newStack)) - yield return new ItemStackChange { Item = item, StackChange = -item.Stack, ChangeType = ChangeType.Removed }; - else if (prevStack != newStack) - yield return new ItemStackChange { Item = item, StackChange = newStack - prevStack, ChangeType = ChangeType.StackChange }; + Item item = entry.Key; + int prevStack = entry.Value; + + if (item.Stack != prevStack) + yield return new ItemStackChange { Item = item, StackChange = item.Stack - prevStack, ChangeType = ChangeType.StackChange }; } } - - /********* - ** Private methods - *********/ - /// Get the player's current inventory. - private IDictionary GetInventory() + /// Release watchers and resources. + public void Dispose() { - return this.Chest.items - .Where(n => n != null) - .Distinct() - .ToDictionary(n => n, n => n.Stack); + this.StackSizes.Clear(); + this.Added.Clear(); + this.Removed.Clear(); + this.InventoryWatcher.Dispose(); } } } -- cgit