From 104aa314127bd816d5dbcac8c57ecb84b12f20d1 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 14 Mar 2017 20:48:02 -0400 Subject: [PATCH] let players override SMAPI incompatible-code detection if needed --- README.md | 2 +- .../Framework/AssemblyLoader.cs | 20 ++-- .../Framework/ModRegistry.cs | 20 ++-- ...IncompatibleMod.cs => ModCompatibility.cs} | 10 +- .../Framework/Models/ModCompatibilityType.cs | 12 +++ .../Framework/Models/SConfig.cs | 4 +- src/StardewModdingAPI/Program.cs | 8 +- .../StardewModdingAPI.config.json | 95 ++++++++++++------- .../StardewModdingAPI.csproj | 3 +- 9 files changed, 111 insertions(+), 63 deletions(-) rename src/StardewModdingAPI/Framework/Models/{IncompatibleMod.cs => ModCompatibility.cs} (86%) create mode 100644 src/StardewModdingAPI/Framework/Models/ModCompatibilityType.cs diff --git a/README.md b/README.md index 87383ffb..c90e62cc 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ field | purpose ----- | ------- `DeveloperMode` | Default `false` (except in _SMAPI for developers_ releases). Whether to enable features intended for mod developers. Currently this only makes `TRACE`-level messages appear in the console. `CheckForUpdates` | Default `true`. Whether SMAPI should check for a newer version when you load the game. If a new version is available, a small message will appear in the console. This doesn't affect the load time even if your connection is offline or slow, because it happens in the background. -`IncompatibleMods` | A list of mod versions SMAPI considers incompatible and will refuse to load. Changing this field is not recommended. +`ModCompatibility` | A list of mod versions SMAPI should consider compatible or broken regardless of whether it detects incompatible code. Each record can be set to `AssumeCompatible` or `AssumeBroken`. Changing this field is not recommended and may destabilise your game. ### Command-line arguments SMAPI recognises the following command-line arguments. These are intended for internal use and may diff --git a/src/StardewModdingAPI/Framework/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/AssemblyLoader.cs index bc56de01..c7ad3da4 100644 --- a/src/StardewModdingAPI/Framework/AssemblyLoader.cs +++ b/src/StardewModdingAPI/Framework/AssemblyLoader.cs @@ -54,9 +54,10 @@ namespace StardewModdingAPI.Framework /// Preprocess and load an assembly. /// The assembly file path. + /// Assume the mod is compatible, even if incompatible code is detected. /// Returns the rewrite metadata for the preprocessed assembly. /// An incompatible CIL instruction was found while rewriting the assembly. - public Assembly Load(string assemblyPath) + public Assembly Load(string assemblyPath, bool assumeCompatible) { // get referenced local assemblies AssemblyParseResult[] assemblies; @@ -73,7 +74,7 @@ namespace StardewModdingAPI.Framework Assembly lastAssembly = null; foreach (AssemblyParseResult assembly in assemblies) { - bool changed = this.RewriteAssembly(assembly.Definition); + bool changed = this.RewriteAssembly(assembly.Definition, assumeCompatible); if (changed) { this.Monitor.Log($"Loading {assembly.File.Name} (rewritten in memory)...", LogLevel.Trace); @@ -159,12 +160,13 @@ namespace StardewModdingAPI.Framework ****/ /// Rewrite the types referenced by an assembly. /// The assembly to rewrite. + /// Assume the mod is compatible, even if incompatible code is detected. /// Returns whether the assembly was modified. /// An incompatible CIL instruction was found while rewriting the assembly. - private bool RewriteAssembly(AssemblyDefinition assembly) + private bool RewriteAssembly(AssemblyDefinition assembly, bool assumeCompatible) { ModuleDefinition module = assembly.MainModule; - HashSet loggedRewrites = new HashSet(); + HashSet loggedMessages = new HashSet(); // swap assembly references if needed (e.g. XNA => MonoGame) bool platformChanged = false; @@ -173,7 +175,7 @@ namespace StardewModdingAPI.Framework // remove old assembly reference if (this.AssemblyMap.RemoveNames.Any(name => module.AssemblyReferences[i].Name == name)) { - this.LogOnce(this.Monitor, loggedRewrites, $"Rewriting {assembly.Name.Name} for OS..."); + this.LogOnce(this.Monitor, loggedMessages, $"Rewriting {assembly.Name.Name} for OS..."); platformChanged = true; module.AssemblyReferences.RemoveAt(i); i--; @@ -203,13 +205,17 @@ namespace StardewModdingAPI.Framework // throw exception if instruction is incompatible but can't be rewritten IInstructionFinder finder = finders.FirstOrDefault(p => p.IsMatch(instruction, platformChanged)); if (finder != null) - throw new IncompatibleInstructionException(finder.NounPhrase, $"Found an incompatible CIL instruction ({finder.NounPhrase}) while loading assembly {assembly.Name.Name}."); + { + if (!assumeCompatible) + throw new IncompatibleInstructionException(finder.NounPhrase, $"Found an incompatible CIL instruction ({finder.NounPhrase}) while loading assembly {assembly.Name.Name}."); + this.LogOnce(this.Monitor, loggedMessages, $"Found an incompatible CIL instruction ({finder.NounPhrase}) while loading assembly {assembly.Name.Name}, but SMAPI is configured to allow it anyway. The mod may crash or behave unexpectedly.", LogLevel.Warn); + } // rewrite instruction if needed IInstructionRewriter rewriter = rewriters.FirstOrDefault(p => p.IsMatch(instruction, platformChanged)); if (rewriter != null) { - this.LogOnce(this.Monitor, loggedRewrites, $"Rewriting {assembly.Name.Name} to fix {rewriter.NounPhrase}..."); + this.LogOnce(this.Monitor, loggedMessages, $"Rewriting {assembly.Name.Name} to fix {rewriter.NounPhrase}..."); rewriter.Rewrite(module, cil, instruction, this.AssemblyMap); anyRewritten = true; } diff --git a/src/StardewModdingAPI/Framework/ModRegistry.cs b/src/StardewModdingAPI/Framework/ModRegistry.cs index 233deb3c..f015b7ba 100644 --- a/src/StardewModdingAPI/Framework/ModRegistry.cs +++ b/src/StardewModdingAPI/Framework/ModRegistry.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; -using System.Text.RegularExpressions; using StardewModdingAPI.Framework.Models; namespace StardewModdingAPI.Framework @@ -20,18 +19,18 @@ namespace StardewModdingAPI.Framework /// The friendly mod names treated as deprecation warning sources (assembly full name => mod name). private readonly IDictionary ModNamesByAssembly = new Dictionary(); - /// The mod versions which should be disabled due to incompatibility. - private readonly IncompatibleMod[] IncompatibleMods; + /// Metadata about mods that SMAPI should assume is compatible or broken, regardless of whether it detects incompatible code. + private readonly ModCompatibility[] CompatibilityRecords; /********* ** Public methods *********/ /// Construct an instance. - /// The mod versions which should be disabled due to incompatibility. - public ModRegistry(IEnumerable incompatibleMods) + /// Metadata about mods that SMAPI should assume is compatible or broken, regardless of whether it detects incompatible code. + public ModRegistry(IEnumerable compatibilityRecords) { - this.IncompatibleMods = incompatibleMods.ToArray(); + this.CompatibilityRecords = compatibilityRecords.ToArray(); } @@ -127,21 +126,20 @@ namespace StardewModdingAPI.Framework return null; } - /// Get a record indicating why a mod is incompatible (if applicable). + /// Get metadata that indicates whether SMAPI should assume the mod is compatible or broken, regardless of whether it detects incompatible code. /// The mod manifest. /// Returns the incompatibility record if applicable, else null. - internal IncompatibleMod GetIncompatibilityRecord(IManifest manifest) + internal ModCompatibility GetCompatibilityRecord(IManifest manifest) { string key = !string.IsNullOrWhiteSpace(manifest.UniqueID) ? manifest.UniqueID : manifest.EntryDll; return ( - from mod in this.IncompatibleMods + from mod in this.CompatibilityRecords where mod.ID == key && (mod.LowerSemanticVersion == null || !manifest.Version.IsOlderThan(mod.LowerSemanticVersion)) && !manifest.Version.IsNewerThan(mod.UpperSemanticVersion) - && (string.IsNullOrWhiteSpace(mod.ForceCompatibleVersion) || !Regex.IsMatch(manifest.Version.ToString(), mod.ForceCompatibleVersion, RegexOptions.IgnoreCase)) select mod ).FirstOrDefault(); } } -} \ No newline at end of file +} diff --git a/src/StardewModdingAPI/Framework/Models/IncompatibleMod.cs b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs similarity index 86% rename from src/StardewModdingAPI/Framework/Models/IncompatibleMod.cs rename to src/StardewModdingAPI/Framework/Models/ModCompatibility.cs index 29e18ddb..1e71dae0 100644 --- a/src/StardewModdingAPI/Framework/Models/IncompatibleMod.cs +++ b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs @@ -3,8 +3,8 @@ using Newtonsoft.Json; namespace StardewModdingAPI.Framework.Models { - /// Contains abstract metadata about an incompatible mod. - internal class IncompatibleMod + /// Metadata about a mod version that SMAPI should assume is compatible or broken, regardless of whether it detects incompatible code. + internal class ModCompatibility { /********* ** Accessors @@ -30,13 +30,13 @@ namespace StardewModdingAPI.Framework.Models /// The URL the user can check for an unofficial updated version. public string UnofficialUpdateUrl { get; set; } - /// A regular expression matching version strings to consider compatible, even if they technically precede . - public string ForceCompatibleVersion { get; set; } - /// The reason phrase to show in the warning, or null to use the default value. /// "this version is incompatible with the latest version of the game" public string ReasonPhrase { get; set; } + /// Indicates how SMAPI should consider the mod. + public ModCompatibilityType Compatibility { get; set; } + /**** ** Injected diff --git a/src/StardewModdingAPI/Framework/Models/ModCompatibilityType.cs b/src/StardewModdingAPI/Framework/Models/ModCompatibilityType.cs new file mode 100644 index 00000000..35edec5e --- /dev/null +++ b/src/StardewModdingAPI/Framework/Models/ModCompatibilityType.cs @@ -0,0 +1,12 @@ +namespace StardewModdingAPI.Framework.Models +{ + /// Indicates how SMAPI should consider a mod. + internal enum ModCompatibilityType + { + /// Assume the mod is not compatible, even if SMAPI doesn't detect any incompatible code. + AssumeBroken = 0, + + /// Assume the mod is compatible, even if SMAPI detects incompatible code. + AssumeCompatible = 1 + } +} diff --git a/src/StardewModdingAPI/Framework/Models/SConfig.cs b/src/StardewModdingAPI/Framework/Models/SConfig.cs index 558da82a..0de96297 100644 --- a/src/StardewModdingAPI/Framework/Models/SConfig.cs +++ b/src/StardewModdingAPI/Framework/Models/SConfig.cs @@ -12,7 +12,7 @@ /// Whether to check if a newer version of SMAPI is available on startup. public bool CheckForUpdates { get; set; } = true; - /// A list of mod versions which should be considered incompatible. - public IncompatibleMod[] IncompatibleMods { get; set; } + /// A list of mod versions which should be considered compatible or incompatible regardless of whether SMAPI detects incompatible code. + public ModCompatibility[] ModCompatibility { get; set; } } } diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index db7a3df6..ac646b1f 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -78,7 +78,7 @@ namespace StardewModdingAPI // initialise this.Monitor = new Monitor("SMAPI", this.ConsoleManager, this.LogFile, this.ExitGameImmediately) { WriteToConsole = writeToConsole }; - this.ModRegistry = new ModRegistry(this.Settings.IncompatibleMods); + this.ModRegistry = new ModRegistry(this.Settings.ModCompatibility); this.DeprecationManager = new DeprecationManager(this.Monitor, this.ModRegistry); } @@ -364,8 +364,8 @@ namespace StardewModdingAPI skippedPrefix = $"Skipped {manifest.Name}"; // validate compatibility - IncompatibleMod compatibility = this.ModRegistry.GetIncompatibilityRecord(manifest); - if (compatibility != null) + ModCompatibility compatibility = this.ModRegistry.GetCompatibilityRecord(manifest); + if (compatibility?.Compatibility == ModCompatibilityType.AssumeBroken) { bool hasOfficialUrl = !string.IsNullOrWhiteSpace(compatibility.UpdateUrl); bool hasUnofficialUrl = !string.IsNullOrWhiteSpace(compatibility.UnofficialUpdateUrl); @@ -433,7 +433,7 @@ namespace StardewModdingAPI Assembly modAssembly; try { - modAssembly = modAssemblyLoader.Load(assemblyPath); + modAssembly = modAssemblyLoader.Load(assemblyPath, assumeCompatible: compatibility?.Compatibility == ModCompatibilityType.AssumeCompatible); } catch (IncompatibleInstructionException ex) { diff --git a/src/StardewModdingAPI/StardewModdingAPI.config.json b/src/StardewModdingAPI/StardewModdingAPI.config.json index 9ecf2912..31514a21 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.config.json +++ b/src/StardewModdingAPI/StardewModdingAPI.config.json @@ -9,7 +9,7 @@ generally shouldn't change this file unless necessary. { "DeveloperMode": true, "CheckForUpdates": true, - "IncompatibleMods": [ + "ModCompatibility": [ /* versions which crash the game */ { "Name": "NPC Map Locations", @@ -17,7 +17,8 @@ generally shouldn't change this file unless necessary. "LowerVersion": "1.42", "UpperVersion": "1.43", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/239", - "ReasonPhrase": "this version has an update check error which crashes the game" + "ReasonPhrase": "this version has an update check error which crashes the game", + "Compatibility": "AssumeBroken" }, /* versions not compatible with Stardew Valley 1.1+ */ @@ -25,7 +26,8 @@ generally shouldn't change this file unless necessary. "Name": "Chest Label System", "ID": "SPDChestLabel", "UpperVersion": "1.5", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/242" + "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/242", + "Compatibility": "AssumeBroken" }, /* versions not compatible with Stardew Valley 1.2+ */ @@ -35,14 +37,16 @@ generally shouldn't change this file unless necessary. "UpperVersion": "1.1", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/257", "UnofficialUpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/518", - "Notes": "Crashes with 'Method not found: Void StardewValley.Item.set_Name(System.String)'." + "Notes": "Crashes with 'Method not found: Void StardewValley.Item.set_Name(System.String)'.", + "Compatibility": "AssumeBroken" }, { "Name": "Almighty Tool", "ID": "AlmightyTool.dll", "UpperVersion": "1.1.1", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/439", - "Notes": "Uses obsolete StardewModdingAPI.Extensions." + "Notes": "Uses obsolete StardewModdingAPI.Extensions.", + "Compatibility": "AssumeBroken" }, { "Name": "Better Sprinklers", @@ -50,189 +54,216 @@ generally shouldn't change this file unless necessary. "UpperVersion": "2.3", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/41", "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/125031", - "Notes": "Uses obsolete StardewModdingAPI.Extensions." + "Notes": "Uses obsolete StardewModdingAPI.Extensions.", + "Compatibility": "AssumeBroken" }, { "Name": "Casks Anywhere", "ID": "CasksAnywhere", "UpperVersion": "1.1", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/878", - "Notes": "Uses obsolete StardewModdingAPI.Inheritance.ItemStackChange." + "Notes": "Uses obsolete StardewModdingAPI.Inheritance.ItemStackChange.", + "Compatibility": "AssumeBroken" }, { "Name": "Chests Anywhere", "ID": "ChestsAnywhere", "UpperVersion": "1.8.2", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/518", - "Notes": "Crashes with 'Method not found: Void StardewValley.Menus.TextBox.set_Highlighted(Boolean)'." + "Notes": "Crashes with 'Method not found: Void StardewValley.Menus.TextBox.set_Highlighted(Boolean)'.", + "Compatibility": "AssumeBroken" }, { "Name": "Chests Anywhere", "ID": "Pathoschild.ChestsAnywhere", "UpperVersion": "1.9-beta", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/518", - "Notes": "ID changed in 1.9. Crashes with InvalidOperationException: 'The menu doesn't seem to have a player inventory'." + "Notes": "ID changed in 1.9. Crashes with InvalidOperationException: 'The menu doesn't seem to have a player inventory'.", + "Compatibility": "AssumeBroken" }, { "Name": "CJB Automation", "ID": "CJBAutomation", "UpperVersion": "1.4", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/211", - "Notes": "Crashes with 'Method not found: Void StardewValley.Item.set_Name(System.String)'." + "Notes": "Crashes with 'Method not found: Void StardewValley.Item.set_Name(System.String)'.", + "Compatibility": "AssumeBroken" }, { "Name": "CJB Cheats Menu", "ID": "CJBCheatsMenu", "UpperVersion": "1.13", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/4", - "Notes": "Uses removed Game1.borderFont." + "Notes": "Uses removed Game1.borderFont.", + "Compatibility": "AssumeBroken" }, { "Name": "CJB Item Spawner", "ID": "CJBItemSpawner", "UpperVersion": "1.6", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/93", - "Notes": "Uses removed Game1.borderFont." + "Notes": "Uses removed Game1.borderFont.", + "Compatibility": "AssumeBroken" }, { "Name": "Cooking Skill", "ID": "CookingSkill", "UpperVersion": "1.0.3", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/522", - "Notes": "Crashes with 'Method not found: Void StardewValley.Buff..ctor(Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, System.String)'." + "Notes": "Crashes with 'Method not found: Void StardewValley.Buff..ctor(Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, System.String)'.", + "Compatibility": "AssumeBroken" }, { "Name": "Enemy Health Bars", "ID": "SPDHealthBar", "UpperVersion": "1.7", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/193", - "Notes": "Uses obsolete GraphicsEvents.DrawTick." + "Notes": "Uses obsolete GraphicsEvents.DrawTick.", + "Compatibility": "AssumeBroken" }, { "Name": "Entoarox Framework", "ID": "eacdb74b-4080-4452-b16b-93773cda5cf9", "UpperVersion": "1.6.5", "UpdateUrl": "http://community.playstarbound.com/resources/4228", - "Notes": "Uses obsolete StardewModdingAPI.Inheritance.SObject until 1.6.1; then crashes until 1.6.4 ('Entoarox Framework requested an immediate game shutdown: Fatal error attempting to update player tick properties System.NullReferenceException: Object reference not set to an instance of an object. at Entoarox.Framework.PlayerHelper.Update(Object s, EventArgs e)')." + "Notes": "Uses obsolete StardewModdingAPI.Inheritance.SObject until 1.6.1; then crashes until 1.6.4 ('Entoarox Framework requested an immediate game shutdown: Fatal error attempting to update player tick properties System.NullReferenceException: Object reference not set to an instance of an object. at Entoarox.Framework.PlayerHelper.Update(Object s, EventArgs e)').", + "Compatibility": "AssumeBroken" }, { "Name": "Extended Fridge", "ID": "Mystra007ExtendedFridge", "UpperVersion": "1.0", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/485", - "Notes": "Actual upper version is 0.94, but mod incorrectly sets it to 1.0 in the manifest. Crashes with 'Field not found: StardewValley.Game1.mouseCursorTransparency'." + "Notes": "Actual upper version is 0.94, but mod incorrectly sets it to 1.0 in the manifest. Crashes with 'Field not found: StardewValley.Game1.mouseCursorTransparency'.", + "Compatibility": "AssumeBroken" }, { "Name": "Get Dressed", "ID": "GetDressed.dll", "UpperVersion": "3.2", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/331", - "Notes": "Crashes with NullReferenceException in GameEvents.UpdateTick." + "Notes": "Crashes with NullReferenceException in GameEvents.UpdateTick.", + "Compatibility": "AssumeBroken" }, { "Name": "Lookup Anything", "ID": "LookupAnything", "UpperVersion": "1.10", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/541", - "Notes": "Crashes with FormatException when looking up NPCs." + "Notes": "Crashes with FormatException when looking up NPCs.", + "Compatibility": "AssumeBroken" }, { "Name": "Lookup Anything", "ID": "Pathoschild.LookupAnything", "UpperVersion": "1.10.1", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/541", - "Notes": "ID changed in 1.10.1. Crashes with FormatException when looking up NPCs." + "Notes": "ID changed in 1.10.1. Crashes with FormatException when looking up NPCs.", + "Compatibility": "AssumeBroken" }, { "Name": "Makeshift Multiplayer", "ID": "StardewValleyMP", "UpperVersion": "0.2.10", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/501", - "Notes": "Uses obsolete GraphicsEvents.OnPreRenderHudEventNoCheck." + "Notes": "Uses obsolete GraphicsEvents.OnPreRenderHudEventNoCheck.", + "Compatibility": "AssumeBroken" }, { "Name": "NoSoilDecay", "ID": "289dee03-5f38-4d8e-8ffc-e440198e8610", "UpperVersion": "0.5", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/237", - "Notes": "Uses Assembly.GetExecutingAssembly().Location." + "Notes": "Uses Assembly.GetExecutingAssembly().Location.", + "Compatibility": "AssumeBroken" }, { "Name": "Point-and-Plant", "ID": "PointAndPlant.dll", "UpperVersion": "1.0.2", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/572", - "Notes": "Uses obsolete StardewModdingAPI.Extensions." + "Notes": "Uses obsolete StardewModdingAPI.Extensions.", + "Compatibility": "AssumeBroken" }, { "Name": "Reusable Wallpapers", "ID": "dae1b553-2e39-43e7-8400-c7c5c836134b", "UpperVersion": "1.5", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/356", - "Notes": "Uses obsolete StardewModdingAPI.Inheritance.ItemStackChange." + "Notes": "Uses obsolete StardewModdingAPI.Inheritance.ItemStackChange.", + "Compatibility": "AssumeBroken" }, { "Name": "Save Anywhere", "ID": "SaveAnywhere", "UpperVersion": "2.0", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/444", - "Notes": "Depends on StarDustCore." + "Notes": "Depends on StarDustCore.", + "Compatibility": "AssumeBroken" }, { "Name": "StackSplitX", "ID": "StackSplitX.dll", "UpperVersion": "1.0", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/798", - "Notes": "Uses SMAPI's internal SGame class." + "Notes": "Uses SMAPI's internal SGame class.", + "Compatibility": "AssumeBroken" }, { "Name": "StarDustCore", "ID": "StarDustCore", "UpperVersion": "1.0", "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/683", - "Notes": "Crashes with 'Method not found: Void StardewModdingAPI.Command.CallCommand(System.String)'." + "Notes": "Crashes with 'Method not found: Void StardewModdingAPI.Command.CallCommand(System.String)'.", + "Compatibility": "AssumeBroken" }, { "Name": "Teleporter", "ID": "Teleporter", "UpperVersion": "1.0.2", "UpdateUrl": "http://community.playstarbound.com/resources/4374", - "Notes": "Crashes with 'InvalidOperationException: The StardewValley.Menus.MapPage object doesn't have a private 'points' instance field'." + "Notes": "Crashes with 'InvalidOperationException: The StardewValley.Menus.MapPage object doesn't have a private 'points' instance field'.", + "Compatibility": "AssumeBroken" }, { "Name": "Zoryn's Better RNG", "ID": "76b6d1e1-f7ba-4d72-8c32-5a1e6d2716f6", "UpperVersion": "1.5", "UpdateUrl": "http://community.playstarbound.com/threads/108756", - "Notes": "Uses SMAPI's internal SGame class." + "Notes": "Uses SMAPI's internal SGame class.", + "Compatibility": "AssumeBroken" }, { "Name": "Zoryn's Calendar Anywhere", "ID": "a41c01cd-0437-43eb-944f-78cb5a53002a", "UpperVersion": "1.5", "UpdateUrl": "http://community.playstarbound.com/threads/108756", - "Notes": "Uses SMAPI's internal SGame class." + "Notes": "Uses SMAPI's internal SGame class.", + "Compatibility": "AssumeBroken" }, { "Name": "Zoryn's Health Bars", "ID": "HealthBars.dll", "UpperVersion": "1.5", "UpdateUrl": "http://community.playstarbound.com/threads/108756", - "Notes": "Uses SMAPI's internal SGame class." + "Notes": "Uses SMAPI's internal SGame class.", + "Compatibility": "AssumeBroken" }, { "Name": "Zoryn's Movement Mod", "ID": "8a632929-8335-484f-87dd-c29d2ba3215d", "UpperVersion": "1.5", "UpdateUrl": "http://community.playstarbound.com/threads/108756", - "Notes": "Uses SMAPI's internal SGame class." + "Notes": "Uses SMAPI's internal SGame class.", + "Compatibility": "AssumeBroken" }, { "Name": "Zoryn's Regen Mod", "ID": "dfac4383-1b6b-4f33-ae4e-37fc23e5252e", "UpperVersion": "1.5", "UpdateUrl": "http://community.playstarbound.com/threads/108756", - "Notes": "Uses SMAPI's internal SGame class." + "Notes": "Uses SMAPI's internal SGame class.", + "Compatibility": "AssumeBroken" } ] } diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 99666f08..091b3d90 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -154,6 +154,7 @@ + @@ -176,7 +177,7 @@ - +