summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/SGame.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI/Framework/SGame.cs')
-rw-r--r--src/SMAPI/Framework/SGame.cs75
1 files changed, 71 insertions, 4 deletions
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index 47261862..d6c3b836 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -12,10 +13,12 @@ using Microsoft.Xna.Framework.Graphics;
using Netcode;
using StardewModdingAPI.Enums;
using StardewModdingAPI.Events;
+using StardewModdingAPI.Framework.Content;
using StardewModdingAPI.Framework.Events;
using StardewModdingAPI.Framework.Input;
using StardewModdingAPI.Framework.Networking;
using StardewModdingAPI.Framework.Reflection;
+using StardewModdingAPI.Framework.StateTracking.Comparers;
using StardewModdingAPI.Framework.StateTracking.Snapshots;
using StardewModdingAPI.Framework.Utilities;
using StardewModdingAPI.Toolkit.Serialization;
@@ -99,7 +102,7 @@ namespace StardewModdingAPI.Framework
private WatcherCore Watchers;
/// <summary>A snapshot of the current <see cref="Watchers"/> state.</summary>
- private WatcherSnapshot WatcherSnapshot = new WatcherSnapshot();
+ private readonly WatcherSnapshot WatcherSnapshot = new WatcherSnapshot();
/// <summary>Whether post-game-startup initialization has been performed.</summary>
private bool IsInitialized;
@@ -133,6 +136,9 @@ namespace StardewModdingAPI.Framework
/// <remarks>This property must be threadsafe, since it's accessed from a separate console input thread.</remarks>
public ConcurrentQueue<string> CommandQueue { get; } = new ConcurrentQueue<string>();
+ /// <summary>Asset interceptors added or removed since the last tick.</summary>
+ private readonly List<AssetInterceptorChange> ReloadAssetInterceptorsQueue = new List<AssetInterceptorChange>();
+
/*********
** Protected methods
@@ -249,6 +255,24 @@ namespace StardewModdingAPI.Framework
this.Events.ReturnedToTitle.RaiseEmpty();
}
+ /// <summary>A callback invoked when a mod adds or removes an asset interceptor.</summary>
+ /// <param name="mod">The mod which added or removed interceptors.</param>
+ /// <param name="added">The added interceptors.</param>
+ /// <param name="removed">The removed interceptors.</param>
+ internal void OnAssetInterceptorsChanged(IModMetadata mod, IEnumerable added, IEnumerable removed)
+ {
+ if (added != null)
+ {
+ foreach (object instance in added)
+ this.ReloadAssetInterceptorsQueue.Add(new AssetInterceptorChange(mod, instance, wasAdded: true));
+ }
+ if (removed != null)
+ {
+ foreach (object instance in removed)
+ this.ReloadAssetInterceptorsQueue.Add(new AssetInterceptorChange(mod, instance, wasAdded: false));
+ }
+ }
+
/// <summary>Constructor a content manager to read XNB files.</summary>
/// <param name="serviceProvider">The service provider to use to locate services.</param>
/// <param name="rootDirectory">The root directory to search for content.</param>
@@ -405,6 +429,38 @@ namespace StardewModdingAPI.Framework
}
/*********
+ ** Reload assets when interceptors are added/removed
+ *********/
+ if (this.ReloadAssetInterceptorsQueue.Any())
+ {
+ // get unique interceptors
+ AssetInterceptorChange[] interceptors = this.ReloadAssetInterceptorsQueue
+ .GroupBy(p => p.Instance, new ObjectReferenceComparer<object>())
+ .Select(p => p.First())
+ .ToArray();
+ this.ReloadAssetInterceptorsQueue.Clear();
+
+ // log summary
+ this.Monitor.Log("Invalidating cached assets for new editors & loaders...");
+ this.Monitor.Log(
+ " changed: "
+ + string.Join(", ",
+ interceptors
+ .GroupBy(p => p.Mod)
+ .OrderBy(p => p.Key.DisplayName)
+ .Select(modGroup =>
+ $"{modGroup.Key.DisplayName} ("
+ + string.Join(", ", modGroup.GroupBy(p => p.WasAdded).ToDictionary(p => p.Key, p => p.Count()).Select(p => $"{(p.Key ? "added" : "removed")} {p.Value}"))
+ + ")"
+ )
+ )
+ );
+
+ // reload affected assets
+ this.ContentCore.InvalidateCache(asset => interceptors.Any(p => p.CanIntercept(asset)));
+ }
+
+ /*********
** Execute commands
*********/
while (this.CommandQueue.TryDequeue(out string rawInput))
@@ -654,6 +710,16 @@ namespace StardewModdingAPI.Framework
if (locState.Objects.IsChanged)
events.ObjectListChanged.Raise(new ObjectListChangedEventArgs(location, locState.Objects.Added, locState.Objects.Removed));
+ // chest items changed
+ if (events.ChestInventoryChanged.HasListeners())
+ {
+ foreach (var pair in locState.ChestItems)
+ {
+ SnapshotItemListDiff diff = pair.Value;
+ events.ChestInventoryChanged.Raise(new ChestInventoryChangedEventArgs(pair.Key, location, added: diff.Added, removed: diff.Removed, quantityChanged: diff.QuantityChanged));
+ }
+ }
+
// terrain features changed
if (locState.TerrainFeatures.IsChanged)
events.TerrainFeatureListChanged.Raise(new TerrainFeatureListChangedEventArgs(location, locState.TerrainFeatures.Added, locState.TerrainFeatures.Removed));
@@ -692,12 +758,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));
}
}
}