fix NetList watcher not handling array replacement and conflicting changes correctly

This commit is contained in:
Jesse Plamondon-Willard 2020-01-01 00:48:44 -05:00
parent c5cfcc6c9f
commit 6766fcd0fd
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
2 changed files with 50 additions and 15 deletions

View File

@ -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);

View File

@ -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<TValue, NetRef<TValue>> Field;
/// <summary>The pairs added since the last reset.</summary>
private readonly IList<TValue> AddedImpl = new List<TValue>();
private readonly ISet<TValue> AddedImpl = new HashSet<TValue>(new ObjectReferenceComparer<TValue>());
/// <summary>The pairs removed since the last reset.</summary>
private readonly IList<TValue> RemovedImpl = new List<TValue>();
private readonly ISet<TValue> RemovedImpl = new HashSet<TValue>(new ObjectReferenceComparer<TValue>());
/*********
@ -81,14 +82,19 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
/// <param name="newValues">The new list of values.</param>
private void OnArrayReplaced(NetList<TValue, NetRef<TValue>> list, IList<TValue> oldValues, IList<TValue> newValues)
{
this.AddedImpl.Clear();
this.RemovedImpl.Clear();
ISet<TValue> oldSet = new HashSet<TValue>(oldValues, new ObjectReferenceComparer<TValue>());
ISet<TValue> changed = new HashSet<TValue>(newValues, new ObjectReferenceComparer<TValue>());
foreach (TValue value in newValues)
this.AddedImpl.Add(value);
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);
}
}
/// <summary>A callback invoked when an entry is replaced.</summary>
@ -98,11 +104,40 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
/// <param name="newValue">The new value.</param>
private void OnElementChanged(NetList<TValue, NetRef<TValue>> list, int index, TValue oldValue, TValue newValue)
{
if (newValue != null)
this.AddedImpl.Add(newValue);
this.Remove(oldValue);
this.Add(newValue);
}
if (oldValue != null)
this.RemovedImpl.Add(oldValue);
/// <summary>Track an added item.</summary>
/// <param name="value">The value that was added.</param>
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);
}
/// <summary>Track a removed item.</summary>
/// <param name="value">The value that was removed.</param>
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);
}
}
}