From 2894b4322304022b1924e4554c762b560b66b614 Mon Sep 17 00:00:00 2001 From: wartech0 Date: Tue, 31 Dec 2019 01:36:18 -0600 Subject: reworking chest event handler --- src/SMAPI/Framework/StateTracking/ChestTracker.cs | 79 +++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/SMAPI/Framework/StateTracking/ChestTracker.cs (limited to 'src/SMAPI/Framework/StateTracking/ChestTracker.cs') diff --git a/src/SMAPI/Framework/StateTracking/ChestTracker.cs b/src/SMAPI/Framework/StateTracking/ChestTracker.cs new file mode 100644 index 00000000..4440bf4b --- /dev/null +++ b/src/SMAPI/Framework/StateTracking/ChestTracker.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using StardewModdingAPI.Enums; +using StardewModdingAPI.Events; +using StardewModdingAPI.Framework.StateTracking.FieldWatchers; +using StardewValley; +using ChangeType = StardewModdingAPI.Events.ChangeType; +using Chest = StardewValley.Objects.Chest; + +namespace StardewModdingAPI.Framework.StateTracking +{ + internal class ChestTracker + { + /********* + ** Fields + *********/ + /// The chest's inventory as of the last reset. + private IDictionary PreviousInventory; + + /// The chest's inventory change as of the last update. + private IDictionary CurrentInventory; + + /********* + ** Accessors + *********/ + /// The chest being tracked + public Chest Chest { get; } + + /********* + ** Public methods + *********/ + public ChestTracker(Chest chest) + { + this.Chest = chest; + this.PreviousInventory = this.GetInventory(); + } + + public void Update() + { + this.CurrentInventory = this.GetInventory(); + } + + + public void Reset() + { + this.PreviousInventory = this.CurrentInventory; + } + + public IEnumerable GetInventoryChanges() + { + IDictionary previous = this.PreviousInventory; + Console.WriteLine(previous.Count); + IDictionary current = this.GetInventory(); + Console.WriteLine(current.Count); + foreach (Item item in previous.Keys.Union(current.Keys)) + { + 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 }; + } + } + + /********* + ** Private methods + *********/ + + private IDictionary GetInventory() + { + return this.Chest.items + .Where(n => n != null) + .Distinct() + .ToDictionary(n => n, n => n.Stack); + } + } +} -- cgit From 0411dcf3db277ed0d3c9f0201b7554a7d61ed1e8 Mon Sep 17 00:00:00 2001 From: wartech0 Date: Tue, 31 Dec 2019 04:20:36 -0600 Subject: Finished chest events --- src/SMAPI/Events/ChestInventoryChangedEventArgs.cs | 20 ++++++-------------- src/SMAPI/Events/ItemStackChange.cs | 7 ++++++- src/SMAPI/Framework/SGame.cs | 4 +--- src/SMAPI/Framework/StateTracking/ChestTracker.cs | 6 +++--- src/SMAPI/Framework/StateTracking/LocationTracker.cs | 15 +++++++++------ .../StateTracking/Snapshots/LocationSnapshot.cs | 20 ++++++++++---------- .../Snapshots/WorldLocationsSnapshot.cs | 2 +- 7 files changed, 36 insertions(+), 38 deletions(-) (limited to 'src/SMAPI/Framework/StateTracking/ChestTracker.cs') diff --git a/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs b/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs index 0b54e909..a0c63fdc 100644 --- a/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs +++ b/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs @@ -16,17 +16,10 @@ namespace StardewModdingAPI.Events public GameLocation Location { get; } /// The tile position of the chest. - public Vector2 Tile { get; } - - /// The objects added to the location. - public IEnumerable Added { get; } - - /// The objects removed from the location. - public IEnumerable Removed { get; } - - /// Whether this is the location containing the local player. - public bool IsCurrentLocation => object.ReferenceEquals(this.Location, Game1.player?.currentLocation); + public StardewValley.Objects.Chest Chest { get; } + /// The inventory changes added to the chest. + public ItemStackChange[] Changes { get; } /********* ** Public methods @@ -36,12 +29,11 @@ namespace StardewModdingAPI.Events /// The tile position of the chest. /// The objects added to the location. /// The objects removed from the location. - internal ChestInventoryChangedEventArgs(GameLocation location, Vector2 tile, IEnumerable added, IEnumerable removed) + internal ChestInventoryChangedEventArgs(GameLocation location, StardewValley.Objects.Chest chest, ItemStackChange[] changes) { this.Location = location; - this.Tile = tile; - this.Added = added.ToArray(); - this.Removed = removed.ToArray(); + this.Chest = chest; + this.Changes = changes; } } } diff --git a/src/SMAPI/Events/ItemStackChange.cs b/src/SMAPI/Events/ItemStackChange.cs index f9ae6df6..dbb529d6 100644 --- a/src/SMAPI/Events/ItemStackChange.cs +++ b/src/SMAPI/Events/ItemStackChange.cs @@ -16,5 +16,10 @@ namespace StardewModdingAPI.Events /// How the inventory slot changed. public ChangeType ChangeType { get; set; } + + public override string ToString() + { + return this.StackChange + " " + this.Item.Name + " " + this.ChangeType.ToString(); + } } -} \ No newline at end of file +} diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 64da1cf1..c62bcf84 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -707,9 +707,7 @@ namespace StardewModdingAPI.Framework { foreach (var pair in locState.ChestItems) { - var diff = pair.Value; - if (diff.IsChanged) - events.ChestInventoryChanged.Raise(new ChestInventoryChangedEventArgs(location, pair.Key, diff.Added, diff.Removed)); + events.ChestInventoryChanged.Raise(new ChestInventoryChangedEventArgs(location, pair.Key, pair.Value)); } } diff --git a/src/SMAPI/Framework/StateTracking/ChestTracker.cs b/src/SMAPI/Framework/StateTracking/ChestTracker.cs index 4440bf4b..74039753 100644 --- a/src/SMAPI/Framework/StateTracking/ChestTracker.cs +++ b/src/SMAPI/Framework/StateTracking/ChestTracker.cs @@ -44,15 +44,15 @@ namespace StardewModdingAPI.Framework.StateTracking public void Reset() { - this.PreviousInventory = this.CurrentInventory; + if(this.CurrentInventory!=null) + this.PreviousInventory = this.CurrentInventory; } public IEnumerable GetInventoryChanges() { IDictionary previous = this.PreviousInventory; - Console.WriteLine(previous.Count); IDictionary current = this.GetInventory(); - Console.WriteLine(current.Count); + foreach (Item item in previous.Keys.Union(current.Keys)) { if (!previous.TryGetValue(item, out int prevStack)) diff --git a/src/SMAPI/Framework/StateTracking/LocationTracker.cs b/src/SMAPI/Framework/StateTracking/LocationTracker.cs index 170fd537..659efc57 100644 --- a/src/SMAPI/Framework/StateTracking/LocationTracker.cs +++ b/src/SMAPI/Framework/StateTracking/LocationTracker.cs @@ -49,7 +49,7 @@ namespace StardewModdingAPI.Framework.StateTracking public IDictionaryWatcher TerrainFeaturesWatcher { get; } /// Tracks items added or removed to chests. - public Dictionary> ChestWatchers = new Dictionary>(); + public Dictionary ChestWatchers = new Dictionary(); /********* @@ -89,6 +89,9 @@ namespace StardewModdingAPI.Framework.StateTracking watcher.Update(); this.UpdateChestWatcherList(added: this.ObjectsWatcher.Added, removed: this.ObjectsWatcher.Removed); + + foreach (var watcher in this.ChestWatchers) + watcher.Value.Update(); } /// Set the current value as the baseline. @@ -96,6 +99,9 @@ namespace StardewModdingAPI.Framework.StateTracking { foreach (IWatcher watcher in this.Watchers) watcher.Reset(); + + foreach (var watcher in this.ChestWatchers) + watcher.Value.Reset(); } /// Stop watching the player fields and release all references. @@ -117,9 +123,8 @@ namespace StardewModdingAPI.Framework.StateTracking // remove unused watchers foreach (KeyValuePair pair in removed) { - if (pair.Value is Chest && this.ChestWatchers.TryGetValue(pair.Key, out ICollectionWatcher watcher)) + if (pair.Value is Chest && this.ChestWatchers.TryGetValue(pair.Key, out ChestTracker watcher)) { - this.Watchers.Remove(watcher); this.ChestWatchers.Remove(pair.Key); } } @@ -129,9 +134,7 @@ namespace StardewModdingAPI.Framework.StateTracking { if (pair.Value is Chest chest && !this.ChestWatchers.ContainsKey(pair.Key)) { - ICollectionWatcher watcher = new NetListWatcher(chest.items); - this.Watchers.Add(watcher); - this.ChestWatchers.Add(pair.Key, watcher); + this.ChestWatchers.Add(pair.Key, new ChestTracker(chest)); } } } diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs index 4074336b..62a56c84 100644 --- a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs +++ b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; +using StardewModdingAPI.Events; using StardewValley; using StardewValley.Buildings; using StardewValley.TerrainFeatures; @@ -35,7 +36,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots public SnapshotListDiff> TerrainFeatures { get; } = new SnapshotListDiff>(); /// Tracks changed chest inventories. - public IDictionary> ChestItems { get; } = new Dictionary>(); + public IDictionary ChestItems { get; } = new Dictionary(); /********* @@ -61,17 +62,16 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots this.TerrainFeatures.Update(watcher.TerrainFeaturesWatcher); // chest inventories - foreach (Vector2 key in this.ChestItems.Keys.ToArray()) - { - if (!watcher.ChestWatchers.ContainsKey(key)) - this.ChestItems.Remove(key); - } foreach (var pair in watcher.ChestWatchers) { - if (!this.ChestItems.TryGetValue(pair.Key, out var diff)) - this.ChestItems[pair.Key] = diff = new SnapshotListDiff(); - - diff.Update(pair.Value); + IEnumerable temp = pair.Value.GetInventoryChanges(); + if (temp.Any()) + if (this.ChestItems.ContainsKey(pair.Value.Chest)) + this.ChestItems[pair.Value.Chest] = pair.Value.GetInventoryChanges().ToArray(); + else + this.ChestItems.Add(pair.Value.Chest, pair.Value.GetInventoryChanges().ToArray()); + else + this.ChestItems.Remove(pair.Value.Chest); } } } diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs index ed8001d6..73ed2d8f 100644 --- a/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs +++ b/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs @@ -43,7 +43,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots foreach (LocationTracker locationWatcher in watcher.Locations) { if (!this.LocationsDict.TryGetValue(locationWatcher.Location, out LocationSnapshot snapshot)) - this.LocationsDict[locationWatcher.Location] = snapshot = new LocationSnapshot(locationWatcher); + this.LocationsDict[locationWatcher.Location] = snapshot = new LocationSnapshot(locationWatcher.Location); snapshot.Update(locationWatcher); } -- cgit From 6bf99f0f81582ab6d6212dc21e8c36686ceb5a35 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 31 Dec 2019 17:32:46 -0500 Subject: minor refactoring --- src/SMAPI/Events/ChestInventoryChangedEventArgs.cs | 20 +++++++++----------- src/SMAPI/Events/ItemStackChange.cs | 5 ----- src/SMAPI/Framework/SGame.cs | 4 +--- src/SMAPI/Framework/StateTracking/ChestTracker.cs | 21 +++++++++++++-------- .../Framework/StateTracking/LocationTracker.cs | 6 +----- src/SMAPI/Framework/StateTracking/PlayerTracker.cs | 2 +- .../StateTracking/Snapshots/LocationSnapshot.cs | 12 +++++------- 7 files changed, 30 insertions(+), 40 deletions(-) (limited to 'src/SMAPI/Framework/StateTracking/ChestTracker.cs') diff --git a/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs b/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs index a0c63fdc..7771cd7c 100644 --- a/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs +++ b/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Xna.Framework; using StardewValley; +using StardewValley.Objects; namespace StardewModdingAPI.Events { @@ -12,24 +10,24 @@ namespace StardewModdingAPI.Events /********* ** Accessors *********/ + /// The chest whose inventory changed. + public Chest Chest { get; } + /// The location containing the chest. public GameLocation Location { get; } - /// The tile position of the chest. - public StardewValley.Objects.Chest Chest { get; } - - /// The inventory changes added to the chest. + /// The inventory changes in the chest. public ItemStackChange[] Changes { get; } + /********* ** Public methods *********/ /// Construct an instance. + /// The chest whose inventory changed. /// The location containing the chest. - /// The tile position of the chest. - /// The objects added to the location. - /// The objects removed from the location. - internal ChestInventoryChangedEventArgs(GameLocation location, StardewValley.Objects.Chest chest, ItemStackChange[] changes) + /// The inventory changes in the chest. + internal ChestInventoryChangedEventArgs(Chest chest, GameLocation location, ItemStackChange[] changes) { this.Location = location; this.Chest = chest; diff --git a/src/SMAPI/Events/ItemStackChange.cs b/src/SMAPI/Events/ItemStackChange.cs index dbb529d6..cb5d2b88 100644 --- a/src/SMAPI/Events/ItemStackChange.cs +++ b/src/SMAPI/Events/ItemStackChange.cs @@ -16,10 +16,5 @@ namespace StardewModdingAPI.Events /// How the inventory slot changed. public ChangeType ChangeType { get; set; } - - public override string ToString() - { - return this.StackChange + " " + this.Item.Name + " " + this.ChangeType.ToString(); - } } } diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index c62bcf84..eccb6387 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -706,9 +706,7 @@ namespace StardewModdingAPI.Framework if (events.ChestInventoryChanged.HasListeners()) { foreach (var pair in locState.ChestItems) - { - events.ChestInventoryChanged.Raise(new ChestInventoryChangedEventArgs(location, pair.Key, pair.Value)); - } + events.ChestInventoryChanged.Raise(new ChestInventoryChangedEventArgs(pair.Key, location, pair.Value)); } // terrain features changed diff --git a/src/SMAPI/Framework/StateTracking/ChestTracker.cs b/src/SMAPI/Framework/StateTracking/ChestTracker.cs index 74039753..f907b9a5 100644 --- a/src/SMAPI/Framework/StateTracking/ChestTracker.cs +++ b/src/SMAPI/Framework/StateTracking/ChestTracker.cs @@ -1,15 +1,13 @@ -using System; using System.Collections.Generic; using System.Linq; -using StardewModdingAPI.Enums; using StardewModdingAPI.Events; -using StardewModdingAPI.Framework.StateTracking.FieldWatchers; using StardewValley; +using StardewValley.Objects; using ChangeType = StardewModdingAPI.Events.ChangeType; -using Chest = StardewValley.Objects.Chest; namespace StardewModdingAPI.Framework.StateTracking { + /// Tracks changes to a chest's items. internal class ChestTracker { /********* @@ -21,33 +19,39 @@ namespace StardewModdingAPI.Framework.StateTracking /// The chest's inventory change as of the last update. private IDictionary CurrentInventory; + /********* ** Accessors *********/ - /// The chest being tracked + /// The chest being tracked. public Chest Chest { get; } + /********* ** Public methods *********/ + /// Construct an instance. + /// The chest being tracked. public ChestTracker(Chest chest) { this.Chest = chest; this.PreviousInventory = this.GetInventory(); } + /// Update the current values if needed. public void Update() { this.CurrentInventory = this.GetInventory(); } - + /// Reset all trackers so their current values are the baseline. public void Reset() { - if(this.CurrentInventory!=null) + if (this.CurrentInventory != null) this.PreviousInventory = this.CurrentInventory; } + /// Get the inventory changes since the last update. public IEnumerable GetInventoryChanges() { IDictionary previous = this.PreviousInventory; @@ -64,10 +68,11 @@ namespace StardewModdingAPI.Framework.StateTracking } } + /********* ** Private methods *********/ - + /// Get the player's current inventory. private IDictionary GetInventory() { return this.Chest.items diff --git a/src/SMAPI/Framework/StateTracking/LocationTracker.cs b/src/SMAPI/Framework/StateTracking/LocationTracker.cs index 659efc57..7445add9 100644 --- a/src/SMAPI/Framework/StateTracking/LocationTracker.cs +++ b/src/SMAPI/Framework/StateTracking/LocationTracker.cs @@ -49,7 +49,7 @@ namespace StardewModdingAPI.Framework.StateTracking public IDictionaryWatcher TerrainFeaturesWatcher { get; } /// Tracks items added or removed to chests. - public Dictionary ChestWatchers = new Dictionary(); + public IDictionary ChestWatchers { get; } = new Dictionary(); /********* @@ -124,18 +124,14 @@ namespace StardewModdingAPI.Framework.StateTracking foreach (KeyValuePair pair in removed) { if (pair.Value is Chest && this.ChestWatchers.TryGetValue(pair.Key, out ChestTracker watcher)) - { this.ChestWatchers.Remove(pair.Key); - } } // add new watchers foreach (KeyValuePair pair in added) { if (pair.Value is Chest chest && !this.ChestWatchers.ContainsKey(pair.Key)) - { this.ChestWatchers.Add(pair.Key, new ChestTracker(chest)); - } } } } diff --git a/src/SMAPI/Framework/StateTracking/PlayerTracker.cs b/src/SMAPI/Framework/StateTracking/PlayerTracker.cs index 6302a889..cd7d75ec 100644 --- a/src/SMAPI/Framework/StateTracking/PlayerTracker.cs +++ b/src/SMAPI/Framework/StateTracking/PlayerTracker.cs @@ -99,7 +99,7 @@ namespace StardewModdingAPI.Framework.StateTracking return this.Player.currentLocation ?? this.LastValidLocation; } - /// Get the player inventory changes between two states. + /// Get the inventory changes since the last update. public IEnumerable GetInventoryChanges() { IDictionary previous = this.PreviousInventory; diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs index 62a56c84..4e08a1ac 100644 --- a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs +++ b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs @@ -4,6 +4,7 @@ using Microsoft.Xna.Framework; using StardewModdingAPI.Events; using StardewValley; using StardewValley.Buildings; +using StardewValley.Objects; using StardewValley.TerrainFeatures; namespace StardewModdingAPI.Framework.StateTracking.Snapshots @@ -36,7 +37,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots public SnapshotListDiff> TerrainFeatures { get; } = new SnapshotListDiff>(); /// Tracks changed chest inventories. - public IDictionary ChestItems { get; } = new Dictionary(); + public IDictionary ChestItems { get; } = new Dictionary(); /********* @@ -64,12 +65,9 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots // chest inventories foreach (var pair in watcher.ChestWatchers) { - IEnumerable temp = pair.Value.GetInventoryChanges(); - if (temp.Any()) - if (this.ChestItems.ContainsKey(pair.Value.Chest)) - this.ChestItems[pair.Value.Chest] = pair.Value.GetInventoryChanges().ToArray(); - else - this.ChestItems.Add(pair.Value.Chest, pair.Value.GetInventoryChanges().ToArray()); + ItemStackChange[] changes = pair.Value.GetInventoryChanges().ToArray(); + if (changes.Length > 0) + this.ChestItems[pair.Value.Chest] = changes; else this.ChestItems.Remove(pair.Value.Chest); } -- cgit 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 +++++++++++++++------- .../StateTracking/FieldWatchers/WatcherFactory.cs | 8 ++ .../Framework/StateTracking/LocationTracker.cs | 6 ++ src/SMAPI/Framework/StateTracking/PlayerTracker.cs | 5 +- 4 files changed, 79 insertions(+), 29 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(); } } } diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs index 314ff7f5..bde43486 100644 --- a/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs @@ -82,6 +82,14 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers return new NetCollectionWatcher(collection); } + /// Get a watcher for a net list. + /// The value type. + /// The net list. + public static ICollectionWatcher ForNetList(NetList> collection) where T : class, INetObject + { + return new NetListWatcher(collection); + } + /// Get a watcher for a net dictionary. /// The dictionary key type. /// The dictionary value type. diff --git a/src/SMAPI/Framework/StateTracking/LocationTracker.cs b/src/SMAPI/Framework/StateTracking/LocationTracker.cs index 7445add9..519fe8f4 100644 --- a/src/SMAPI/Framework/StateTracking/LocationTracker.cs +++ b/src/SMAPI/Framework/StateTracking/LocationTracker.cs @@ -109,6 +109,9 @@ namespace StardewModdingAPI.Framework.StateTracking { foreach (IWatcher watcher in this.Watchers) watcher.Dispose(); + + foreach (var watcher in this.ChestWatchers.Values) + watcher.Dispose(); } @@ -124,7 +127,10 @@ namespace StardewModdingAPI.Framework.StateTracking foreach (KeyValuePair pair in removed) { if (pair.Value is Chest && this.ChestWatchers.TryGetValue(pair.Key, out ChestTracker watcher)) + { + watcher.Dispose(); this.ChestWatchers.Remove(pair.Key); + } } // add new watchers diff --git a/src/SMAPI/Framework/StateTracking/PlayerTracker.cs b/src/SMAPI/Framework/StateTracking/PlayerTracker.cs index cd7d75ec..d25f3345 100644 --- a/src/SMAPI/Framework/StateTracking/PlayerTracker.cs +++ b/src/SMAPI/Framework/StateTracking/PlayerTracker.cs @@ -115,9 +115,12 @@ namespace StardewModdingAPI.Framework.StateTracking } } - /// Stop watching the player fields and release all references. + /// Release watchers and resources. public void Dispose() { + this.PreviousInventory.Clear(); + this.CurrentInventory?.Clear(); + foreach (IWatcher watcher in this.Watchers) watcher.Dispose(); } -- cgit From 6766fcd0fd5f6982d1ffc91a46e0d4a5703315dc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 1 Jan 2020 00:48:44 -0500 Subject: fix NetList watcher not handling array replacement and conflicting changes correctly --- src/SMAPI/Framework/StateTracking/ChestTracker.cs | 4 +- .../StateTracking/FieldWatchers/NetListWatcher.cs | 61 +++++++++++++++++----- 2 files changed, 50 insertions(+), 15 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 66dc61eb..3ce70c17 100644 --- a/src/SMAPI/Framework/StateTracking/ChestTracker.cs +++ b/src/SMAPI/Framework/StateTracking/ChestTracker.cs @@ -57,9 +57,9 @@ namespace StardewModdingAPI.Framework.StateTracking { // update watcher this.InventoryWatcher.Update(); - foreach (Item item in this.InventoryWatcher.Added.Where(p => p != null)) + foreach (Item item in this.InventoryWatcher.Added) this.Added.Add(item); - foreach (Item item in this.InventoryWatcher.Removed.Where(p => p != null)) + foreach (Item item in this.InventoryWatcher.Removed) { 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); diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetListWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetListWatcher.cs index 8aa0eab5..0b4d3030 100644 --- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetListWatcher.cs +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetListWatcher.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Netcode; +using StardewModdingAPI.Framework.StateTracking.Comparers; namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers { @@ -15,10 +16,10 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers private readonly NetList> Field; /// The pairs added since the last reset. - private readonly IList AddedImpl = new List(); + private readonly ISet AddedImpl = new HashSet(new ObjectReferenceComparer()); /// The pairs removed since the last reset. - private readonly IList RemovedImpl = new List(); + private readonly ISet RemovedImpl = new HashSet(new ObjectReferenceComparer()); /********* @@ -81,14 +82,19 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers /// The new list of values. private void OnArrayReplaced(NetList> list, IList oldValues, IList newValues) { - this.AddedImpl.Clear(); - this.RemovedImpl.Clear(); - - foreach (TValue value in newValues) - this.AddedImpl.Add(value); + ISet oldSet = new HashSet(oldValues, new ObjectReferenceComparer()); + ISet changed = new HashSet(newValues, new ObjectReferenceComparer()); - foreach (TValue value in oldValues) - this.RemovedImpl.Add(value); + foreach (TValue value in oldSet) + { + if (!changed.Contains(value)) + this.Remove(value); + } + foreach (TValue value in changed) + { + if (!oldSet.Contains(value)) + this.Add(value); + } } /// A callback invoked when an entry is replaced. @@ -98,11 +104,40 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers /// The new value. private void OnElementChanged(NetList> list, int index, TValue oldValue, TValue newValue) { - if (newValue != null) - this.AddedImpl.Add(newValue); + this.Remove(oldValue); + this.Add(newValue); + } + + /// Track an added item. + /// The value that was added. + private void Add(TValue value) + { + if (value == null) + return; + + if (this.RemovedImpl.Contains(value)) + { + this.AddedImpl.Remove(value); + this.RemovedImpl.Remove(value); + } + else + this.AddedImpl.Add(value); + } - if (oldValue != null) - this.RemovedImpl.Add(oldValue); + /// Track a removed item. + /// The value that was removed. + private void Remove(TValue value) + { + if (value == null) + return; + + if (this.AddedImpl.Contains(value)) + { + this.AddedImpl.Remove(value); + this.RemovedImpl.Remove(value); + } + else + this.RemovedImpl.Add(value); } } } -- cgit From e64a1220e309e8fc83e20833b8849de1721ec469 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 1 Jan 2020 18:52:24 -0500 Subject: unify item diff logic for players & chests --- src/SMAPI/Events/ChestInventoryChangedEventArgs.cs | 21 +++++-- src/SMAPI/Events/InventoryChangedEventArgs.cs | 34 ++++------- src/SMAPI/Events/ItemStackChange.cs | 20 ------- src/SMAPI/Framework/SGame.cs | 12 ++-- src/SMAPI/Framework/SnapshotItemListDiff.cs | 66 ++++++++++++++++++++++ src/SMAPI/Framework/StateTracking/ChestTracker.cs | 26 ++------- src/SMAPI/Framework/StateTracking/PlayerTracker.cs | 27 +++++---- .../StateTracking/Snapshots/LocationSnapshot.cs | 7 +-- .../StateTracking/Snapshots/PlayerSnapshot.cs | 15 ++++- 9 files changed, 135 insertions(+), 93 deletions(-) delete mode 100644 src/SMAPI/Events/ItemStackChange.cs create mode 100644 src/SMAPI/Framework/SnapshotItemListDiff.cs (limited to 'src/SMAPI/Framework/StateTracking/ChestTracker.cs') diff --git a/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs b/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs index 7771cd7c..4b4c4210 100644 --- a/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs +++ b/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using StardewValley; using StardewValley.Objects; @@ -16,8 +17,14 @@ namespace StardewModdingAPI.Events /// The location containing the chest. public GameLocation Location { get; } - /// The inventory changes in the chest. - public ItemStackChange[] Changes { get; } + /// The added item stacks. + public IEnumerable Added { get; } + + /// The removed item stacks. + public IEnumerable Removed { get; } + + /// The item stacks whose size changed. + public IEnumerable QuantityChanged { get; } /********* @@ -26,12 +33,16 @@ namespace StardewModdingAPI.Events /// Construct an instance. /// The chest whose inventory changed. /// The location containing the chest. - /// The inventory changes in the chest. - internal ChestInventoryChangedEventArgs(Chest chest, GameLocation location, ItemStackChange[] changes) + /// The added item stacks. + /// The removed item stacks. + /// The item stacks whose size changed. + internal ChestInventoryChangedEventArgs(Chest chest, GameLocation location, Item[] added, Item[] removed, ItemStackSizeChange[] quantityChanged) { this.Location = location; this.Chest = chest; - this.Changes = changes; + this.Added = added; + this.Removed = removed; + this.QuantityChanged = quantityChanged; } } } diff --git a/src/SMAPI/Events/InventoryChangedEventArgs.cs b/src/SMAPI/Events/InventoryChangedEventArgs.cs index 874c2e48..40cd4128 100644 --- a/src/SMAPI/Events/InventoryChangedEventArgs.cs +++ b/src/SMAPI/Events/InventoryChangedEventArgs.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using StardewValley; namespace StardewModdingAPI.Events @@ -14,13 +13,13 @@ namespace StardewModdingAPI.Events /// The player whose inventory changed. public Farmer Player { get; } - /// The added items. + /// The added item stacks. public IEnumerable Added { get; } - /// The removed items. + /// The removed item stacks. public IEnumerable Removed { get; } - /// The items whose stack sizes changed, with the relative change. + /// The item stacks whose size changed. public IEnumerable QuantityChanged { get; } /// Whether the affected player is the local one. @@ -32,28 +31,15 @@ namespace StardewModdingAPI.Events *********/ /// Construct an instance. /// The player whose inventory changed. - /// The inventory changes. - internal InventoryChangedEventArgs(Farmer player, ItemStackChange[] changedItems) + /// The added item stacks. + /// The removed item stacks. + /// The item stacks whose size changed. + internal InventoryChangedEventArgs(Farmer player, Item[] added, Item[] removed, ItemStackSizeChange[] quantityChanged) { this.Player = player; - this.Added = changedItems - .Where(n => n.ChangeType == ChangeType.Added) - .Select(p => p.Item) - .ToArray(); - - this.Removed = changedItems - .Where(n => n.ChangeType == ChangeType.Removed) - .Select(p => p.Item) - .ToArray(); - - this.QuantityChanged = changedItems - .Where(n => n.ChangeType == ChangeType.StackChange) - .Select(change => new ItemStackSizeChange( - item: change.Item, - oldSize: change.Item.Stack - change.StackChange, - newSize: change.Item.Stack - )) - .ToArray(); + this.Added = added; + this.Removed = removed; + this.QuantityChanged = quantityChanged; } } } diff --git a/src/SMAPI/Events/ItemStackChange.cs b/src/SMAPI/Events/ItemStackChange.cs deleted file mode 100644 index cb5d2b88..00000000 --- a/src/SMAPI/Events/ItemStackChange.cs +++ /dev/null @@ -1,20 +0,0 @@ -using StardewValley; - -namespace StardewModdingAPI.Events -{ - /// Represents an inventory slot that changed. - public class ItemStackChange - { - /********* - ** Accessors - *********/ - /// The item in the slot. - public Item Item { get; set; } - - /// The amount by which the item's stack size changed. - public int StackChange { get; set; } - - /// How the inventory slot changed. - public ChangeType ChangeType { get; set; } - } -} diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index eccb6387..e2b22ba7 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -706,7 +706,10 @@ namespace StardewModdingAPI.Framework if (events.ChestInventoryChanged.HasListeners()) { foreach (var pair in locState.ChestItems) - events.ChestInventoryChanged.Raise(new ChestInventoryChangedEventArgs(pair.Key, location, pair.Value)); + { + SnapshotItemListDiff diff = pair.Value; + events.ChestInventoryChanged.Raise(new ChestInventoryChangedEventArgs(pair.Key, location, added: diff.Added, removed: diff.Removed, quantityChanged: diff.QuantityChanged)); + } } // terrain features changed @@ -747,12 +750,13 @@ namespace StardewModdingAPI.Framework } // raise player inventory changed - ItemStackChange[] changedItems = playerState.InventoryChanges.ToArray(); - if (changedItems.Any()) + if (playerState.Inventory.IsChanged) { + var inventory = playerState.Inventory; + if (this.Monitor.IsVerbose) this.Monitor.Log("Events: player inventory changed.", LogLevel.Trace); - events.InventoryChanged.Raise(new InventoryChangedEventArgs(player, changedItems)); + events.InventoryChanged.Raise(new InventoryChangedEventArgs(player, added: inventory.Added, removed: inventory.Removed, quantityChanged: inventory.QuantityChanged)); } } } diff --git a/src/SMAPI/Framework/SnapshotItemListDiff.cs b/src/SMAPI/Framework/SnapshotItemListDiff.cs new file mode 100644 index 00000000..e8ab1b1e --- /dev/null +++ b/src/SMAPI/Framework/SnapshotItemListDiff.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Linq; +using StardewModdingAPI.Events; +using StardewValley; + +namespace StardewModdingAPI.Framework +{ + /// A snapshot of a tracked item list. + internal class SnapshotItemListDiff + { + /********* + ** Accessors + *********/ + /// Whether the item list changed. + public bool IsChanged { get; } + + /// The removed values. + public Item[] Removed { get; } + + /// The added values. + public Item[] Added { get; } + + /// The items whose stack sizes changed. + public ItemStackSizeChange[] QuantityChanged { get; } + + + /********* + ** Public methods + *********/ + /// Update the snapshot. + /// The added values. + /// The removed values. + /// The items whose stack sizes changed. + public SnapshotItemListDiff(Item[] added, Item[] removed, ItemStackSizeChange[] sizesChanged) + { + this.Removed = removed; + this.Added = added; + this.QuantityChanged = sizesChanged; + + this.IsChanged = removed.Length > 0 || added.Length > 0 || sizesChanged.Length > 0; + } + + /// Get a snapshot diff if anything changed in the given data. + /// The added item stacks. + /// The removed item stacks. + /// The items with their previous stack sizes. + /// The inventory changes, or null if nothing changed. + /// Returns whether anything changed. + public static bool TryGetChanges(ISet added, ISet removed, IDictionary stackSizes, out SnapshotItemListDiff changes) + { + KeyValuePair[] sizesChanged = stackSizes.Where(p => p.Key.Stack != p.Value).ToArray(); + if (sizesChanged.Any() || added.Any() || removed.Any()) + { + changes = new SnapshotItemListDiff( + added: added.ToArray(), + removed: removed.ToArray(), + sizesChanged: sizesChanged.Select(p => new ItemStackSizeChange(p.Key, p.Value, p.Key.Stack)).ToArray() + ); + return true; + } + + changes = null; + return false; + } + } +} diff --git a/src/SMAPI/Framework/StateTracking/ChestTracker.cs b/src/SMAPI/Framework/StateTracking/ChestTracker.cs index 3ce70c17..65f58ee7 100644 --- a/src/SMAPI/Framework/StateTracking/ChestTracker.cs +++ b/src/SMAPI/Framework/StateTracking/ChestTracker.cs @@ -1,12 +1,10 @@ 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; namespace StardewModdingAPI.Framework.StateTracking { @@ -83,26 +81,12 @@ namespace StardewModdingAPI.Framework.StateTracking this.Removed.Clear(); } - /// Get the inventory changes since the last update. - public IEnumerable GetInventoryChanges() + /// Get the inventory changes since the last update, if anything changed. + /// The inventory changes, or null if nothing changed. + /// Returns whether anything changed. + public bool TryGetInventoryChanges(out SnapshotItemListDiff changes) { - // 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 }; - - // stack size changed - foreach (var entry in this.StackSizes) - { - 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 }; - } + return SnapshotItemListDiff.TryGetChanges(added: this.Added, removed: this.Removed, stackSizes: this.StackSizes, out changes); } /// Release watchers and resources. diff --git a/src/SMAPI/Framework/StateTracking/PlayerTracker.cs b/src/SMAPI/Framework/StateTracking/PlayerTracker.cs index d25f3345..cf49a7c1 100644 --- a/src/SMAPI/Framework/StateTracking/PlayerTracker.cs +++ b/src/SMAPI/Framework/StateTracking/PlayerTracker.cs @@ -2,10 +2,9 @@ using System; using System.Collections.Generic; using System.Linq; using StardewModdingAPI.Enums; -using StardewModdingAPI.Events; +using StardewModdingAPI.Framework.StateTracking.Comparers; using StardewModdingAPI.Framework.StateTracking.FieldWatchers; using StardewValley; -using ChangeType = StardewModdingAPI.Events.ChangeType; namespace StardewModdingAPI.Framework.StateTracking { @@ -99,20 +98,24 @@ namespace StardewModdingAPI.Framework.StateTracking return this.Player.currentLocation ?? this.LastValidLocation; } - /// Get the inventory changes since the last update. - public IEnumerable GetInventoryChanges() + /// Get the inventory changes since the last update, if anything changed. + /// The inventory changes, or null if nothing changed. + /// Returns whether anything changed. + public bool TryGetInventoryChanges(out SnapshotItemListDiff changes) { - IDictionary previous = this.PreviousInventory; IDictionary current = this.GetInventory(); - foreach (Item item in previous.Keys.Union(current.Keys)) + + ISet added = new HashSet(new ObjectReferenceComparer()); + ISet removed = new HashSet(new ObjectReferenceComparer()); + foreach (Item item in this.PreviousInventory.Keys.Union(current.Keys)) { - 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 }; + if (!this.PreviousInventory.ContainsKey(item)) + added.Add(item); + else if (!current.ContainsKey(item)) + removed.Add(item); } + + return SnapshotItemListDiff.TryGetChanges(added: added, removed: removed, stackSizes: this.PreviousInventory, out changes); } /// Release watchers and resources. diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs index edfba736..6ae52fd0 100644 --- a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs +++ b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; -using System.Linq; using Microsoft.Xna.Framework; -using StardewModdingAPI.Events; using StardewValley; using StardewValley.Buildings; using StardewValley.Objects; @@ -37,7 +35,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots public SnapshotListDiff> TerrainFeatures { get; } = new SnapshotListDiff>(); /// Tracks changed chest inventories. - public IDictionary ChestItems { get; } = new Dictionary(); + public IDictionary ChestItems { get; } = new Dictionary(); /********* @@ -66,8 +64,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots this.ChestItems.Clear(); foreach (ChestTracker tracker in watcher.ChestWatchers.Values) { - ItemStackChange[] changes = tracker.GetInventoryChanges().ToArray(); - if (changes.Length > 0) + if (tracker.TryGetInventoryChanges(out SnapshotItemListDiff changes)) this.ChestItems[tracker.Chest] = changes; } } diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/PlayerSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/PlayerSnapshot.cs index 7bcd9f82..f0fb9485 100644 --- a/src/SMAPI/Framework/StateTracking/Snapshots/PlayerSnapshot.cs +++ b/src/SMAPI/Framework/StateTracking/Snapshots/PlayerSnapshot.cs @@ -10,6 +10,13 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots /// A frozen snapshot of a tracked player. internal class PlayerSnapshot { + /********* + ** Fields + *********/ + /// An empty item list diff. + private readonly SnapshotItemListDiff EmptyItemListDiff = new SnapshotItemListDiff(new Item[0], new Item[0], new ItemStackSizeChange[0]); + + /********* ** Accessors *********/ @@ -27,7 +34,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots .ToDictionary(skill => skill, skill => new SnapshotDiff()); /// Get a list of inventory changes. - public IEnumerable InventoryChanges { get; private set; } + public SnapshotItemListDiff Inventory { get; private set; } /********* @@ -47,7 +54,11 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots this.Location.Update(watcher.LocationWatcher); foreach (var pair in this.Skills) pair.Value.Update(watcher.SkillWatchers[pair.Key]); - this.InventoryChanges = watcher.GetInventoryChanges().ToArray(); + + this.Inventory = watcher.TryGetInventoryChanges(out SnapshotItemListDiff itemChanges) + ? itemChanges + : this.EmptyItemListDiff; + } } } -- cgit