summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2021-01-03 14:31:27 -0500
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2021-01-03 14:31:27 -0500
commit04c6733adae9ce568aefb5d9dee6101097e994c5 (patch)
treec93f0650f6f79a95016c29526f8af437ad91a815 /src
parent48bb1581a6adeabfefbdd774011796e09a07aae2 (diff)
parent2b3f0506a16622b25a702aae250e10005287c4f4 (diff)
downloadSMAPI-04c6733adae9ce568aefb5d9dee6101097e994c5.tar.gz
SMAPI-04c6733adae9ce568aefb5d9dee6101097e994c5.tar.bz2
SMAPI-04c6733adae9ce568aefb5d9dee6101097e994c5.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src')
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/manifest.json4
-rw-r--r--src/SMAPI.Mods.SaveBackup/manifest.json4
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataField.cs1
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataFieldKey.cs5
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs5
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs3
-rw-r--r--src/SMAPI.Toolkit/Serialization/JsonHelper.cs6
-rw-r--r--src/SMAPI.Web/wwwroot/SMAPI.metadata.json215
-rw-r--r--src/SMAPI/Constants.cs2
-rw-r--r--src/SMAPI/Events/ModMessageReceivedEventArgs.cs10
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs23
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManager.cs84
-rw-r--r--src/SMAPI/Framework/CursorPosition.cs21
-rw-r--r--src/SMAPI/Framework/DeprecationManager.cs5
-rw-r--r--src/SMAPI/Framework/Input/SInputState.cs18
-rw-r--r--src/SMAPI/Framework/Logging/LogManager.cs20
-rw-r--r--src/SMAPI/Framework/ModLoading/ModResolver.cs36
-rw-r--r--src/SMAPI/Framework/SCore.cs4
-rw-r--r--src/SMAPI/Framework/SMultiplayer.cs15
-rw-r--r--src/SMAPI/ICursorPosition.cs18
20 files changed, 337 insertions, 162 deletions
diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json
index 61c610d0..13e85c70 100644
--- a/src/SMAPI.Mods.ConsoleCommands/manifest.json
+++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json
@@ -1,9 +1,9 @@
{
"Name": "Console Commands",
"Author": "SMAPI",
- "Version": "3.8.1",
+ "Version": "3.8.2",
"Description": "Adds SMAPI console commands that let you manipulate the game.",
"UniqueID": "SMAPI.ConsoleCommands",
"EntryDll": "ConsoleCommands.dll",
- "MinimumApiVersion": "3.8.1"
+ "MinimumApiVersion": "3.8.2"
}
diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json
index 7cf63e66..475ec165 100644
--- a/src/SMAPI.Mods.SaveBackup/manifest.json
+++ b/src/SMAPI.Mods.SaveBackup/manifest.json
@@ -1,9 +1,9 @@
{
"Name": "Save Backup",
"Author": "SMAPI",
- "Version": "3.8.1",
+ "Version": "3.8.2",
"Description": "Automatically backs up all your saves once per day into its folder.",
"UniqueID": "SMAPI.SaveBackup",
"EntryDll": "SaveBackup.dll",
- "MinimumApiVersion": "3.8.1"
+ "MinimumApiVersion": "3.8.2"
}
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataField.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataField.cs
index b3954693..44422f01 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataField.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataField.cs
@@ -71,6 +71,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
// non-manifest fields
case ModDataFieldKey.AlternativeUrl:
case ModDataFieldKey.StatusReasonPhrase:
+ case ModDataFieldKey.StatusReasonDetails:
case ModDataFieldKey.Status:
return false;
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataFieldKey.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataFieldKey.cs
index 09dd0cc5..068291aa 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataFieldKey.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataFieldKey.cs
@@ -13,6 +13,9 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
Status,
/// <summary>A reason phrase for the <see cref="Status"/>, or <c>null</c> to use the default reason.</summary>
- StatusReasonPhrase
+ StatusReasonPhrase,
+
+ /// <summary>Technical details shown in TRACE logs for the <see cref="Status"/>, or <c>null</c> to omit it.</summary>
+ StatusReasonDetails
}
}
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs
index 3201c421..f28f6afe 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs
@@ -107,6 +107,11 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
case ModDataFieldKey.StatusReasonPhrase:
parsed.StatusReasonPhrase = field.Value;
break;
+
+ // status technical reason
+ case ModDataFieldKey.StatusReasonDetails:
+ parsed.StatusReasonDetails = field.Value;
+ break;
}
}
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs
index 598da66a..f0282eb4 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs
@@ -24,6 +24,9 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// <summary>A reason phrase for the <see cref="Status"/>, or <c>null</c> to use the default reason.</summary>
public string StatusReasonPhrase { get; set; }
+ /// <summary>Technical details shown in TRACE logs for the <see cref="Status"/>, or <c>null</c> to omit it.</summary>
+ public string StatusReasonDetails { get; set; }
+
/// <summary>The upper version for which the <see cref="Status"/> applies (if any).</summary>
public ISemanticVersion StatusUpperVersion { get; set; }
}
diff --git a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
index 031afbb0..00db9903 100644
--- a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
+++ b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
@@ -132,5 +132,11 @@ namespace StardewModdingAPI.Toolkit.Serialization
{
return JsonConvert.SerializeObject(model, formatting, this.JsonSettings);
}
+
+ /// <summary>Get a low-level JSON serializer matching the <see cref="JsonSettings"/>.</summary>
+ public JsonSerializer GetSerializer()
+ {
+ return JsonSerializer.CreateDefault(this.JsonSettings);
+ }
}
}
diff --git a/src/SMAPI.Web/wwwroot/SMAPI.metadata.json b/src/SMAPI.Web/wwwroot/SMAPI.metadata.json
index 2a81e12a..e75a6bc0 100644
--- a/src/SMAPI.Web/wwwroot/SMAPI.metadata.json
+++ b/src/SMAPI.Web/wwwroot/SMAPI.metadata.json
@@ -42,6 +42,9 @@
* - StatusReasonPhrase: a message to show to the player explaining why the mod can't be loaded
* (if applicable). If blank, will default to a generic not-compatible message.
*
+ * - StatusReasonDetails: a technical reason shown in TRACE logs, indicating why the status
+ * was overridden. If not provided, it defaults to the StatusReasonPhrase or 'no reason given'.
+ *
* - AlternativeUrl: a URL where the player can find an unofficial update or alternative if the
* mod is no longer compatible.
*/
@@ -76,8 +79,7 @@
"JSON Assets": {
"ID": "spacechase0.JsonAssets",
- "Default | UpdateKey": "Nexus:1720",
- "1.3.1 | Status": "AssumeBroken" // causes runtime crashes
+ "Default | UpdateKey": "Nexus:1720"
},
"Mail Framework": {
@@ -87,8 +89,7 @@
"MTN": {
"ID": "SgtPickles.MTN",
- "Default | UpdateKey": "Nexus:2256",
- "~1.2.6 | Status": "AssumeBroken" // replaces Game1.multiplayer, which breaks SMAPI's multiplayer API.
+ "Default | UpdateKey": "Nexus:2256"
},
"PyTK": {
@@ -157,42 +158,76 @@
},
/*********
- ** Broke in SDV 1.5
+ ** Broke in SDV 1.5 (Content Patcher packs)
+ *********/
+ "mi.Mermaids": {
+ "ID": "mi.Mermaids",
+ "~1.0.0 | Status": "AssumeBroken",
+ "~1.0.0 | StatusReasonDetails": "causes errors due to removed Stardew Valley 1.5 content"
+ },
+
+ /*********
+ ** Broke in SDV 1.5 (SMAPI mods)
*********/
"Audio Devices": {
"ID": "maxvollmer.audiodevices",
- "~2.0.0 | Status": "AssumeBroken" // causes crash to desktop when starting the game
+ "~2.0.0 | Status": "AssumeBroken",
+ "~2.0.0 | StatusReasonDetails": "causes crash to desktop when starting the game"
+ },
+
+ "ChestEx": {
+ "ID": "berkayylmao.ChestEx",
+ "~1.3.4 | Status": "AssumeBroken",
+ "~1.3.4 | StatusReasonDetails": "has no effect due to changes in Stardew Valley 1.5, causes crashes in other mods like Chests Anywhere"
},
+ "Custom Furniture": {
+ "ID": "Platonymous.CustomFurniture",
+ "~0.11.2 | Status": "AssumeBroken",
+ "~0.11.2 | StatusReasonDetails": "causes errors and custom furniture no longer work in Stardew Valley 1.5"
+ },
+
"Custom Localization": {
"ID": "ZaneYork.CustomLocalization",
"FormerIDs": "SMAPI.CustomLocalization", // changed in 1.0.1
- "~1.1 | Status": "AssumeBroken" // reflection error for _localizedAssets field
+ "~1.1 | Status": "AssumeBroken",
+ "~1.1 | StatusReasonDetails": "reflection error due to renamed _localizedAssets field"
+ },
+
+ "Geode Info Menu": {
+ "ID": "cat.geodeinfomenu",
+ "~1.5.2 | Status": "AssumeBroken",
+ "~1.5.2 | StatusReasonDetails": "shows no info, freezes game if you try to search"
},
"Mod Settings Tab": {
"ID": "GilarF.ModSettingsTab",
- "~0.2.1 | Status": "AssumeBroken" // fails extending title menu
+ "~0.2.1 | Status": "AssumeBroken",
+ "~0.2.1 | StatusReasonDetails": "fails extending title menu"
},
"More Grass": {
"ID": "EpicBellyFlop45.MoreGrass",
- "~1.0.8 | Status": "AssumeBroken" // crashes save load
+ "~1.0.8 | Status": "AssumeBroken",
+ "~1.0.8 | StatusReasonDetails": "crashes on save load"
},
"Movement Speed": {
"ID": "bcmpinc.MovementSpeed",
- "~3.0.0 | Status": "AssumeBroken" // transpiler errors
+ "~3.0.0 | Status": "AssumeBroken",
+ "~3.0.0 | StatusReasonDetails": "broken due to transpiler errors"
},
"Tree Spread": {
"ID": "bcmpinc.TreeSpread",
- "~3.0.0 | Status": "AssumeBroken" // transpiler errors
+ "~3.0.0 | Status": "AssumeBroken",
+ "~3.0.0 | StatusReasonDetails": "broken due to transpiler errors"
},
"TreeTransplant": {
"ID": "TreeTransplant",
- "~1.0.9 | Status": "AssumeBroken" // causes AccessViolationException which prevents game launch
+ "~1.0.9 | Status": "AssumeBroken",
+ "~3.0.0 | StatusReasonDetails": "breaks game launch due to AccessViolationException"
},
/*********
@@ -200,17 +235,14 @@
*********/
"Auto Quality Patch": {
"ID": "SilentOak.AutoQualityPatch",
- "~2.1.3-unofficial.7-mizzion | Status": "AssumeBroken" // runtime errors
+ "~2.1.3-unofficial.7-mizzion | Status": "AssumeBroken",
+ "~2.1.3-unofficial.7-mizzion | StatusReasonDetails": "broken due to runtime errors"
},
"Fix Dice": {
"ID": "ashley.fixdice",
- "~1.1.2 | Status": "AssumeBroken" // crashes game on startup
- },
-
- "Grass Growth": {
- "ID": "bcmpinc.GrassGrowth",
- "~1.0 | Status": "AssumeBroken"
+ "~1.1.2 | Status": "AssumeBroken",
+ "~1.1.2 | StatusReasonDetails": "crashes game on startup"
},
"Invite Code Mod": {
@@ -238,11 +270,6 @@
"~2.15 | Status": "AssumeBroken"
},
- "Yet Another Harvest With Scythe Mod": {
- "ID": "bcmpinc.HarvestWithScythe",
- "~1.1 | Status": "AssumeBroken"
- },
-
/*********
** Broke in SMAPI 3.0 (runtime errors due to lifecycle changes)
*********/
@@ -251,16 +278,6 @@
"~1.0.0 | Status": "AssumeBroken"
},
- "Arcade 2048": {
- "ID": "Platonymous.2048",
- "~1.0.6 | Status": "AssumeBroken" // possibly due to PyTK
- },
-
- "Arcade Snake": {
- "ID": "Platonymous.Snake",
- "~1.1.0 | Status": "AssumeBroken" // possibly due to PyTK
- },
-
"Better Sprinklers": {
"ID": "Speeder.BetterSprinklers",
"~2.3.1-unofficial.7-pathoschild | Status": "AssumeBroken"
@@ -278,12 +295,8 @@
"Decrafting Mod": {
"ID": "MSCFC.DecraftingMod",
- "~1.0 | Status": "AssumeBroken" // NRE in ModEntry
- },
-
- "JoJaBan - Arcade Sokoban": {
- "ID": "Platonymous.JoJaBan",
- "~0.4.3 | Status": "AssumeBroken" // possibly due to PyTK
+ "~1.0 | Status": "AssumeBroken",
+ "~1.0 | StatusReasonDetails": "fails due to NullReferenceException in ModEntry"
},
"Level Extender": {
@@ -301,11 +314,6 @@
"~1.5 | Status": "AssumeBroken"
},
- "Seed Bag": {
- "ID": "Platonymous.SeedBag",
- "~1.2.7 | Status": "AssumeBroken" // possibly due to PyTK
- },
-
"Stardew Valley ESP": {
"ID": "reimu.sdv-helper",
"~1.1 | Status": "AssumeBroken"
@@ -313,12 +321,14 @@
"Underdark Krobus": {
"ID": "melnoelle.underdarkkrobus",
- "~1.0.0 | Status": "AssumeBroken" // NRE in ModEntry
+ "~1.0 | Status": "AssumeBroken",
+ "~1.0 | StatusReasonDetails": "fails due to NullReferenceException in ModEntry"
},
"Underdark Sewer": {
"ID": "melnoelle.underdarksewer",
- "~1.1.0 | Status": "AssumeBroken" // NRE in ModEntry
+ "~1.1.0 | Status": "AssumeBroken",
+ "~1.1.0 | StatusReasonDetails": "fails due to NullReferenceException in ModEntry"
},
/*********
@@ -327,60 +337,70 @@
"2cute FarmCave": {
"ID": "taintedwheat.2CuteFarmCave",
"Default | UpdateKey": "Nexus:843",
- "~2.0 | Status": "AssumeBroken" // references deleted Content/Mine.xnb
+ "~2.0 | Status": "AssumeBroken",
+ "~2.0 | StatusReasonDetails": "references the deleted Content/Mine asset"
},
"Ace's Expanded Caves - Default Cave": {
"ID": "Acerbicon.AECdefault",
"Default | UpdateKey": "Nexus:2131",
- "~1.2.2 | Status": "AssumeBroken" // references deleted Content/Mine.xnb
+ "~1.2.2 | Status": "AssumeBroken",
+ "~1.2.2 | StatusReasonDetails": "references the deleted Content/Mine asset"
},
"Ace's Expanded Caves - Desert Cave": {
"ID": "Acerbicon.AECdesert",
"Default | UpdateKey": "Nexus:2131",
- "~1.2.2 | Status": "AssumeBroken" // references deleted Content/Mine.xnb
+ "~1.2.2 | Status": "AssumeBroken",
+ "~1.2.2 | StatusReasonDetails": "references the deleted Content/Mine asset"
},
"Ace's Expanded Caves - Ice Cave": {
"ID": "Acerbicon.AECice",
"Default | UpdateKey": "Nexus:2131",
- "~1.2.2 | Status": "AssumeBroken" // references deleted Content/Mine.xnb
+ "~1.2.2 | Status": "AssumeBroken",
+ "~1.2.2 | StatusReasonDetails": "references the deleted Content/Mine asset"
},
"Ace's Expanded Caves - Lava Cave": {
"ID": "Acerbicon.AEClava",
"Default | UpdateKey": "Nexus:2131",
- "~1.2.2 | Status": "AssumeBroken" // references deleted Content/Mine.xnb
+ "~1.2.2 | Status": "AssumeBroken",
+ "~1.2.2 | StatusReasonDetails": "references the deleted Content/Mine asset"
},
"Ace's Expanded Caves - Slime Cave": {
"ID": "Acerbicon.AECslime",
"Default | UpdateKey": "Nexus:2131",
- "~1.2.2 | Status": "AssumeBroken" // references deleted Content/Mine.xnb
+ "~1.2.2 | Status": "AssumeBroken",
+ "~1.2.2 | StatusReasonDetails": "references the deleted Content/Mine asset"
},
"Green Pastures Farm": {
"ID": "bugbuddy.GreenPasturesFarm",
"Default | UpdateKey": "Nexus:2326",
- "~1.0 | Status": "AssumeBroken" // references deleted Content/weapons.xnb
+ "~1.0 | Status": "AssumeBroken",
+ "~1.0 | StatusReasonDetails": "references the deleted Content/weapons asset"
},
"Immersive Farm 2": {
"ID": "zander.immersivefarm2",
- "~2.0.1 | Status": "AssumeBroken" // references deleted Content/Mine.xnb
+ "~2.0.1 | Status": "AssumeBroken",
+ "~2.0.1 | StatusReasonDetails": "references the deleted Content/Mine asset"
},
"Karmylla's Immersive Map Edits": {
"ID": "Karmylla.ImmersiveMapEdits",
"Default | UpdateKey": "Nexus:1149",
- "~2.4 | Status": "AssumeBroken" // references deleted Content/weapons.xnb
+ "~2.4 | Status": "AssumeBroken",
+ "~2.4 | StatusReasonDetails": "references the deleted Content/weapons asset"
},
"Secret Gardens Greenhouse": {
"ID": "jessebot.secretgardens",
"Default | UpdateKey": "Nexus:3067",
- "~2.0.1 | Status": "AssumeBroken" // references deleted Content/Mine.xnb
+ "~2.0.1 | Status": "AssumeBroken",
+ "~2.0.1 | StatusReasonDetails": "references the deleted Content/Mine asset"
},
/*********
@@ -388,102 +408,55 @@
*********/
"Canon-Friendly Dialogue Expansion": {
"ID": "gizzymo.canonfriendlyexpansion",
- "~1.1.1 | Status": "AssumeBroken" // causes a save crash on certain dates
+ "~1.1.1 | Status": "AssumeBroken",
+ "~1.1.1 | StatusReasonDetails": "causes a save crash on certain dates"
},
"Everytime Submarine": {
"ID": "MustafaDemirel.EverytimeSubmarine",
- "~1.0.0 | Status": "AssumeBroken" // breaks player saves if their beach bridge is fixed
- },
-
- "Always Scroll Map": {
- "ID": "bcmpinc.AlwaysScrollMap",
- "~0.6 | Status": "AssumeBroken" // breaks newer versions of bcmpinc mods (per bcmpinc's request)
- },
-
- "Arcade Pong": {
- "ID": "Platonymous.ArcadePong",
- "~1.0.2 | Status": "AssumeBroken" // broke in SMAPI 2.6-beta.16 due to reflection into SMAPI internals
+ "~1.0.0 | Status": "AssumeBroken",
+ "~1.1.1 | StatusReasonDetails": "breaks player saves if their beach bridge is fixed"
},
"BJS Night Sounds": {
"ID": "BunnyJumps.BJSNightSounds",
- "~1.0.0 | Status": "AssumeBroken" // runtime errors with Harmony 1.2.0.1 in SMAPI 2.8+
- },
-
- "Craft Counter": {
- "ID": "bcmpinc.CraftCounter",
- "~0.6 | Status": "AssumeBroken" // breaks newer versions of bcmpinc mods (per bcmpinc's request)
+ "~1.0.0 | Status": "AssumeBroken",
+ "~1.0.0 | StatusReasonDetails": "runtime errors with Harmony 1.2.0.1 in SMAPI 2.8+"
},
"Fishing Adjust": {
"ID": "shuaiz.FishingAdjustMod",
- "~2.0.1 | Status": "AssumeBroken" // Method not found: 'Void Harmony.HarmonyInstance.Patch(System.Reflection.MethodBase, Harmony.HarmonyMethod, Harmony.HarmonyMethod, Harmony.HarmonyMethod)'
+ "~2.0.1 | Status": "AssumeBroken",
+ "~2.0.1 | StatusReasonDetails": "fails with 'method not found' error for 'Void Harmony.HarmonyInstance.Patch(System.Reflection.MethodBase, Harmony.HarmonyMethod, Harmony.HarmonyMethod, Harmony.HarmonyMethod)'"
},
"Fishing Automaton": {
"ID": "Drynwynn.FishingAutomaton",
- "~1.1 | Status": "AssumeBroken" // runtime errors with Harmony 1.2.0.1 in SMAPI 2.8+
- },
-
- "Fix Animal Tools": {
- "ID": "bcmpinc.FixAnimalTools",
- "~0.6 | Status": "AssumeBroken" // breaks newer versions of bcmpinc mods (per bcmpinc's request)
- },
-
- "Fix Scythe Exp": {
- "ID": "bcmpinc.FixScytheExp",
- "~0.3 | Status": "AssumeBroken" // broke in 1.3: Exception from HarmonyInstance "bcmpinc.FixScytheExp" [...] Bad label content in ILGenerator.
+ "~1.1 | Status": "AssumeBroken",
+ "~1.1 | StatusReasonDetails": "runtime errors with Harmony 1.2.0.1 in SMAPI 2.8+"
},
"More Silo Storage": {
"ID": "OrneryWalrus.MoreSiloStorage",
- "~1.0.1 | Status": "AssumeBroken" // broke in SDV 1.3
+ "~1.0.1 | Status": "AssumeBroken"
},
"No Added Flying Mine Monsters": {
"ID": "Drynwynn.NoAddedFlyingMineMonsters",
- "~1.1 | Status": "AssumeBroken" // runtime errors with Harmony 1.2.0.1 in SMAPI 2.8+
+ "~1.1 | Status": "AssumeBroken",
+ "~1.1 | StatusReasonDetails": "runtime errors with Harmony 1.2.0.1 in SMAPI 2.8+"
},
"Server Bookmarker": {
"ID": "Ilyaki.ServerBookmarker",
- "~1.0.0 | Status": "AssumeBroken" // broke in Stardew Valley 1.3.29 (runtime errors)
- },
-
- "Skull Cave Saver": {
- "ID": "cantorsdust.SkullCaveSaver",
- "FormerIDs": "8ac06349-26f7-4394-806c-95d48fd35774 | community.SkullCaveSaver", // changed in 1.1 and 1.2.2
- "1.3-beta | Status": "AssumeBroken" // doesn't work in multiplayer, no longer maintained
- },
-
- "Stardew Hack": {
- "ID": "bcmpinc.StardewHack",
- "~0.6 | Status": "AssumeBroken" // breaks newer versions of bcmpinc mods (per bcmpinc's request)
+ "~1.0.0 | Status": "AssumeBroken",
+ "~1.0.0 | StatusReasonDetails": "runtime errors in Stardew Valley 1.3.29"
},
"Stephan's Lots of Crops": {
"ID": "stephansstardewcrops",
- "~1.1 | Status": "AssumeBroken" // broke in SDV 1.3 (overwrites vanilla items)
- },
-
- "Summit Reborn": {
- "ID": "KoihimeNakamura.summitreborn",
- "FormerIDs": "emissaryofinfinity.summitreborn", // changed in 1.0.2
- "~1.0.2 | Status": "AssumeBroken" // broke in SDV 1.3 (runtime errors)
- },
-
- "Tilled Soil Decay": {
- "ID": "bcmpinc.TilledSoilDecay",
- "~0.6 | Status": "AssumeBroken" // breaks newer versions of bcmpinc mods (per bcmpinc's request)
- },
-
- /*********
- ** Broke circa SDV 1.2
- *********/
- "Move Faster": {
- "ID": "shuaiz.MoveFasterMod",
- "~1.0.1 | Status": "AssumeBroken" // doesn't do anything as of SDV 1.2.33 (bad Harmony patch?)
+ "~1.1 | Status": "AssumeBroken",
+ "~1.1 | StatusReasonDetails": "causes errors due to overwritten Stardew Valley 1.3 items"
}
}
}
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index 9d5501a3..ef996c0f 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -54,7 +54,7 @@ namespace StardewModdingAPI
** Public
****/
/// <summary>SMAPI's current semantic version.</summary>
- public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.8.1");
+ public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.8.2");
/// <summary>The minimum supported version of Stardew Valley.</summary>
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.1");
diff --git a/src/SMAPI/Events/ModMessageReceivedEventArgs.cs b/src/SMAPI/Events/ModMessageReceivedEventArgs.cs
index d4370028..d75a7540 100644
--- a/src/SMAPI/Events/ModMessageReceivedEventArgs.cs
+++ b/src/SMAPI/Events/ModMessageReceivedEventArgs.cs
@@ -1,5 +1,6 @@
using System;
using StardewModdingAPI.Framework.Networking;
+using StardewModdingAPI.Toolkit.Serialization;
namespace StardewModdingAPI.Events
{
@@ -12,6 +13,9 @@ namespace StardewModdingAPI.Events
/// <summary>The underlying message model.</summary>
private readonly ModMessageModel Message;
+ /// <summary>The JSON helper used to deserialize models.</summary>
+ private readonly JsonHelper JsonHelper;
+
/*********
** Accessors
@@ -31,16 +35,18 @@ namespace StardewModdingAPI.Events
*********/
/// <summary>Construct an instance.</summary>
/// <param name="message">The received message.</param>
- internal ModMessageReceivedEventArgs(ModMessageModel message)
+ /// <param name="jsonHelper">The JSON helper used to deserialize models.</param>
+ internal ModMessageReceivedEventArgs(ModMessageModel message, JsonHelper jsonHelper)
{
this.Message = message;
+ this.JsonHelper = jsonHelper;
}
/// <summary>Read the message data into the given model type.</summary>
/// <typeparam name="TModel">The message model type.</typeparam>
public TModel ReadAs<TModel>()
{
- return this.Message.Data.ToObject<TModel>();
+ return this.Message.Data.ToObject<TModel>(this.JsonHelper.GetSerializer());
}
}
}
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs
index f9027972..3d5bb29d 100644
--- a/src/SMAPI/Framework/ContentCoordinator.cs
+++ b/src/SMAPI/Framework/ContentCoordinator.cs
@@ -54,6 +54,9 @@ namespace StardewModdingAPI.Framework
/// <remarks>The game may adds content managers in asynchronous threads (e.g. when populating the load screen).</remarks>
private readonly ReaderWriterLockSlim ContentManagerLock = new ReaderWriterLockSlim();
+ /// <summary>An unmodified content manager which doesn't intercept assets, used to compare asset data.</summary>
+ private readonly LocalizedContentManager VanillaContentManager;
+
/*********
** Accessors
@@ -95,6 +98,7 @@ namespace StardewModdingAPI.Framework
this.ContentManagers.Add(
this.MainContentManager = new GameContentManager("Game1.content", serviceProvider, rootDirectory, currentCulture, this, monitor, reflection, this.OnDisposing, onLoadingFirstAsset)
);
+ this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
this.CoreAssets = new CoreAssetPropagator(this.MainContentManager.AssertAndNormalizeAssetName, reflection);
}
@@ -150,6 +154,8 @@ namespace StardewModdingAPI.Framework
{
foreach (IContentManager contentManager in this.ContentManagers)
contentManager.OnLocaleChanged();
+
+ this.VanillaContentManager.Unload();
});
}
@@ -287,6 +293,23 @@ namespace StardewModdingAPI.Framework
});
}
+ /// <summary>Get a vanilla asset without interception.</summary>
+ /// <typeparam name="T">The type of asset to load.</typeparam>
+ /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
+ public bool TryLoadVanillaAsset<T>(string assetName, out T asset)
+ {
+ try
+ {
+ asset = this.VanillaContentManager.Load<T>(assetName);
+ return true;
+ }
+ catch
+ {
+ asset = default;
+ return false;
+ }
+ }
+
/// <summary>Dispose held resources.</summary>
public void Dispose()
{
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
index ad8f2ef1..424d6ff3 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
@@ -11,6 +11,7 @@ using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Framework.Utilities;
using StardewValley;
using xTile;
+using xTile.Tiles;
namespace StardewModdingAPI.Framework.ContentManagers
{
@@ -308,15 +309,10 @@ namespace StardewModdingAPI.Framework.ContentManagers
return null;
}
- // validate asset
- if (data == null)
- {
- mod.LogAsMod($"Mod incorrectly set asset '{info.AssetName}' to a null value; ignoring override.", LogLevel.Error);
- return null;
- }
-
// return matched asset
- return new AssetDataForObject(info, data, this.AssertAndNormalizeAssetName);
+ return this.TryValidateLoadedAsset(info, data, mod)
+ ? new AssetDataForObject(info, data, this.AssertAndNormalizeAssetName)
+ : null;
}
/// <summary>Apply any <see cref="Editors"/> to a loaded asset.</summary>
@@ -386,5 +382,77 @@ namespace StardewModdingAPI.Framework.ContentManagers
// return result
return asset;
}
+
+ /// <summary>Validate that an asset loaded by a mod is valid and won't cause issues.</summary>
+ /// <typeparam name="T">The asset type.</typeparam>
+ /// <param name="info">The basic asset metadata.</param>
+ /// <param name="data">The loaded asset data.</param>
+ /// <param name="mod">The mod which loaded the asset.</param>
+ private bool TryValidateLoadedAsset<T>(IAssetInfo info, T data, IModMetadata mod)
+ {
+ // can't load a null asset
+ if (data == null)
+ {
+ mod.LogAsMod($"SMAPI blocked asset replacement for '{info.AssetName}': mod incorrectly set asset to a null value.", LogLevel.Error);
+ return false;
+ }
+
+ // when replacing a map, the vanilla tilesheets must have the same order and IDs
+ if (data is Map loadedMap && this.Coordinator.TryLoadVanillaAsset(info.AssetName, out Map vanillaMap))
+ {
+ for (int i = 0; i < vanillaMap.TileSheets.Count; i++)
+ {
+ // check for match
+ TileSheet vanillaSheet = vanillaMap.TileSheets[i];
+ bool found = this.TryFindTilesheet(loadedMap, vanillaSheet.Id, out int loadedIndex, out TileSheet loadedSheet);
+ if (found && loadedIndex == i)
+ continue;
+
+ // handle mismatch
+ {
+ // only show warning if not farm map
+ // This is temporary: mods shouldn't do this for any vanilla map, but these are the ones we know will crash. Showing a warning for others instead gives modders time to update their mods, while still simplifying troubleshooting.
+ bool isFarmMap = info.AssetNameEquals("Maps/Farm") || info.AssetNameEquals("Maps/Farm_Combat") || info.AssetNameEquals("Maps/Farm_Fishing") || info.AssetNameEquals("Maps/Farm_Foraging") || info.AssetNameEquals("Maps/Farm_FourCorners") || info.AssetNameEquals("Maps/Farm_Island") || info.AssetNameEquals("Maps/Farm_Mining");
+
+
+ string reason = found
+ ? $"mod reordered the original tilesheets, which {(isFarmMap ? "would cause a crash" : "often causes crashes")}.\n\nTechnical details for mod author:\nExpected order [{string.Join(", ", vanillaMap.TileSheets.Select(p => $"'{p.ImageSource}' (id: {p.Id})"))}], but found tilesheet '{vanillaSheet.Id}' at index {loadedIndex} instead of {i}. Make sure custom tilesheet IDs are prefixed with 'z_' to avoid reordering tilesheets."
+ : $"mod has no tilesheet with ID '{vanillaSheet.Id}'. Map replacements must keep the original tilesheets to avoid errors or crashes.";
+
+ SCore.DeprecationManager.PlaceholderWarn("3.8.2", DeprecationLevel.PendingRemoval);
+ if (isFarmMap)
+ {
+ mod.LogAsMod($"SMAPI blocked asset replacement for