Skip to content

Commit ba65a39

Browse files
committed
Merge branch 'develop' into stable
2 parents 7756abe + 6bf7a3e commit ba65a39

64 files changed

Lines changed: 1020 additions & 613 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build/common.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ repo. It imports the other MSBuild files as needed.
77
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
88
<PropertyGroup>
99
<!--set general build properties -->
10-
<Version>4.3.2</Version>
10+
<Version>4.4.0</Version>
1111
<Product>SMAPI</Product>
1212
<LangVersion>latest</LangVersion>
1313
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>

build/deploy-local-smapi.targets

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ This assumes `find-game-folder.targets` has already been imported and validated.
3939
<!-- FluentHttpClient + dependencies -->
4040
<Copy SourceFiles="$(TargetDir)\Pathoschild.Http.Client.dll" DestinationFolder="$(GamePath)\smapi-internal" />
4141
<Copy SourceFiles="$(TargetDir)\System.Net.Http.Formatting.dll" DestinationFolder="$(GamePath)\smapi-internal" />
42-
43-
<!-- .NET dependencies -->
44-
<Copy SourceFiles="$(TargetDir)\System.Management.dll" DestinationFolder="$(GamePath)\smapi-internal" Condition="$(OS) == 'Windows_NT'" />
4542
</Target>
4643

4744
<!-- .NET metadata files -->

build/unix/prepare-install-package.sh

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,6 @@ for folder in ${folders[@]}; do
172172
cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config"
173173
fi
174174

175-
# copy .NET dependencies
176-
if [ $folder == "windows" ]; then
177-
cp "$smapiBin/System.Management.dll" "$bundlePath/smapi-internal"
178-
fi
179-
180175
# copy bundled mods
181176
for modName in ${bundleModNames[@]}; do
182177
fromPath="src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish"

build/windows/prepare-install-package.ps1

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,6 @@ foreach ($folder in $folders) {
197197
cp "$installAssets/windows-exe-config.xml" "$bundlePath/StardewModdingAPI.exe.config"
198198
}
199199

200-
# copy .NET dependencies
201-
if ($folder -eq "windows") {
202-
cp "$smapiBin/System.Management.dll" "$bundlePath/smapi-internal"
203-
}
204-
205200
# copy bundled mods
206201
foreach ($modName in $bundleModNames) {
207202
$fromPath = "src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish"

docs/release-notes.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,39 @@
11
[README](README.md)
22

33
# Release notes
4+
## 4.4.0
5+
Released 10 January 2026 for Stardew Valley 1.6.14 or later. See [release highlights](https://www.patreon.com/posts/147916705).
6+
7+
* For players:
8+
* Added [`set_verbose` console command](https://stardewvalleywiki.com/Modding:Console_commands#set_verbose).
9+
* The SMAPI log now shows a friendly Windows name (like "Windows 11") instead of its internal identifier.
10+
* Fixed `player_add` and `list_items` console commands not including some newer juice items.
11+
* Fixed farmhouse map edits sometimes removing the spouse room (thanks to SinZ!).
12+
* Fixed installer error if Steam has an empty game path saved to the registry.
13+
14+
* For mod authors:
15+
* Added [input API to send button presses to the game](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Input#Send_input) (thanks to martiandweller!).
16+
* Added transparency masks via `PatchMode.Mask` when editing images (thanks to PinkSerenity!).
17+
* Added support for map tilesheets referencing an asset outside `Content/Maps` using a relative `../` path (thanks to Spiderbuttons!).
18+
* Added asset propagation for spouse room map edits.
19+
* Improved performance when propagating localized assets in some cases (thanks to SinZ!).
20+
* Improved error-handling during asset propagation.
21+
* Updated dependencies, including...
22+
* [Newtonsoft.Json](https://www.newtonsoft.com/json) 13.0.3 → 13.0.4 (see [changes](https://github.com/JamesNK/Newtonsoft.Json/releases/tag/13.0.4));
23+
* [Pintail](https://github.com/Nanoray-pl/Pintail) 2.8.1 → 2.9.1 (see [changes](https://github.com/Nanoray-pl/Pintail/blob/master/docs/release-notes.md#291)).
24+
* Fixed asset propagation for farmer sprites before a save is loaded.
25+
* Removed `System.Management.dll`, which SMAPI no longer uses.
26+
27+
* For the web UI:
28+
* Improved mod compatibility list:
29+
* Added support for mod links in warnings.
30+
* Improved Content Patcher [JSON schemas](technical/web.md#using-a-schema-file-directly):
31+
* Updated for Content Patcher 2.8.0.
32+
* Fixed schema requiring `AddNPCWarps` instead of `AddNpcWarps`.
33+
* Fixed validation error if a warp field contains tokens or consecutive spaces (thanks to irocendar!).
34+
* Fixed validation error if a `Target` contains multiple targets (thanks to irocendar!).
35+
* Fixed `FromFile` errors like "_matches a schema that is not allowed_" (thanks to irocendar!).
36+
437
## 4.3.2
538
Released 14 July 2025 for Stardew Valley 1.6.14 or later. See [4.3 release highlights](https://www.patreon.com/posts/133992196).
639

src/Directory.Packages.props

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,35 @@
55

66
<ItemGroup>
77
<!-- SMAPI + toolkit -->
8-
<PackageVersion Include="HtmlAgilityPack" Version="1.12.1" />
8+
<PackageVersion Include="HtmlAgilityPack" Version="1.12.4" />
99
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.10.4" />
1010
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2" />
1111
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
1212
<PackageVersion Include="Mono.Cecil" Version="0.11.6" />
1313
<PackageVersion Include="MonoMod.Common" Version="22.3.5.1" />
14-
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
14+
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
1515
<PackageVersion Include="Pathoschild.Http.FluentClient" Version="4.4.2" />
16-
<PackageVersion Include="Pintail" Version="2.8.1" />
16+
<PackageVersion Include="Pintail" Version="2.9.1" />
1717
<PackageVersion Include="Platonymous.TMXTile" Version="1.5.9" />
18-
<PackageVersion Include="System.Management" Version="9.0.5" />
1918
<PackageVersion Include="System.Reflection.Emit" Version="4.7.0" />
2019
<PackageVersion Include="VdfConverter" Version="1.0.3" />
2120

2221
<!-- tests only -->
23-
<PackageVersion Include="FluentAssertions" Version="8.3.0" />
22+
<PackageVersion Include="FluentAssertions" Version="8.8.0" />
2423
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
2524
<PackageVersion Include="Moq" Version="4.20.72" />
26-
<PackageVersion Include="NUnit" Version="4.3.2" />
27-
<PackageVersion Include="NUnit3TestAdapter" Version="5.0.0" />
25+
<PackageVersion Include="NUnit" Version="4.4.0" />
26+
<PackageVersion Include="NUnit3TestAdapter" Version="6.0.1" />
2827

2928
<!-- web only -->
30-
<PackageVersion Include="Azure.Storage.Blobs" Version="12.24.0" />
31-
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.20" />
29+
<PackageVersion Include="Azure.Storage.Blobs" Version="12.26.0" />
30+
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.22" />
3231
<PackageVersion Include="Hangfire.Console" Version="1.4.3" />
33-
<PackageVersion Include="Hangfire.MemoryStorage" Version="1.8.1.1" />
34-
<PackageVersion Include="Humanizer.Core" Version="2.14.1" />
35-
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
36-
<PackageVersion Include="Markdig" Version="0.41.1" />
37-
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.16" />
32+
<PackageVersion Include="Hangfire.MemoryStorage" Version="1.8.1.2" />
33+
<PackageVersion Include="Humanizer.Core" Version="3.0.1" />
34+
<PackageVersion Include="JetBrains.Annotations" Version="2025.2.4" />
35+
<PackageVersion Include="Markdig" Version="0.44.0" />
36+
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.1" />
3837
<PackageVersion Include="Newtonsoft.Json.Schema" Version="4.0.1" />
3938
<PackageVersion Include="Pathoschild.FluentNexus" Version="1.0.5" />
4039
</ItemGroup>

src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,20 @@ internal class ApplySaveFixCommand : ConsoleCommand
1515
*********/
1616
/// <summary>Construct an instance.</summary>
1717
public ApplySaveFixCommand()
18-
: base("apply_save_fix", "Apply one of the game's save migrations to the currently loaded save. WARNING: This may corrupt or make permanent changes to your save. DO NOT USE THIS unless you're absolutely sure.\n\nUsage: apply_save_fix list\nList all valid save IDs.\n\nUsage: apply_save_fix <fix ID>\nApply the named save fix.") { }
18+
: base(
19+
name: "apply_save_fix",
20+
description:
21+
"""
22+
Apply one of the game's save migrations to the currently loaded save. WARNING: This may corrupt or make permanent changes to your save. DO NOT USE THIS unless you're absolutely sure.
23+
24+
Usage: apply_save_fix list
25+
List all valid save IDs.
26+
27+
Usage: apply_save_fix <fix ID>
28+
Apply the named save fix.
29+
"""
30+
)
31+
{ }
1932

2033
/// <summary>Handle the command.</summary>
2134
/// <param name="monitor">Writes messages to the console and log file.</param>

src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/RegenerateBundles.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,19 @@ internal class RegenerateBundlesCommand : ConsoleCommand
1818
*********/
1919
/// <summary>Construct an instance.</summary>
2020
public RegenerateBundlesCommand()
21-
: base("regenerate_bundles", $"Regenerate the game's community center bundle data. WARNING: this will reset all bundle progress, and may have unintended effects if you've already completed bundles. DO NOT USE THIS unless you're absolutely sure.\n\nUsage: regenerate_bundles confirm [<type>] [ignore_seed]\nRegenerate all bundles for this save. If the <type> is set to '{string.Join("' or '", Enum.GetNames(typeof(Game1.BundleType)))}', change the bundle type for the save. If an 'ignore_seed' option is included, remixed bundles are re-randomized without using the predetermined save seed.\n\nExample: regenerate_bundles remixed confirm") { }
21+
: base(
22+
name: "regenerate_bundles",
23+
description:
24+
$"""
25+
Regenerate the game's community center bundle data. WARNING: this will reset all bundle progress, and may have unintended effects if you've already completed bundles. DO NOT USE THIS unless you're absolutely sure.
26+
27+
Usage: regenerate_bundles confirm [<type>] [ignore_seed]
28+
Regenerate all bundles for this save. If the <type> is set to '{string.Join("' or '", Enum.GetNames(typeof(Game1.BundleType)))}', change the bundle type for the save. If an 'ignore_seed' option is included, remixed bundles are re-randomized without using the predetermined save seed.
29+
30+
Example: regenerate_bundles remixed confirm
31+
"""
32+
)
33+
{ }
2234

2335
/// <summary>Handle the command.</summary>
2436
/// <param name="monitor">Writes messages to the console and log file.</param>
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using StardewModdingAPI.Framework;
5+
using StardewValley.Extensions;
6+
7+
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other;
8+
9+
/// <summary>A command which toggles verbose mode in the SMAPI log.</summary>
10+
internal class SetVerboseCommand : ConsoleCommand
11+
{
12+
/*********
13+
** Fields
14+
*********/
15+
/// <summary>The mod register with which to validate mod IDs.</summary>
16+
private readonly IModRegistry ModRegistry;
17+
18+
19+
/*********
20+
** Public methods
21+
*********/
22+
/// <summary>Construct an instance.</summary>
23+
/// <param name="modRegistry">The mod register with which to validate mod IDs.</param>
24+
public SetVerboseCommand(IModRegistry modRegistry)
25+
: base(
26+
name: "set_verbose",
27+
description:
28+
"""
29+
Toggles whether more detailed information is written to the SMAPI log file (and console in developer mode). This may impact performance. This doesn't affect mods manually set to verbose in the config file.
30+
31+
Usage: set_verbose
32+
Toggles verbose logging for SMAPI and all mods.
33+
34+
Usage: set_verbose [true|false]
35+
Sets whether verbose logging is enabled (true) or disabled (false) for SMAPI and all mods.
36+
37+
Usage: set_verbose [true|false] [modId]+
38+
Sets whether verbose logging is enabled (true) or disabled (false) for the specified mod IDs. You can specify 'SMAPI' to set it for SMAPI itself.
39+
""",
40+
mayNeedUpdate: true
41+
)
42+
{
43+
this.ModRegistry = modRegistry;
44+
}
45+
46+
/// <summary>Handle the command.</summary>
47+
/// <param name="monitor">Writes messages to the console and log file.</param>
48+
/// <param name="command">The command name.</param>
49+
/// <param name="args">The command arguments.</param>
50+
public override void Handle(IMonitor monitor, string command, ArgumentParser args)
51+
{
52+
// parse mode
53+
bool setTo = !(Monitor.ForceVerboseLoggingForAll || Monitor.ForceVerboseLogging.Count > 0);
54+
if (args.Count > 0)
55+
{
56+
if (int.TryParse(args[0], out int numeric) && numeric is 0 or 1)
57+
setTo = numeric is 1;
58+
else if (!bool.TryParse(args[0], out setTo))
59+
{
60+
monitor.Log("Invalid argument: if specified, the first argument should be 'true' or 'false' to indicate whether to enable or disable verbose logging.");
61+
return;
62+
}
63+
}
64+
65+
// apply
66+
if (args.Count is 0 or 1)
67+
{
68+
if (setTo)
69+
{
70+
Monitor.ForceVerboseLoggingForAll = true;
71+
Monitor.ForceVerboseLogging.Clear();
72+
monitor.Log("Enabled verbose logs for SMAPI and all mods.", LogLevel.Info);
73+
}
74+
else
75+
{
76+
Monitor.ForceVerboseLoggingForAll = false;
77+
Monitor.ForceVerboseLogging.Clear();
78+
monitor.Log("Reset to normal.", LogLevel.Info);
79+
}
80+
}
81+
else
82+
{
83+
List<string> toggled = [];
84+
List<string> unknown = [];
85+
86+
for (int i = 1; i < args.Count; i++)
87+
{
88+
// get mod
89+
string modId = args[i];
90+
IModInfo? mod;
91+
if (modId.EqualsIgnoreCase("SMAPI"))
92+
{
93+
modId = "SMAPI";
94+
mod = null;
95+
}
96+
else
97+
{
98+
mod = this.ModRegistry.Get(modId);
99+
if (mod is null)
100+
{
101+
unknown.Add(modId);
102+
continue;
103+
}
104+
105+
modId = mod.Manifest.UniqueID;
106+
}
107+
108+
// toggle
109+
Monitor.ForceVerboseLogging.Toggle(modId, setTo);
110+
toggled.Add(mod is null ? "SMAPI" : mod.Manifest.Name);
111+
}
112+
113+
if (toggled.Count > 0)
114+
monitor.Log($"{(setTo ? "Enabled" : "Disabled")} verbose logging for mod{(toggled.Count > 1 ? "s" : "")} '{string.Join("', '", toggled.OrderBy(p => p, StringComparer.OrdinalIgnoreCase))}'.", LogLevel.Info);
115+
116+
if (unknown.Count > 0)
117+
{
118+
bool plural = unknown.Count > 1;
119+
monitor.Log($"No mod{(plural ? "s" : "")} found with ID{(plural ? "s" : "")} '{string.Join("', '", unknown)}'.", LogLevel.Warn);
120+
}
121+
}
122+
}
123+
}

src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,28 @@ internal class AddCommand : ConsoleCommand
2020
*********/
2121
/// <summary>Construct an instance.</summary>
2222
public AddCommand()
23-
: base("player_add", AddCommand.GetDescription()) { }
23+
: base(
24+
name: "player_add",
25+
description:
26+
$"""
27+
Gives the player an item.
28+
29+
Usage: player_add <item id> [count] [quality]
30+
- item id: the item ID (use the 'list_items' command to see a list).
31+
- count (optional): how many of the item to give.
32+
- quality (optional): one of {Object.lowQuality} (normal), {Object.medQuality} (silver), {Object.highQuality} (gold), or {Object.bestQuality} (iridium).
33+
34+
Usage: player_add name "<item name>" [count] [quality]
35+
- item name: the item name to search (use the 'list_items' command to see a list). This will add the item immediately if it's an exact match, else show a table of matching items.
36+
- count (optional): how many of the item to give.
37+
- quality (optional): one of {Object.lowQuality} (normal), {Object.medQuality} (silver), {Object.highQuality} (gold), or {Object.bestQuality} (iridium).
38+
39+
These examples both add the galaxy sword to your inventory:
40+
player_add weapon 4
41+
player_add name "Galaxy Sword"
42+
"""
43+
)
44+
{ }
2445

2546
/// <summary>Handle the command.</summary>
2647
/// <param name="monitor">Writes messages to the console and log file.</param>
@@ -162,27 +183,4 @@ private bool TryReadArguments(ArgumentParser args, out string? id, out string? n
162183
monitor.Log($"Multiple items have a name containing '{name}'. Do you mean one of these?\n\n{options}", LogLevel.Info);
163184
return null;
164185
}
165-
166-
/// <summary>Get the command description.</summary>
167-
private static string GetDescription()
168-
{
169-
return
170-
$"""
171-
Gives the player an item.
172-
173-
Usage: player_add <item id> [count] [quality]
174-
- item id: the item ID (use the 'list_items' command to see a list).
175-
- count (optional): how many of the item to give.
176-
- quality (optional): one of {Object.lowQuality} (normal), {Object.medQuality} (silver), {Object.highQuality} (gold), or {Object.bestQuality} (iridium).
177-
178-
Usage: player_add name "<item name>" [count] [quality]
179-
- item name: the item name to search (use the 'list_items' command to see a list). This will add the item immediately if it's an exact match, else show a table of matching items.
180-
- count (optional): how many of the item to give.
181-
- quality (optional): one of {Object.lowQuality} (normal), {Object.medQuality} (silver), {Object.highQuality} (gold), or {Object.bestQuality} (iridium).
182-
183-
These examples both add the galaxy sword to your inventory:
184-
player_add weapon 4
185-
player_add name "Galaxy Sword"
186-
""";
187-
}
188186
}

0 commit comments

Comments
 (0)