From dad67e213e68eb85c534d7c1c4035dfde90ff822 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 4 Dec 2018 23:16:13 -0500 Subject: fix world events in the mines (#603) --- .../FieldWatchers/ComparableListWatcher.cs | 82 ++++++++++++++++++++++ .../StateTracking/FieldWatchers/WatcherFactory.cs | 8 +++ .../StateTracking/WorldLocationsTracker.cs | 50 +++++++++---- 3 files changed, 125 insertions(+), 15 deletions(-) create mode 100644 src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs (limited to 'src/SMAPI/Framework/StateTracking') diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs new file mode 100644 index 00000000..95e9ef16 --- /dev/null +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.Linq; + +namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers +{ + /// A watcher which detects changes to a collection of values using a specified instance. + internal class ComparableListWatcher : BaseDisposableWatcher, ICollectionWatcher + { + /********* + ** Properties + *********/ + /// The collection to watch. + private readonly ICollection CurrentValues; + + /// The values during the previous update. + private HashSet LastValues; + + /// The pairs added since the last reset. + private readonly List AddedImpl = new List(); + + /// The pairs demoved since the last reset. + private readonly List RemovedImpl = new List(); + + + /********* + ** Accessors + *********/ + /// Whether the value changed since the last reset. + public bool IsChanged => this.AddedImpl.Count > 0 || this.RemovedImpl.Count > 0; + + /// The values added since the last reset. + public IEnumerable Added => this.AddedImpl; + + /// The values removed since the last reset. + public IEnumerable Removed => this.RemovedImpl; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The collection to watch. + /// The equality comparer which indicates whether two values are the same. + public ComparableListWatcher(ICollection values, IEqualityComparer comparer) + { + this.CurrentValues = values; + this.LastValues = new HashSet(comparer); + } + + /// Update the current value if needed. + public void Update() + { + this.AssertNotDisposed(); + + // optimise for zero items + if (this.CurrentValues.Count == 0) + { + if (this.LastValues.Count > 0) + { + this.AddedImpl.AddRange(this.LastValues); + this.LastValues.Clear(); + } + return; + } + + // detect changes + HashSet curValues = new HashSet(this.CurrentValues, this.LastValues.Comparer); + this.RemovedImpl.AddRange(from value in this.LastValues where !curValues.Contains(value) select value); + this.AddedImpl.AddRange(from value in curValues where !this.LastValues.Contains(value) select value); + this.LastValues = curValues; + } + + /// Set the current value as the baseline. + public void Reset() + { + this.AssertNotDisposed(); + + this.AddedImpl.Clear(); + this.RemovedImpl.Clear(); + } + } +} diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs index ab4ab0d5..8301351e 100644 --- a/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs @@ -36,6 +36,14 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers return new ComparableWatcher(getValue, new ObjectReferenceComparer()); } + /// Get a watcher which detects when an object reference in a collection changes. + /// The value type. + /// The observable collection. + public static ComparableListWatcher ForReferenceList(ICollection collection) + { + return new ComparableListWatcher(collection, new ObjectReferenceComparer()); + } + /// Get a watcher for an observable collection. /// The value type. /// The observable collection. diff --git a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs index 5a259663..d9d598f8 100644 --- a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs +++ b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -19,6 +18,9 @@ namespace StardewModdingAPI.Framework.StateTracking /// Tracks changes to the location list. private readonly ICollectionWatcher LocationListWatcher; + /// Tracks changes to the list of active mine locations. + private readonly ICollectionWatcher MineLocationListWatcher; + /// A lookup of the tracked locations. private IDictionary LocationDict { get; } = new Dictionary(new ObjectReferenceComparer()); @@ -50,24 +52,34 @@ namespace StardewModdingAPI.Framework.StateTracking *********/ /// Construct an instance. /// The game's list of locations. - public WorldLocationsTracker(ObservableCollection locations) + /// The game's list of active mine locations. + public WorldLocationsTracker(ObservableCollection locations, IList activeMineLocations) { this.LocationListWatcher = WatcherFactory.ForObservableCollection(locations); + this.MineLocationListWatcher = WatcherFactory.ForReferenceList(activeMineLocations); } /// Update the current value if needed. public void Update() { - // detect location changes + // detect added/removed locations + this.LocationListWatcher.Update(); + this.MineLocationListWatcher.Update(); if (this.LocationListWatcher.IsChanged) { this.Remove(this.LocationListWatcher.Removed); this.Add(this.LocationListWatcher.Added); } + if (this.MineLocationListWatcher.IsChanged) + { + this.Remove(this.MineLocationListWatcher.Removed); + this.Add(this.MineLocationListWatcher.Added); + } - // detect building changes + // detect building changed foreach (LocationTracker watcher in this.Locations.ToArray()) { + watcher.Update(); if (watcher.BuildingsWatcher.IsChanged) { this.Remove(watcher.BuildingsWatcher.Removed); @@ -75,7 +87,7 @@ namespace StardewModdingAPI.Framework.StateTracking } } - // detect building interior changed (e.g. construction completed) + // detect building interiors changed (e.g. construction completed) foreach (KeyValuePair pair in this.BuildingIndoors.Where(p => !object.Equals(p.Key.indoors.Value, p.Value))) { GameLocation oldIndoors = pair.Value; @@ -86,10 +98,6 @@ namespace StardewModdingAPI.Framework.StateTracking if (newIndoors != null) this.Removed.Add(newIndoors); } - - // update watchers - foreach (IWatcher watcher in this.Locations) - watcher.Update(); } /// Set the current location list as the baseline. @@ -98,21 +106,21 @@ namespace StardewModdingAPI.Framework.StateTracking this.Removed.Clear(); this.Added.Clear(); this.LocationListWatcher.Reset(); + this.MineLocationListWatcher.Reset(); } /// Set the current value as the baseline. public void Reset() { this.ResetLocationList(); - foreach (IWatcher watcher in this.Locations) + foreach (IWatcher watcher in this.GetWatchers()) watcher.Reset(); } /// Stop watching the player fields and release all references. public void Dispose() { - this.LocationListWatcher.Dispose(); - foreach (IWatcher watcher in this.Locations) + foreach (IWatcher watcher in this.GetWatchers()) watcher.Dispose(); } @@ -180,11 +188,11 @@ namespace StardewModdingAPI.Framework.StateTracking // remove old location if needed this.Remove(location); - // track change + // add location this.Added.Add(location); - - // add this.LocationDict[location] = new LocationTracker(location); + + // add buildings if (location is BuildableGameLocation buildableLocation) this.Add(buildableLocation.buildings); } @@ -219,5 +227,17 @@ namespace StardewModdingAPI.Framework.StateTracking this.Remove(buildableLocation.buildings); } } + + /**** + ** Helpers + ****/ + /// The underlying watchers. + private IEnumerable GetWatchers() + { + yield return this.LocationListWatcher; + yield return this.MineLocationListWatcher; + foreach (LocationTracker watcher in this.Locations) + yield return watcher; + } } } -- cgit From 699fc41a7d72ec680c35ab36f3e18d54639d9b93 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 4 Dec 2018 23:31:39 -0500 Subject: cleanup, add release note --- docs/release-notes.md | 1 + .../Framework/Commands/World/SetTimeCommand.cs | 2 +- .../StateTracking/FieldWatchers/ComparableListWatcher.cs | 3 ++- .../StateTracking/FieldWatchers/ComparableWatcher.cs | 13 +++++++------ .../StateTracking/FieldWatchers/NetCollectionWatcher.cs | 3 ++- .../StateTracking/FieldWatchers/NetDictionaryWatcher.cs | 2 +- .../StateTracking/FieldWatchers/NetValueWatcher.cs | 14 ++++++++------ .../FieldWatchers/ObservableCollectionWatcher.cs | 3 ++- 8 files changed, 24 insertions(+), 17 deletions(-) (limited to 'src/SMAPI/Framework/StateTracking') diff --git a/docs/release-notes.md b/docs/release-notes.md index 5788fa9f..d7cb6680 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -11,6 +11,7 @@ * Added `IsLocalPlayer` to new player events. * Reloading a map asset will now update affected locations. * Reloading the `Data\NPCDispositions` asset will now update affected NPCs. + * Fixed world events (like `ObjectListChanged`) not working in the mines. * Fixed some map tilesheets not editable if not playing in English. * Fixed newlines in most manifest fields not being ignored. * Fixed `Display.RenderedWorld` event not invoked before overlays are rendered. diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs index 7644ee46..a6075013 100644 --- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs @@ -47,7 +47,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World { // define conversion between game time and TimeSpan TimeSpan ToTimeSpan(int value) => new TimeSpan(0, value / 100, value % 100, 0); - int FromTimeSpan(TimeSpan span) => (int)((span.Hours * 100) + span.Minutes); + int FromTimeSpan(TimeSpan span) => (span.Hours * 100) + span.Minutes; // transition to new time int intervals = (int)((ToTimeSpan(time) - ToTimeSpan(Game1.timeOfDay)).TotalMinutes / 10); diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs index 95e9ef16..2ea6609a 100644 --- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs @@ -4,6 +4,7 @@ using System.Linq; namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers { /// A watcher which detects changes to a collection of values using a specified instance. + /// The value type within the collection. internal class ComparableListWatcher : BaseDisposableWatcher, ICollectionWatcher { /********* @@ -18,7 +19,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers /// The pairs added since the last reset. private readonly List AddedImpl = new List(); - /// The pairs demoved since the last reset. + /// The pairs removed since the last reset. private readonly List RemovedImpl = new List(); diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs index d51fc2ac..dda30a15 100644 --- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs @@ -4,26 +4,27 @@ using System.Collections.Generic; namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers { /// A watcher which detects changes to a value using a specified instance. - internal class ComparableWatcher : IValueWatcher + /// The comparable value type. + internal class ComparableWatcher : IValueWatcher { /********* ** Properties *********/ /// Get the current value. - private readonly Func GetValue; + private readonly Func GetValue; /// The equality comparer. - private readonly IEqualityComparer Comparer; + private readonly IEqualityComparer Comparer; /********* ** Accessors *********/ /// The field value at the last reset. - public T PreviousValue { get; private set; } + public TValue PreviousValue { get; private set; } /// The latest value. - public T CurrentValue { get; private set; } + public TValue CurrentValue { get; private set; } /// Whether the value changed since the last reset. public bool IsChanged { get; private set; } @@ -35,7 +36,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers /// Construct an instance. /// Get the current value. /// The equality comparer which indicates whether two values are the same. - public ComparableWatcher(Func getValue, IEqualityComparer comparer) + public ComparableWatcher(Func getValue, IEqualityComparer comparer) { this.GetValue = getValue; this.Comparer = comparer; diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs index 8a841a79..d3022a69 100644 --- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs @@ -4,6 +4,7 @@ using Netcode; namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers { /// A watcher which detects changes to a Netcode collection. + /// The value type within the collection. internal class NetCollectionWatcher : BaseDisposableWatcher, ICollectionWatcher where TValue : class, INetObject { @@ -16,7 +17,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers /// The pairs added since the last reset. private readonly List AddedImpl = new List(); - /// The pairs demoved since the last reset. + /// The pairs removed since the last reset. private readonly List RemovedImpl = new List(); diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs index 7a2bf84e..7a7ab89d 100644 --- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs @@ -20,7 +20,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers /// The pairs added since the last reset. private readonly IDictionary PairsAdded = new Dictionary(); - /// The pairs demoved since the last reset. + /// The pairs removed since the last reset. private readonly IDictionary PairsRemoved = new Dictionary(); /// The field being watched. diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs index 188ed9f3..85099988 100644 --- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs @@ -3,13 +3,15 @@ using Netcode; namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers { /// A watcher which detects changes to a net value field. - internal class NetValueWatcher : BaseDisposableWatcher, IValueWatcher where TSelf : NetFieldBase + /// The value type wrapped by the net field. + /// The net field type. + internal class NetValueWatcher : BaseDisposableWatcher, IValueWatcher where TNetField : NetFieldBase { /********* ** Properties *********/ /// The field being watched. - private readonly NetFieldBase Field; + private readonly NetFieldBase Field; /********* @@ -19,10 +21,10 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers public bool IsChanged { get; private set; } /// The field value at the last reset. - public T PreviousValue { get; private set; } + public TValue PreviousValue { get; private set; } /// The latest value. - public T CurrentValue { get; private set; } + public TValue CurrentValue { get; private set; } /********* @@ -30,7 +32,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers *********/ /// Construct an instance. /// The field to watch. - public NetValueWatcher(NetFieldBase field) + public NetValueWatcher(NetFieldBase field) { this.Field = field; this.PreviousValue = field.Value; @@ -74,7 +76,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers /// The field being watched. /// The old field value. /// The new field value. - private void OnValueChanged(TSelf field, T oldValue, T newValue) + private void OnValueChanged(TNetField field, TValue oldValue, TValue newValue) { this.CurrentValue = newValue; this.IsChanged = true; diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs index 34a97097..0c65789f 100644 --- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs +++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs @@ -6,6 +6,7 @@ using System.Linq; namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers { /// A watcher which detects changes to an observable collection. + /// The value type within the collection. internal class ObservableCollectionWatcher : BaseDisposableWatcher, ICollectionWatcher { /********* @@ -17,7 +18,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers /// The pairs added since the last reset. private readonly List AddedImpl = new List(); - /// The pairs demoved since the last reset. + /// The pairs removed since the last reset. private readonly List RemovedImpl = new List(); -- cgit