Skip to content

Commit 0209e70

Browse files
committed
Merge branch 'develop' into stable
2 parents 2ab2182 + e4cd7c8 commit 0209e70

27 files changed

+218
-133
lines changed

build/common.targets

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22
<PropertyGroup>
33
<!--set general build properties -->
4-
<Version>3.14.5</Version>
4+
<Version>3.14.6</Version>
55
<Product>SMAPI</Product>
66
<LangVersion>latest</LangVersion>
77
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>

docs/release-notes.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
[README](README.md)
22

33
# Release notes
4+
## 3.14.6
5+
Released 27 May 2022 for Stardew Valley 1.5.6 or later.
6+
7+
* For players:
8+
* Fixed error in split-screen mode when a mod provides a localized asset in one screen but not another.
9+
* Minor optimizations.
10+
411
## 3.14.5
512
Released 22 May 2022 for Stardew Valley 1.5.6 or later.
613

docs/technical/mod-package.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -412,14 +412,14 @@ The NuGet package is generated automatically in `StardewModdingAPI.ModBuildConfi
412412
when you compile it.
413413

414414
## Release notes
415-
## 4.0.1
415+
### 4.0.1
416416
Released 14 April 2022.
417417

418418
* Added detection for Xbox app game folders.
419419
* Fixed "_conflicts between different versions of Microsoft.Win32.Registry_" warnings in recent SMAPI versions.
420420
* Internal refactoring.
421421

422-
## 4.0.0
422+
### 4.0.0
423423
Released 30 November 2021.
424424

425425
* Updated for Stardew Valley 1.5.5 and SMAPI 3.13.0. (Older versions are no longer supported.)
@@ -441,7 +441,7 @@ Released 30 November 2021.
441441
* If you need to bundle extra DLLs besides your mod DLL, see the [`BundleExtraAssemblies`
442442
documentation](#configure).
443443

444-
## 3.3.0
444+
### 3.3.0
445445
Released 30 March 2021.
446446

447447
* Added a build warning when the mod isn't compiled for `Any CPU`.
@@ -450,7 +450,7 @@ Released 30 March 2021.
450450
* Added support for building mods against the 64-bit Linux version of the game on Windows.
451451
* The package now suppresses the misleading 'processor architecture mismatch' warnings.
452452

453-
## 3.2.2
453+
### 3.2.2
454454
Released 23 September 2020.
455455

456456
* Reworked and streamlined how the package is compiled.
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"Name": "Console Commands",
33
"Author": "SMAPI",
4-
"Version": "3.14.5",
4+
"Version": "3.14.6",
55
"Description": "Adds SMAPI console commands that let you manipulate the game.",
66
"UniqueID": "SMAPI.ConsoleCommands",
77
"EntryDll": "ConsoleCommands.dll",
8-
"MinimumApiVersion": "3.14.5"
8+
"MinimumApiVersion": "3.14.6"
99
}
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"Name": "Error Handler",
33
"Author": "SMAPI",
4-
"Version": "3.14.5",
4+
"Version": "3.14.6",
55
"Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.",
66
"UniqueID": "SMAPI.ErrorHandler",
77
"EntryDll": "ErrorHandler.dll",
8-
"MinimumApiVersion": "3.14.5"
8+
"MinimumApiVersion": "3.14.6"
99
}
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"Name": "Save Backup",
33
"Author": "SMAPI",
4-
"Version": "3.14.5",
4+
"Version": "3.14.6",
55
"Description": "Automatically backs up all your saves once per day into its folder.",
66
"UniqueID": "SMAPI.SaveBackup",
77
"EntryDll": "SaveBackup.dll",
8-
"MinimumApiVersion": "3.14.5"
8+
"MinimumApiVersion": "3.14.6"
99
}

src/SMAPI/Constants.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ internal static class EarlyConstants
5050
internal static int? LogScreenId { get; set; }
5151

5252
/// <summary>SMAPI's current raw semantic version.</summary>
53-
internal static string RawApiVersion = "3.14.5";
53+
internal static string RawApiVersion = "3.14.6";
5454
}
5555

5656
/// <summary>Contains SMAPI's constants and assumptions.</summary>

src/SMAPI/Framework/ContentCoordinator.cs

+19-5
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
using StardewModdingAPI.Internal;
1616
using StardewModdingAPI.Metadata;
1717
using StardewModdingAPI.Toolkit.Serialization;
18-
using StardewModdingAPI.Toolkit.Utilities;
1918
using StardewModdingAPI.Toolkit.Utilities.PathLookups;
19+
using StardewModdingAPI.Utilities;
2020
using StardewValley;
2121
using StardewValley.GameData;
2222
using xTile;
@@ -110,6 +110,10 @@ internal class ContentCoordinator : IDisposable
110110
/// <summary>The absolute path to the <see cref="ContentManager.RootDirectory"/>.</summary>
111111
public string FullRootDirectory { get; }
112112

113+
/// <summary>A lookup which tracks whether each given asset name has a localized form.</summary>
114+
/// <remarks>This is a per-screen equivalent to the base game's <see cref="LocalizedContentManager.localizedAssetNames"/> field, since mods may provide different assets per-screen.</remarks>
115+
public PerScreen<Dictionary<string, string>> LocalizedAssetNames { get; } = new(() => new());
116+
113117

114118
/*********
115119
** Public methods
@@ -245,6 +249,9 @@ public void OnLocaleChanged()
245249
{
246250
this.VanillaContentManager.Unload();
247251
});
252+
253+
// forget localized flags (to match the logic in Game1.TranslateFields, which is called on language change)
254+
this.LocalizedAssetNames.Value.Clear();
248255
}
249256

250257
/// <summary>Clean up when the player is returning to the title screen.</summary>
@@ -275,6 +282,10 @@ public void OnReturningToTitleScreen()
275282
// their changes, the assets won't be found in the cache so no changes will be propagated.
276283
if (LocalizedContentManager.CurrentLanguageCode != LocalizedContentManager.LanguageCode.en)
277284
this.InvalidateCache((contentManager, _, _) => contentManager is GameContentManager);
285+
286+
// clear the localized assets lookup (to match the logic in Game1.CleanupReturningToTitle)
287+
foreach ((_, Dictionary<string, string> localizedAssets) in this.LocalizedAssetNames.GetActiveValues())
288+
localizedAssets.Clear();
278289
}
279290

280291
/// <summary>Parse a raw asset name.</summary>
@@ -411,12 +422,15 @@ public IEnumerable<IAssetName> InvalidateCache(Func<IContentManager, string, Typ
411422
// A mod might provide a localized variant of a normally non-localized asset (like
412423
// `Maps/MovieTheater.fr-FR`). When the asset is invalidated, we need to recheck
413424
// whether the asset is localized in case it stops providing it.
414-
foreach (IAssetName assetName in invalidatedAssets.Keys)
415425
{
416-
LocalizedContentManager.localizedAssetNames.Remove(assetName.Name);
426+
Dictionary<string, string> localizedAssetNames = this.LocalizedAssetNames.Value;
427+
foreach (IAssetName assetName in invalidatedAssets.Keys)
428+
{
429+
localizedAssetNames.Remove(assetName.Name);
417430

418-
if (LocalizedContentManager.localizedAssetNames.TryGetValue(assetName.BaseName, out string? targetForBaseKey) && targetForBaseKey == assetName.Name)
419-
LocalizedContentManager.localizedAssetNames.Remove(assetName.BaseName);
431+
if (localizedAssetNames.TryGetValue(assetName.BaseName, out string? targetForBaseKey) && targetForBaseKey == assetName.Name)
432+
localizedAssetNames.Remove(assetName.BaseName);
433+
}
420434
}
421435

422436
// special case: maps may be loaded through a temporary content manager that's removed while the map is still in use.

src/SMAPI/Framework/ContentManagers/BaseContentManager.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,17 @@ public T LoadLocalized<T>(IAssetName assetName, LanguageCode language, bool useC
153153
return this.LoadExact<T>(assetName, useCache: useCache);
154154

155155
// check for localized asset
156-
if (!LocalizedContentManager.localizedAssetNames.TryGetValue(assetName.Name, out _))
156+
// ReSharper disable once LocalVariableHidesMember -- this is deliberate
157+
Dictionary<string, string> localizedAssetNames = this.Coordinator.LocalizedAssetNames.Value;
158+
if (!localizedAssetNames.TryGetValue(assetName.Name, out _))
157159
{
158160
string localeCode = this.LanguageCodeString(language);
159161
IAssetName localizedName = new AssetName(baseName: assetName.BaseName, localeCode: localeCode, languageCode: language);
160162

161163
try
162164
{
163165
T data = this.LoadExact<T>(localizedName, useCache: useCache);
164-
LocalizedContentManager.localizedAssetNames[assetName.Name] = localizedName.Name;
166+
localizedAssetNames[assetName.Name] = localizedName.Name;
165167
return data;
166168
}
167169
catch (ContentLoadException)
@@ -170,18 +172,18 @@ public T LoadLocalized<T>(IAssetName assetName, LanguageCode language, bool useC
170172
try
171173
{
172174
T data = this.LoadExact<T>(localizedName, useCache: useCache);
173-
LocalizedContentManager.localizedAssetNames[assetName.Name] = localizedName.Name;
175+
localizedAssetNames[assetName.Name] = localizedName.Name;
174176
return data;
175177
}
176178
catch (ContentLoadException)
177179
{
178-
LocalizedContentManager.localizedAssetNames[assetName.Name] = assetName.Name;
180+
localizedAssetNames[assetName.Name] = assetName.Name;
179181
}
180182
}
181183
}
182184

183185
// use cached key
184-
string rawName = LocalizedContentManager.localizedAssetNames[assetName.Name];
186+
string rawName = localizedAssetNames[assetName.Name];
185187
if (assetName.Name != rawName)
186188
assetName = this.Coordinator.ParseAssetName(rawName, allowLocales: this.TryLocalizeKeys);
187189
return this.LoadExact<T>(assetName, useCache: useCache);

src/SMAPI/Framework/ContentManagers/ModContentManager.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ private T LoadDataFile<T>(IAssetName assetName, FileInfo file)
176176
return asset;
177177
}
178178

179-
/// <summary>Load an unpacked image file (<c>.json</c>).</summary>
179+
/// <summary>Load an unpacked image file (<c>.png</c>).</summary>
180180
/// <typeparam name="T">The type of asset to load.</typeparam>
181181
/// <param name="assetName">The asset name relative to the loader root directory.</param>
182182
/// <param name="file">The file to load.</param>

src/SMAPI/Framework/Deprecations/DeprecationManager.cs

+3
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ public void PlaceholderWarn(string version, DeprecationLevel severity) { }
9595
/// <summary>Print any queued messages.</summary>
9696
public void PrintQueued()
9797
{
98+
if (!this.QueuedWarnings.Any())
99+
return;
100+
98101
foreach (DeprecationWarning warning in this.QueuedWarnings.OrderBy(p => p.ModName).ThenBy(p => p.NounPhrase))
99102
{
100103
// build message

src/SMAPI/Framework/SCore.cs

+1
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ private void OnPlayerInstanceUpdating(SGame instance, GameTime gameTime, Action
596596
/*********
597597
** Execute commands
598598
*********/
599+
if (this.ScreenCommandQueue.Value.Any())
599600
{
600601
var commandQueue = this.ScreenCommandQueue.Value;
601602
foreach ((Command? command, string? name, string[]? args) in commandQueue)

src/SMAPI/Framework/StateTracking/ChestTracker.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ internal class ChestTracker : IDisposable
3939
** Public methods
4040
*********/
4141
/// <summary>Construct an instance.</summary>
42+
/// <param name="name">A name which identifies what the watcher is watching, used for troubleshooting.</param>
4243
/// <param name="chest">The chest being tracked.</param>
43-
public ChestTracker(Chest chest)
44+
public ChestTracker(string name, Chest chest)
4445
{
4546
this.Chest = chest;
46-
this.InventoryWatcher = WatcherFactory.ForNetList(chest.items);
47+
this.InventoryWatcher = WatcherFactory.ForNetList($"{name}.{nameof(chest.items)}", chest.items);
4748

4849
this.StackSizes = this.Chest.items
4950
.Where(n => n != null)

src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs

+11-6
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,34 @@ internal class ComparableListWatcher<TValue> : BaseDisposableWatcher, ICollectio
2626
/*********
2727
** Accessors
2828
*********/
29-
/// <summary>Whether the value changed since the last reset.</summary>
29+
/// <inheritdoc />
30+
public string Name { get; }
31+
32+
/// <inheritdoc />
3033
public bool IsChanged => this.AddedImpl.Count > 0 || this.RemovedImpl.Count > 0;
3134

32-
/// <summary>The values added since the last reset.</summary>
35+
/// <inheritdoc />
3336
public IEnumerable<TValue> Added => this.AddedImpl;
3437

35-
/// <summary>The values removed since the last reset.</summary>
38+
/// <inheritdoc />
3639
public IEnumerable<TValue> Removed => this.RemovedImpl;
3740

3841

3942
/*********
4043
** Public methods
4144
*********/
4245
/// <summary>Construct an instance.</summary>
46+
/// <param name="name">A name which identifies what the watcher is watching, used for troubleshooting.</param>
4347
/// <param name="values">The collection to watch.</param>
4448
/// <param name="comparer">The equality comparer which indicates whether two values are the same.</param>
45-
public ComparableListWatcher(ICollection<TValue> values, IEqualityComparer<TValue> comparer)
49+
public ComparableListWatcher(string name, ICollection<TValue> values, IEqualityComparer<TValue> comparer)
4650
{
51+
this.Name = name;
4752
this.CurrentValues = values;
4853
this.LastValues = new HashSet<TValue>(comparer);
4954
}
5055

51-
/// <summary>Update the current value if needed.</summary>
56+
/// <inheritdoc />
5257
public void Update()
5358
{
5459
this.AssertNotDisposed();
@@ -71,7 +76,7 @@ public void Update()
7176
this.LastValues = curValues;
7277
}
7378

74-
/// <summary>Set the current value as the baseline.</summary>
79+
/// <inheritdoc />
7580
public void Reset()
7681
{
7782
this.AssertNotDisposed();

src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs

+12-7
Original file line numberDiff line numberDiff line change
@@ -20,45 +20,50 @@ internal class ComparableWatcher<TValue> : IValueWatcher<TValue>
2020
/*********
2121
** Accessors
2222
*********/
23-
/// <summary>The field value at the last reset.</summary>
23+
/// <inheritdoc />
24+
public string Name { get; }
25+
26+
/// <inheritdoc />
2427
public TValue PreviousValue { get; private set; }
2528

26-
/// <summary>The latest value.</summary>
29+
/// <inheritdoc />
2730
public TValue CurrentValue { get; private set; }
2831

29-
/// <summary>Whether the value changed since the last reset.</summary>
32+
/// <inheritdoc />
3033
public bool IsChanged { get; private set; }
3134

3235

3336
/*********
3437
** Public methods
3538
*********/
3639
/// <summary>Construct an instance.</summary>
40+
/// <param name="name">A name which identifies what the watcher is watching, used for troubleshooting.</param>
3741
/// <param name="getValue">Get the current value.</param>
3842
/// <param name="comparer">The equality comparer which indicates whether two values are the same.</param>
39-
public ComparableWatcher(Func<TValue> getValue, IEqualityComparer<TValue> comparer)
43+
public ComparableWatcher(string name, Func<TValue> getValue, IEqualityComparer<TValue> comparer)
4044
{
45+
this.Name = name;
4146
this.GetValue = getValue;
4247
this.Comparer = comparer;
4348
this.CurrentValue = getValue();
4449
this.PreviousValue = this.CurrentValue;
4550
}
4651

47-
/// <summary>Update the current value if needed.</summary>
52+
/// <inheritdoc />
4853
public void Update()
4954
{
5055
this.CurrentValue = this.GetValue();
5156
this.IsChanged = !this.Comparer.Equals(this.PreviousValue, this.CurrentValue);
5257
}
5358

54-
/// <summary>Set the current value as the baseline.</summary>
59+
/// <inheritdoc />
5560
public void Reset()
5661
{
5762
this.PreviousValue = this.CurrentValue;
5863
this.IsChanged = false;
5964
}
6065

61-
/// <summary>Release any references if needed when the field is no longer needed.</summary>
66+
/// <inheritdoc />
6267
public void Dispose() { }
6368
}
6469
}

src/SMAPI/Framework/StateTracking/FieldWatchers/ImmutableCollectionWatcher.cs

+9-6
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,29 @@ internal class ImmutableCollectionWatcher<TValue> : BaseDisposableWatcher, IColl
1313
/// <summary>A singleton collection watcher instance.</summary>
1414
public static ImmutableCollectionWatcher<TValue> Instance { get; } = new();
1515

16-
/// <summary>Whether the collection changed since the last reset.</summary>
16+
/// <inheritdoc />
17+
public string Name => nameof(ImmutableCollectionWatcher<TValue>);
18+
19+
/// <inheritdoc />
1720
public bool IsChanged { get; } = false;
1821

19-
/// <summary>The values added since the last reset.</summary>
22+
/// <inheritdoc />
2023
public IEnumerable<TValue> Added { get; } = Array.Empty<TValue>();
2124

22-
/// <summary>The values removed since the last reset.</summary>
25+
/// <inheritdoc />
2326
public IEnumerable<TValue> Removed { get; } = Array.Empty<TValue>();
2427

2528

2629
/*********
2730
** Public methods
2831
*********/
29-
/// <summary>Update the current value if needed.</summary>
32+
/// <inheritdoc />
3033
public void Update() { }
3134

32-
/// <summary>Set the current value as the baseline.</summary>
35+
/// <inheritdoc />
3336
public void Reset() { }
3437

35-
/// <summary>Stop watching the field and release all references.</summary>
38+
/// <inheritdoc />
3639
public override void Dispose() { }
3740
}
3841
}

0 commit comments

Comments
 (0)