From ae01396d9db77789937ff26cfc1cdf90feea68b2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 13 Jul 2017 17:26:36 -0400 Subject: fix crash in unique-ID check when mod has no manifest (#323) --- src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs index b75453b7..16c397be 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs @@ -161,7 +161,7 @@ namespace StardewModdingAPI.Framework.ModLoading #if !SMAPI_1_x { var duplicatesByID = mods - .GroupBy(mod => mod.Manifest.UniqueID?.Trim(), mod => mod, StringComparer.InvariantCultureIgnoreCase) + .GroupBy(mod => mod.Manifest?.UniqueID?.Trim(), mod => mod, StringComparer.InvariantCultureIgnoreCase) .Where(p => p.Count() > 1); foreach (var group in duplicatesByID) { -- cgit From 48ced0336c5e0f41efd2ecf3b5a03f11596d79d1 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 13 Jul 2017 18:30:46 -0400 Subject: use more readable colours if player has a light-backgrounded terminal (#327) --- release-notes.md | 1 + src/StardewModdingAPI/Framework/Monitor.cs | 61 +++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 9 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index 924ecb90..6e6d87ab 100644 --- a/release-notes.md +++ b/release-notes.md @@ -4,6 +4,7 @@ For players: * The SMAPI console is now much simpler and easier-to-read. +* The SMAPI console now uses more readable colors in terminals with a light background. For mod developers: * Added API to edit XNB images & data loaded by the game (see [API reference](http://stardewvalleywiki.com/Modding:SMAPI_APIs#Content)). diff --git a/src/StardewModdingAPI/Framework/Monitor.cs b/src/StardewModdingAPI/Framework/Monitor.cs index 5ae24a73..c2c3a689 100644 --- a/src/StardewModdingAPI/Framework/Monitor.cs +++ b/src/StardewModdingAPI/Framework/Monitor.cs @@ -25,15 +25,7 @@ namespace StardewModdingAPI.Framework private static readonly int MaxLevelLength = (from level in Enum.GetValues(typeof(LogLevel)).Cast() select level.ToString().Length).Max(); /// The console text color for each log level. - private static readonly Dictionary Colors = new Dictionary - { - [LogLevel.Trace] = ConsoleColor.DarkGray, - [LogLevel.Debug] = ConsoleColor.DarkGray, - [LogLevel.Info] = ConsoleColor.White, - [LogLevel.Warn] = ConsoleColor.Yellow, - [LogLevel.Error] = ConsoleColor.Red, - [LogLevel.Alert] = ConsoleColor.Magenta - }; + private static readonly IDictionary Colors = Monitor.GetConsoleColorScheme(); /// Propagates notification that SMAPI should exit. private readonly CancellationTokenSource ExitTokenSource; @@ -172,5 +164,56 @@ namespace StardewModdingAPI.Framework if (this.WriteToFile) this.LogFile.WriteLine(fullMessage); } + + /// Get the color scheme to use for the current console. + private static IDictionary GetConsoleColorScheme() + { +#if !SMAPI_1_x + // scheme for dark console background + if (Monitor.IsDark(Console.BackgroundColor)) + { +#endif + return new Dictionary + { + [LogLevel.Trace] = ConsoleColor.DarkGray, + [LogLevel.Debug] = ConsoleColor.DarkGray, + [LogLevel.Info] = ConsoleColor.White, + [LogLevel.Warn] = ConsoleColor.Yellow, + [LogLevel.Error] = ConsoleColor.Red, + [LogLevel.Alert] = ConsoleColor.Magenta + }; +#if !SMAPI_1_x + } + + // scheme for light console background + return new Dictionary + { + [LogLevel.Trace] = ConsoleColor.DarkGray, + [LogLevel.Debug] = ConsoleColor.DarkGray, + [LogLevel.Info] = ConsoleColor.Black, + [LogLevel.Warn] = ConsoleColor.DarkYellow, + [LogLevel.Error] = ConsoleColor.Red, + [LogLevel.Alert] = ConsoleColor.DarkMagenta + }; +#endif + } + + /// Get whether a console color should be considered dark, which is subjectively defined as 'white looks better than black on this text'. + /// The color to check. + private static bool IsDark(ConsoleColor color) + { + switch (color) + { + case ConsoleColor.Black: + case ConsoleColor.Blue: + case ConsoleColor.DarkBlue: + case ConsoleColor.DarkRed: + case ConsoleColor.Red: + return true; + + default: + return false; + } + } } } -- cgit From 24b824644d708a514614b4e30b41567ea87902cc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 20 Jul 2017 00:01:40 -0400 Subject: make semantic versions equatable in 2.0 --- release-notes.md | 1 + src/StardewModdingAPI/ISemanticVersion.cs | 3 +++ src/StardewModdingAPI/SemanticVersion.cs | 12 +++++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index 610426d7..9d9131ca 100644 --- a/release-notes.md +++ b/release-notes.md @@ -11,6 +11,7 @@ For mod developers: * Added `InputEvents` which combine keyboard + mouse + controller input for simpler input handling; add metadata like the cursor position and grab tile for simpler click handling; and add an option to suppress input from the game. * Added support for optional dependencies. * Added support for string versions (like `"1.0-alpha"`) in `manifest.json`. +* Added `IEquatable` to `ISemanticVersion`. * Removed all deprecated code. * Removed support for mods with no `Name`, `Version`, or `UniqueID` in their manifest. * Removed support for mods with a non-unique `UniqueID` value in their manifest. diff --git a/src/StardewModdingAPI/ISemanticVersion.cs b/src/StardewModdingAPI/ISemanticVersion.cs index 27a2f67d..c1a4ca3a 100644 --- a/src/StardewModdingAPI/ISemanticVersion.cs +++ b/src/StardewModdingAPI/ISemanticVersion.cs @@ -4,6 +4,9 @@ namespace StardewModdingAPI { /// A semantic version with an optional release tag. public interface ISemanticVersion : IComparable +#if !SMAPI_1_x + , IEquatable +#endif { /********* ** Accessors diff --git a/src/StardewModdingAPI/SemanticVersion.cs b/src/StardewModdingAPI/SemanticVersion.cs index 4b27c819..f30c43cd 100644 --- a/src/StardewModdingAPI/SemanticVersion.cs +++ b/src/StardewModdingAPI/SemanticVersion.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.RegularExpressions; namespace StardewModdingAPI @@ -178,6 +178,16 @@ namespace StardewModdingAPI return this.IsBetween(new SemanticVersion(min), new SemanticVersion(max)); } +#if !SMAPI_1_x + /// Indicates whether the current object is equal to another object of the same type. + /// true if the current object is equal to the parameter; otherwise, false. + /// An object to compare with this object. + public bool Equals(ISemanticVersion other) + { + return other != null && this.CompareTo(other) == 0; + } +#endif + /// Get a string representation of the version. public override string ToString() { -- cgit From d0e0e9427e9d407e1613a1cb9265beed103bd80f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 20 Jul 2017 00:03:41 -0400 Subject: rename ManifestFieldConverter for broader usage --- src/StardewModdingAPI/Framework/Models/Manifest.cs | 6 +- .../Serialisation/ManifestFieldConverter.cs | 99 ---------------------- .../Framework/Serialisation/SFieldConverter.cs | 99 ++++++++++++++++++++++ src/StardewModdingAPI/StardewModdingAPI.csproj | 2 +- 4 files changed, 103 insertions(+), 103 deletions(-) delete mode 100644 src/StardewModdingAPI/Framework/Serialisation/ManifestFieldConverter.cs create mode 100644 src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/Models/Manifest.cs b/src/StardewModdingAPI/Framework/Models/Manifest.cs index 1b5c2646..29c3517e 100644 --- a/src/StardewModdingAPI/Framework/Models/Manifest.cs +++ b/src/StardewModdingAPI/Framework/Models/Manifest.cs @@ -21,18 +21,18 @@ namespace StardewModdingAPI.Framework.Models public string Author { get; set; } /// The mod version. - [JsonConverter(typeof(ManifestFieldConverter))] + [JsonConverter(typeof(SFieldConverter))] public ISemanticVersion Version { get; set; } /// The minimum SMAPI version required by this mod, if any. - [JsonConverter(typeof(ManifestFieldConverter))] + [JsonConverter(typeof(SFieldConverter))] public ISemanticVersion MinimumApiVersion { get; set; } /// The name of the DLL in the directory that has the method. public string EntryDll { get; set; } /// The other mods that must be loaded before this mod. - [JsonConverter(typeof(ManifestFieldConverter))] + [JsonConverter(typeof(SFieldConverter))] public IManifestDependency[] Dependencies { get; set; } /// The unique mod ID. diff --git a/src/StardewModdingAPI/Framework/Serialisation/ManifestFieldConverter.cs b/src/StardewModdingAPI/Framework/Serialisation/ManifestFieldConverter.cs deleted file mode 100644 index 6947311b..00000000 --- a/src/StardewModdingAPI/Framework/Serialisation/ManifestFieldConverter.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using StardewModdingAPI.Framework.Exceptions; -using StardewModdingAPI.Framework.Models; - -namespace StardewModdingAPI.Framework.Serialisation -{ - /// Overrides how SMAPI reads and writes and fields. - internal class ManifestFieldConverter : JsonConverter - { - /********* - ** Accessors - *********/ - /// Whether this converter can write JSON. - public override bool CanWrite => false; - - - /********* - ** Public methods - *********/ - /// Get whether this instance can convert the specified object type. - /// The object type. - public override bool CanConvert(Type objectType) - { - return objectType == typeof(ISemanticVersion) || objectType == typeof(IManifestDependency[]); - } - - /// Reads the JSON representation of the object. - /// The JSON reader. - /// The object type. - /// The object being read. - /// The calling serializer. - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - // semantic version - if (objectType == typeof(ISemanticVersion)) - { - JToken token = JToken.Load(reader); - switch (token.Type) - { - case JTokenType.Object: - { - JObject obj = (JObject)token; - int major = obj.Value(nameof(ISemanticVersion.MajorVersion)); - int minor = obj.Value(nameof(ISemanticVersion.MinorVersion)); - int patch = obj.Value(nameof(ISemanticVersion.PatchVersion)); - string build = obj.Value(nameof(ISemanticVersion.Build)); - return new SemanticVersion(major, minor, patch, build); - } - - case JTokenType.String: - { - string str = token.Value(); - if (string.IsNullOrWhiteSpace(str)) - return null; - if (!SemanticVersion.TryParse(str, out ISemanticVersion version)) - throw new SParseException($"Can't parse semantic version from invalid value '{str}', should be formatted like 1.2, 1.2.30, or 1.2.30-beta."); - return version; - } - - default: - throw new SParseException($"Can't parse semantic version from {token.Type}, must be an object or string."); - } - } - - // manifest dependency - if (objectType == typeof(IManifestDependency[])) - { - List result = new List(); - foreach (JObject obj in JArray.Load(reader).Children()) - { - string uniqueID = obj.Value(nameof(IManifestDependency.UniqueID)); - string minVersion = obj.Value(nameof(IManifestDependency.MinimumVersion)); -#if SMAPI_1_x - result.Add(new ManifestDependency(uniqueID, minVersion)); -#else - bool required = obj.Value(nameof(IManifestDependency.IsRequired)) ?? true; - result.Add(new ManifestDependency(uniqueID, minVersion, required)); -#endif - } - return result.ToArray(); - } - - // unknown - throw new NotSupportedException($"Unknown type '{objectType?.FullName}'."); - } - - /// Writes the JSON representation of the object. - /// The JSON writer. - /// The value. - /// The calling serializer. - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - throw new InvalidOperationException("This converter does not write JSON."); - } - } -} diff --git a/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs b/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs new file mode 100644 index 00000000..9dc62b6a --- /dev/null +++ b/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using StardewModdingAPI.Framework.Exceptions; +using StardewModdingAPI.Framework.Models; + +namespace StardewModdingAPI.Framework.Serialisation +{ + /// Overrides how SMAPI reads and writes and fields. + internal class SFieldConverter : JsonConverter + { + /********* + ** Accessors + *********/ + /// Whether this converter can write JSON. + public override bool CanWrite => false; + + + /********* + ** Public methods + *********/ + /// Get whether this instance can convert the specified object type. + /// The object type. + public override bool CanConvert(Type objectType) + { + return objectType == typeof(ISemanticVersion) || objectType == typeof(IManifestDependency[]); + } + + /// Reads the JSON representation of the object. + /// The JSON reader. + /// The object type. + /// The object being read. + /// The calling serializer. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + // semantic version + if (objectType == typeof(ISemanticVersion)) + { + JToken token = JToken.Load(reader); + switch (token.Type) + { + case JTokenType.Object: + { + JObject obj = (JObject)token; + int major = obj.Value(nameof(ISemanticVersion.MajorVersion)); + int minor = obj.Value(nameof(ISemanticVersion.MinorVersion)); + int patch = obj.Value(nameof(ISemanticVersion.PatchVersion)); + string build = obj.Value(nameof(ISemanticVersion.Build)); + return new SemanticVersion(major, minor, patch, build); + } + + case JTokenType.String: + { + string str = token.Value(); + if (string.IsNullOrWhiteSpace(str)) + return null; + if (!SemanticVersion.TryParse(str, out ISemanticVersion version)) + throw new SParseException($"Can't parse semantic version from invalid value '{str}', should be formatted like 1.2, 1.2.30, or 1.2.30-beta."); + return version; + } + + default: + throw new SParseException($"Can't parse semantic version from {token.Type}, must be an object or string."); + } + } + + // manifest dependency + if (objectType == typeof(IManifestDependency[])) + { + List result = new List(); + foreach (JObject obj in JArray.Load(reader).Children()) + { + string uniqueID = obj.Value(nameof(IManifestDependency.UniqueID)); + string minVersion = obj.Value(nameof(IManifestDependency.MinimumVersion)); +#if SMAPI_1_x + result.Add(new ManifestDependency(uniqueID, minVersion)); +#else + bool required = obj.Value(nameof(IManifestDependency.IsRequired)) ?? true; + result.Add(new ManifestDependency(uniqueID, minVersion, required)); +#endif + } + return result.ToArray(); + } + + // unknown + throw new NotSupportedException($"Unknown type '{objectType?.FullName}'."); + } + + /// Writes the JSON representation of the object. + /// The JSON writer. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new InvalidOperationException("This converter does not write JSON."); + } + } +} diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index f778660d..9047aea4 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -159,7 +159,7 @@ - + -- cgit From 6ddcef61e94a81711ad25a378f0fa03d7799f2dc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 20 Jul 2017 00:05:39 -0400 Subject: simplify mod compatibility model parsing --- .../Framework/ModLoading/ModResolver.cs | 6 ++-- .../Framework/Models/ModCompatibility.cs | 35 ++++------------------ 2 files changed, 9 insertions(+), 32 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs index 16c397be..cf43eb45 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs @@ -72,8 +72,8 @@ namespace StardewModdingAPI.Framework.ModLoading from mod in compatibilityRecords where mod.ID.Contains(key, StringComparer.InvariantCultureIgnoreCase) - && (mod.LowerSemanticVersion == null || !manifest.Version.IsOlderThan(mod.LowerSemanticVersion)) - && !manifest.Version.IsNewerThan(mod.UpperSemanticVersion) + && (mod.LowerVersion == null || !manifest.Version.IsOlderThan(mod.LowerVersion)) + && !manifest.Version.IsNewerThan(mod.UpperVersion) select mod ).FirstOrDefault(); } @@ -113,7 +113,7 @@ namespace StardewModdingAPI.Framework.ModLoading bool hasUnofficialUrl = !string.IsNullOrWhiteSpace(mod.Compatibility.UnofficialUpdateUrl); string reasonPhrase = compatibility.ReasonPhrase ?? "it's not compatible with the latest version of the game or SMAPI"; - string error = $"{reasonPhrase}. Please check for a version newer than {compatibility.UpperVersionLabel ?? compatibility.UpperVersion} here:"; + string error = $"{reasonPhrase}. Please check for a version newer than {compatibility.UpperVersionLabel ?? compatibility.UpperVersion.ToString()} here:"; if (hasOfficialUrl) error += !hasUnofficialUrl ? $" {compatibility.UpdateUrl}" : $"{Environment.NewLine}- official mod: {compatibility.UpdateUrl}"; if (hasUnofficialUrl) diff --git a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs index 90cbd237..eb312eff 100644 --- a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs +++ b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs @@ -1,5 +1,5 @@ -using System.Runtime.Serialization; -using Newtonsoft.Json; +using Newtonsoft.Json; +using StardewModdingAPI.Framework.Serialisation; namespace StardewModdingAPI.Framework.Models { @@ -19,10 +19,12 @@ namespace StardewModdingAPI.Framework.Models public string Name { get; set; } /// The oldest incompatible mod version, or null for all past versions. - public string LowerVersion { get; set; } + [JsonConverter(typeof(SFieldConverter))] + public ISemanticVersion LowerVersion { get; set; } /// The most recent incompatible mod version. - public string UpperVersion { get; set; } + [JsonConverter(typeof(SFieldConverter))] + public ISemanticVersion UpperVersion { get; set; } /// A label to show to the user instead of , when the manifest version differs from the user-facing version. public string UpperVersionLabel { get; set; } @@ -39,30 +41,5 @@ namespace StardewModdingAPI.Framework.Models /// Indicates how SMAPI should consider the mod. public ModCompatibilityType Compatibility { get; set; } - - - /**** - ** Injected - ****/ - /// The semantic version corresponding to . - [JsonIgnore] - public ISemanticVersion LowerSemanticVersion { get; set; } - - /// The semantic version corresponding to . - [JsonIgnore] - public ISemanticVersion UpperSemanticVersion { get; set; } - - - /********* - ** Private methods - *********/ - /// The method called when the model finishes deserialising. - /// The deserialisation context. - [OnDeserialized] - private void OnDeserialized(StreamingContext context) - { - this.LowerSemanticVersion = this.LowerVersion != null ? new SemanticVersion(this.LowerVersion) : null; - this.UpperSemanticVersion = this.UpperVersion != null ? new SemanticVersion(this.UpperVersion) : null; - } } } -- cgit From 7d73b0bf0c545b281d9d84bc73ad40932764b483 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 20 Jul 2017 00:37:58 -0400 Subject: simplify compatibility skip message in 2.0 & combine update URL fields --- .../Framework/ModLoading/ModResolver.cs | 18 +++- .../Framework/Models/ModCompatibility.cs | 7 +- .../StardewModdingAPI.config.json | 104 +++++++++------------ 3 files changed, 62 insertions(+), 67 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs index cf43eb45..fdcbdaa7 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs @@ -109,15 +109,25 @@ namespace StardewModdingAPI.Framework.ModLoading ModCompatibility compatibility = mod.Compatibility; if (compatibility?.Compatibility == ModCompatibilityType.AssumeBroken) { - bool hasOfficialUrl = !string.IsNullOrWhiteSpace(mod.Compatibility.UpdateUrl); - bool hasUnofficialUrl = !string.IsNullOrWhiteSpace(mod.Compatibility.UnofficialUpdateUrl); +#if SMAPI_1_x + bool hasOfficialUrl = mod.Compatibility.UpdateUrls.Length > 0; + bool hasUnofficialUrl = mod.Compatibility.UpdateUrls.Length > 1; string reasonPhrase = compatibility.ReasonPhrase ?? "it's not compatible with the latest version of the game or SMAPI"; string error = $"{reasonPhrase}. Please check for a version newer than {compatibility.UpperVersionLabel ?? compatibility.UpperVersion.ToString()} here:"; if (hasOfficialUrl) - error += !hasUnofficialUrl ? $" {compatibility.UpdateUrl}" : $"{Environment.NewLine}- official mod: {compatibility.UpdateUrl}"; + error += !hasUnofficialUrl ? $" {compatibility.UpdateUrls[0]}" : $"{Environment.NewLine}- official mod: {compatibility.UpdateUrls[0]}"; if (hasUnofficialUrl) - error += $"{Environment.NewLine}- unofficial update: {compatibility.UnofficialUpdateUrl}"; + error += $"{Environment.NewLine}- unofficial update: {compatibility.UpdateUrls[1]}"; +#else + string reasonPhrase = compatibility.ReasonPhrase ?? "it's no longer compatible"; + string error = $"{reasonPhrase}. Please check for a "; + if (mod.Manifest.Version.Equals(compatibility.UpperVersion) && compatibility.UpperVersionLabel == null) + error += "newer version"; + else + error += $"version newer than {compatibility.UpperVersionLabel ?? compatibility.UpperVersion.ToString()}"; + error += " at " + string.Join(" or ", compatibility.UpdateUrls); +#endif mod.SetStatus(ModMetadataStatus.Failed, error); continue; diff --git a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs index eb312eff..72b6f2af 100644 --- a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs +++ b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs @@ -29,11 +29,8 @@ namespace StardewModdingAPI.Framework.Models /// A label to show to the user instead of , when the manifest version differs from the user-facing version. public string UpperVersionLabel { get; set; } - /// The URL the user can check for an official updated version. - public string UpdateUrl { get; set; } - - /// The URL the user can check for an unofficial updated version. - public string UnofficialUpdateUrl { get; set; } + /// The URLs the user can check for a newer version. + public string[] UpdateUrls { 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" diff --git a/src/StardewModdingAPI/StardewModdingAPI.config.json b/src/StardewModdingAPI/StardewModdingAPI.config.json index 4e871636..90bf29c9 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.config.json +++ b/src/StardewModdingAPI/StardewModdingAPI.config.json @@ -54,8 +54,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "AccessChestAnywhere" ], "UpperVersion": "1.1", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/257", - "UnofficialUpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/518", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/257", "http://www.nexusmods.com/stardewvalley/mods/518" ], "Notes": "Needs update for SDV 1.1." }, { @@ -63,7 +62,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "AlmightyTool.dll" ], "UpperVersion": "1.1.1", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/439", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/439" ], "Notes": "Needs update for SDV 1.2." }, { @@ -71,8 +70,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "SPDSprinklersMod", /*since 2.3*/ "Speeder.BetterSprinklers" ], "UpperVersion": "2.3.1-pathoschild-update", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/41", - "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/41", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2 and to migrate broken TimeEvents.AfterDayOfMonthChanged." }, { @@ -80,8 +78,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "005e02dc-d900-425c-9c68-1ff55c5a295d" ], "UpperVersion": "1.2.2", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/276", - "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/276", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, { @@ -89,8 +86,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "SPDChestLabel" ], "UpperVersion": "1.6", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/242", - "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/242", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.1." }, { @@ -98,7 +94,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "ChestPooling.dll" ], "UpperVersion": "1.2", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/threads/111988", + "UpdateUrls": [ "http://community.playstarbound.com/threads/111988" ], "Notes": "Needs update for SDV 1.2." }, { @@ -106,7 +102,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "ChestsAnywhere", /*since 1.9*/ "Pathoschild.ChestsAnywhere" ], "UpperVersion": "1.9-beta", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/518", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/518" ], "Notes": "Needs update for SDV 1.2." }, { @@ -114,22 +110,21 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "CJBAutomation" ], "UpperVersion": "1.4", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/211", - "UnofficialUpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/1063", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/211", "http://www.nexusmods.com/stardewvalley/mods/1063" ], "Notes": "Needs update for SDV 1.2." }, { "Name": "CJB Cheats Menu", "ID": [ "CJBCheatsMenu" ], "UpperVersion": "1.12", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/4", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/4" ], "Notes": "Needs update for SDV 1.1." }, { "Name": "CJB Item Spawner", "ID": [ "CJBItemSpawner" ], "UpperVersion": "1.5", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/93", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/93" ], "Notes": "Needs update for SDV 1.1." }, { @@ -137,7 +132,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "CJBShowItemSellPrice" ], "UpperVersion": "1.6", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/93", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/93" ], "Notes": "Needs update for SDV 1.2." }, { @@ -145,7 +140,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "CookingSkill" ], "UpperVersion": "1.0.3", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/522", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/522" ], "Notes": "Needs update for SDV 1.2." }, { @@ -153,8 +148,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "20d6b8a3-b6e7-460b-a6e4-07c2b0cb6c63" ], "UpperVersion": "1.0.4", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/569", - "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/569", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, { @@ -162,8 +156,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "SPDHealthBar" ], "UpperVersion": "1.7", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/193", - "UnofficialUrl": "http://community.playstarbound.com/threads/132096", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/193", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, { @@ -171,7 +164,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "eacdb74b-4080-4452-b16b-93773cda5cf9", /*since ???*/ "Entoarox.EntoaroxFramework" ], "UpperVersion": "1.7.5", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/resources/4228", + "UpdateUrls": [ "http://community.playstarbound.com/resources/4228" ], "Notes": "Needs update for SDV 1.2." }, { @@ -180,7 +173,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "UpperVersion": "1.0", "UpperVersionLabel": "0.94", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/485", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/485" ], "Notes": "Needs update for SDV 1.2. Actual upper version is 0.94, but mod incorrectly sets it to 1.0 in the manifest." }, { @@ -188,7 +181,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "FarmAutomation.ItemCollector.dll", /*since 0.4*/ "Maddy99.FarmAutomation.ItemCollector" ], "UpperVersion": "0.4", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/threads/125172", + "UpdateUrls": [ "http://community.playstarbound.com/threads/125172" ], "Notes": "Needs update for SDV 1.2." }, { @@ -196,8 +189,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "InstantGeode" ], "UpperVersion": "1.12", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/threads/109038", - "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096", + "UpdateUrls": [ "http://community.playstarbound.com/threads/109038", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, { @@ -205,7 +197,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "GateOpener.dll", /*since 1.1*/ "mralbobo.GateOpener" ], "UpperVersion": "1.0.1", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/threads/111988", + "UpdateUrls": [ "http://community.playstarbound.com/threads/111988" ], "Notes": "Needs update for SDV 1.2." }, { @@ -213,7 +205,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "GetDressed.dll", /*since 3.3*/ "Advize.GetDressed" ], "UpperVersion": "3.3", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/331", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/331" ], "Notes": "Needs update for SDV 1.2." }, { @@ -221,7 +213,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "8008db57-fa67-4730-978e-34b37ef191d6" ], "UpperVersion": "2.3.1", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/229", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/229" ], "Notes": "Needs update for SDV 1.2." }, { @@ -229,7 +221,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "LookupAnything", /*since 1.10.1*/ "Pathoschild.LookupAnything" ], "UpperVersion": "1.10.1", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/541", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/541" ], "Notes": "Needs update for SDV 1.2." }, { @@ -237,7 +229,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "StardewValleyMP", /*since 0.3*/ "spacechase0.StardewValleyMP" ], "Compatibility": "AssumeBroken", "UpperVersion": "0.3.3", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/501", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/501" ], "Notes": "Needs update for SDV 1.2." }, { @@ -245,8 +237,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "289dee03-5f38-4d8e-8ffc-e440198e8610" ], "UpperVersion": "0.5", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/237", - "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/237", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2, and uses Assembly.GetExecutingAssembly().Location." }, { @@ -255,7 +246,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "LowerVersion": "1.42", "UpperVersion": "1.43", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/239", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/239" ], "ReasonPhrase": "These versions have an update check error which crash the game." }, { @@ -263,7 +254,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "SB_PotC" ], "UpperVersion": "1.0.8", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/923", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/923" ], "ReasonPhrase": "Needs update for SDV 1.2." }, { @@ -271,7 +262,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "PointAndPlant.dll" ], "UpperVersion": "1.0.2", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/572", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/572" ], "Notes": "Needs update for SDV 1.2." }, { @@ -279,8 +270,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "PrairieKingMadeEasy.dll" ], "UpperVersion": "1.0.0", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/resources/3594", - "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096", + "UpdateUrls": [ "http://community.playstarbound.com/resources/3594", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, { @@ -288,7 +278,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "RushOrders", /*since 1.1*/ "spacechase0.RushOrders" ], "UpperVersion": "1.1", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/605", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/605" ], "Notes": "Needs update for SDV 1.2." }, { @@ -296,7 +286,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "SaveAnywhere" ], "UpperVersion": "2.3", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/444", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/444" ], "Notes": "Needs update for SDV 1.2." }, { @@ -304,7 +294,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "SimpleSprinkler.dll" ], "UpperVersion": "1.4", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/76", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/76" ], "Notes": "Needs update for SDV 1.2." }, { @@ -312,8 +302,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "SPDSprintAndDash" ], "UpperVersion": "1.0", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/resources/3531", - "UnofficialUpdateUrl": "http://community.playstarbound.com/resources/4201", + "UpdateUrls": [ "http://community.playstarbound.com/resources/3531", "http://community.playstarbound.com/resources/4201" ], "Notes": "Needs update for SDV 1.2." }, { @@ -321,7 +310,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "SPDSprintAndDash" ], "UpperVersion": "1.2", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/resources/4201", + "UpdateUrls": [ "http://community.playstarbound.com/resources/4201" ], "Notes": "Needs update for SDV 1.2." }, { @@ -329,7 +318,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "StackSplitX.dll" ], "UpperVersion": "1.2", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/798", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/798" ], "Notes": "Needs update for SDV 1.2." }, { @@ -337,7 +326,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "Teleporter" ], "UpperVersion": "1.0.2", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/resources/4374", + "UpdateUrls": [ "http://community.playstarbound.com/resources/4374" ], "Notes": "Needs update for SDV 1.2." }, { @@ -345,7 +334,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "TimeSpeed.dll", /* since 2.0.3 */ "4108e859-333c-4fec-a1a7-d2e18c1019fe", /*since 2.1*/ "community.TimeSpeed" ], "UpperVersion": "2.2", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/169", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/169" ], "Notes": "Needs update for SDV 1.2 and to migrate broken TimeEvents.AfterDayOfMonthChanged." }, { @@ -354,7 +343,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "UpperVersion": "0.5", "UpperVersionLabel": "1.0", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/1023", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1023" ], "Notes": "Needs update for SDV 1.2. Actual upper version is 1.0, but mod incorrectly sets it to 0.5 in the manifest." }, { @@ -362,8 +351,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "WeatherController.dll" ], "UpperVersion": "1.0.2", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/threads/111526", - "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096", + "UpdateUrls": [ "http://community.playstarbound.com/threads/111526", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, { @@ -371,7 +359,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "zdailyincrease" ], "UpperVersion": "1.2", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/resources/4247", + "UpdateUrls": [ "http://community.playstarbound.com/resources/4247" ], "Notes": "Needs update for SDV 1.2." }, { @@ -379,7 +367,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "ZoomMod" ], "UpperVersion": "0.1", "Compatibility": "AssumeBroken", - "UpdateUrl": "http://community.playstarbound.com/threads/115028", + "UpdateUrls": [ "http://community.playstarbound.com/threads/115028" ], "Notes": "Needs update for SDV 1.2." }, { @@ -387,7 +375,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "76b6d1e1-f7ba-4d72-8c32-5a1e6d2716f6", /*since 1.6*/ "Zoryn.BetterRNG" ], "UpperVersion": "1.6", "Compatibility": "AssumeBroken", - "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases", + "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." }, { @@ -395,7 +383,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "a41c01cd-0437-43eb-944f-78cb5a53002a", /*since 1.6*/ "Zoryn.CalendarAnywhere" ], "UpperVersion": "1.6", "Compatibility": "AssumeBroken", - "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases", + "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." }, { @@ -403,7 +391,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "HealthBars.dll", /*since 1.6*/ "Zoryn.HealthBars" ], "UpperVersion": "1.6", "Compatibility": "AssumeBroken", - "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases", + "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." }, { @@ -411,7 +399,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "f93a4fe8-cade-4146-9335-b5f82fbbf7bc", /*since 1.6*/ "Zoryn.JunimoDepositAnywhere" ], "UpperVersion": "1.7", "Compatibility": "AssumeBroken", - "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases", + "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." }, { @@ -419,7 +407,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "8a632929-8335-484f-87dd-c29d2ba3215d", /*since 1.6*/ "Zoryn.MovementModifier" ], "UpperVersion": "1.6", "Compatibility": "AssumeBroken", - "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases", + "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." }, { @@ -427,7 +415,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "dfac4383-1b6b-4f33-ae4e-37fc23e5252e", /*since 1.6*/ "Zoryn.RegenMod" ], "UpperVersion": "1.6", "Compatibility": "AssumeBroken", - "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases", + "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." } ] -- cgit From 7c1ac555a401819bf01ebe0e515b02416bc49b15 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 20 Jul 2017 00:47:28 -0400 Subject: simplify compatibility list by defaulting compatibility type, update readme --- README.md | 14 +++++-- .../Framework/Models/ModCompatibility.cs | 2 +- .../StardewModdingAPI.config.json | 47 +--------------------- 3 files changed, 14 insertions(+), 49 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/README.md b/README.md index bc5da52e..332a3754 100644 --- a/README.md +++ b/README.md @@ -142,14 +142,22 @@ on the wiki for the first-time setup. ## Advanced usage ### Configuration file You can customise the SMAPI behaviour by editing the `StardewModdingAPI.config.json` file in your -game folder. It contains these fields: +game folder. + +Basic fields: field | purpose ----- | ------- `DeveloperMode` | Default `false` (except in _SMAPI for developers_ releases). Whether to enable features intended for mod developers (mainly more detailed console logging). `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. -`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. -`VerboseLogging` | Whether SMAPI should log more information about the game context. +`VerboseLogging` | Default `false`. Whether SMAPI should log more information about the game context. + +Advanced fields (changing these isn't recommended and may destabilise your game): + +field | purpose +----- | ------- +`DisabledMods` | A list of mods to consider obsolete and not load. +`ModCompatibility` | A list of mod versions SMAPI should consider compatible or broken regardless of whether it detects incompatible code. This can be used to force SMAPI to load an incompatible mod, though that isn't recommended. ### Command-line arguments SMAPI recognises the following command-line arguments. These are intended for internal use or diff --git a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs index 72b6f2af..91be1d38 100644 --- a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs +++ b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs @@ -37,6 +37,6 @@ namespace StardewModdingAPI.Framework.Models public string ReasonPhrase { get; set; } /// Indicates how SMAPI should consider the mod. - public ModCompatibilityType Compatibility { get; set; } + public ModCompatibilityType Compatibility { get; set; } = ModCompatibilityType.AssumeBroken; } } diff --git a/src/StardewModdingAPI/StardewModdingAPI.config.json b/src/StardewModdingAPI/StardewModdingAPI.config.json index 90bf29c9..ed03d936 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.config.json +++ b/src/StardewModdingAPI/StardewModdingAPI.config.json @@ -45,7 +45,8 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha /** * 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`. + * detects incompatible code. The default for each record is to assume broken; to force SMAPI to + * load a mod regardless of compatibility checks, add a "Compatibility": "AssumeCompatible" field. * Changing this field is not recommended and may destabilise your game. */ "ModCompatibility": [ @@ -53,7 +54,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "AccessChestAnywhere", "ID": [ "AccessChestAnywhere" ], "UpperVersion": "1.1", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/257", "http://www.nexusmods.com/stardewvalley/mods/518" ], "Notes": "Needs update for SDV 1.1." }, @@ -61,7 +61,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Almighty Tool", "ID": [ "AlmightyTool.dll" ], "UpperVersion": "1.1.1", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/439" ], "Notes": "Needs update for SDV 1.2." }, @@ -69,7 +68,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Better Sprinklers", "ID": [ "SPDSprinklersMod", /*since 2.3*/ "Speeder.BetterSprinklers" ], "UpperVersion": "2.3.1-pathoschild-update", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/41", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2 and to migrate broken TimeEvents.AfterDayOfMonthChanged." }, @@ -77,7 +75,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Birthday Mail", "ID": [ "005e02dc-d900-425c-9c68-1ff55c5a295d" ], "UpperVersion": "1.2.2", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/276", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, @@ -85,7 +82,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Chest Label System", "ID": [ "SPDChestLabel" ], "UpperVersion": "1.6", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/242", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.1." }, @@ -93,7 +89,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Chest Pooling", "ID": [ "ChestPooling.dll" ], "UpperVersion": "1.2", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/threads/111988" ], "Notes": "Needs update for SDV 1.2." }, @@ -101,7 +96,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Chests Anywhere", "ID": [ "ChestsAnywhere", /*since 1.9*/ "Pathoschild.ChestsAnywhere" ], "UpperVersion": "1.9-beta", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/518" ], "Notes": "Needs update for SDV 1.2." }, @@ -109,7 +103,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "CJB Automation", "ID": [ "CJBAutomation" ], "UpperVersion": "1.4", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/211", "http://www.nexusmods.com/stardewvalley/mods/1063" ], "Notes": "Needs update for SDV 1.2." }, @@ -131,7 +124,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "CJB Show Item Sell Price", "ID": [ "CJBShowItemSellPrice" ], "UpperVersion": "1.6", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/93" ], "Notes": "Needs update for SDV 1.2." }, @@ -139,7 +131,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Cooking Skill", "ID": [ "CookingSkill" ], "UpperVersion": "1.0.3", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/522" ], "Notes": "Needs update for SDV 1.2." }, @@ -147,7 +138,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Cooking Skill Prestige Adapter", "ID": [ "20d6b8a3-b6e7-460b-a6e4-07c2b0cb6c63" ], "UpperVersion": "1.0.4", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/569", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, @@ -155,7 +145,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Enemy Health Bars", "ID": [ "SPDHealthBar" ], "UpperVersion": "1.7", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/193", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, @@ -163,7 +152,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Entoarox Framework", "ID": [ "eacdb74b-4080-4452-b16b-93773cda5cf9", /*since ???*/ "Entoarox.EntoaroxFramework" ], "UpperVersion": "1.7.5", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/resources/4228" ], "Notes": "Needs update for SDV 1.2." }, @@ -172,7 +160,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "Mystra007ExtendedFridge" ], "UpperVersion": "1.0", "UpperVersionLabel": "0.94", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/485" ], "Notes": "Needs update for SDV 1.2. Actual upper version is 0.94, but mod incorrectly sets it to 1.0 in the manifest." }, @@ -180,7 +167,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "FarmAutomation.ItemCollector", "ID": [ "FarmAutomation.ItemCollector.dll", /*since 0.4*/ "Maddy99.FarmAutomation.ItemCollector" ], "UpperVersion": "0.4", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/threads/125172" ], "Notes": "Needs update for SDV 1.2." }, @@ -188,7 +174,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Instant Geode", "ID": [ "InstantGeode" ], "UpperVersion": "1.12", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/threads/109038", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, @@ -196,7 +181,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Gate Opener", "ID": [ "GateOpener.dll", /*since 1.1*/ "mralbobo.GateOpener" ], "UpperVersion": "1.0.1", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/threads/111988" ], "Notes": "Needs update for SDV 1.2." }, @@ -204,7 +188,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Get Dressed", "ID": [ "GetDressed.dll", /*since 3.3*/ "Advize.GetDressed" ], "UpperVersion": "3.3", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/331" ], "Notes": "Needs update for SDV 1.2." }, @@ -212,7 +195,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Gift Taste Helper", "ID": [ "8008db57-fa67-4730-978e-34b37ef191d6" ], "UpperVersion": "2.3.1", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/229" ], "Notes": "Needs update for SDV 1.2." }, @@ -220,14 +202,12 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Lookup Anything", "ID": [ "LookupAnything", /*since 1.10.1*/ "Pathoschild.LookupAnything" ], "UpperVersion": "1.10.1", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/541" ], "Notes": "Needs update for SDV 1.2." }, { "Name": "Makeshift Multiplayer", "ID": [ "StardewValleyMP", /*since 0.3*/ "spacechase0.StardewValleyMP" ], - "Compatibility": "AssumeBroken", "UpperVersion": "0.3.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/501" ], "Notes": "Needs update for SDV 1.2." @@ -236,7 +216,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "NoSoilDecay", "ID": [ "289dee03-5f38-4d8e-8ffc-e440198e8610" ], "UpperVersion": "0.5", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/237", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2, and uses Assembly.GetExecutingAssembly().Location." }, @@ -245,7 +224,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "NPCMapLocationsMod" ], "LowerVersion": "1.42", "UpperVersion": "1.43", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/239" ], "ReasonPhrase": "These versions have an update check error which crash the game." }, @@ -253,7 +231,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Part of the Community", "ID": [ "SB_PotC" ], "UpperVersion": "1.0.8", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/923" ], "ReasonPhrase": "Needs update for SDV 1.2." }, @@ -261,7 +238,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Point-and-Plant", "ID": [ "PointAndPlant.dll" ], "UpperVersion": "1.0.2", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/572" ], "Notes": "Needs update for SDV 1.2." }, @@ -269,7 +245,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "PrairieKingMadeEasy", "ID": [ "PrairieKingMadeEasy.dll" ], "UpperVersion": "1.0.0", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/resources/3594", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, @@ -277,7 +252,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Rush Orders", "ID": [ "RushOrders", /*since 1.1*/ "spacechase0.RushOrders" ], "UpperVersion": "1.1", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/605" ], "Notes": "Needs update for SDV 1.2." }, @@ -285,7 +259,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Save Anywhere", "ID": [ "SaveAnywhere" ], "UpperVersion": "2.3", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/444" ], "Notes": "Needs update for SDV 1.2." }, @@ -293,7 +266,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Simple Sprinklers", "ID": [ "SimpleSprinkler.dll" ], "UpperVersion": "1.4", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/76" ], "Notes": "Needs update for SDV 1.2." }, @@ -301,7 +273,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Sprint and Dash", "ID": [ "SPDSprintAndDash" ], "UpperVersion": "1.0", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/resources/3531", "http://community.playstarbound.com/resources/4201" ], "Notes": "Needs update for SDV 1.2." }, @@ -309,7 +280,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Sprint and Dash Redux", "ID": [ "SPDSprintAndDash" ], "UpperVersion": "1.2", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/resources/4201" ], "Notes": "Needs update for SDV 1.2." }, @@ -317,7 +287,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "StackSplitX", "ID": [ "StackSplitX.dll" ], "UpperVersion": "1.2", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/798" ], "Notes": "Needs update for SDV 1.2." }, @@ -325,7 +294,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Teleporter", "ID": [ "Teleporter" ], "UpperVersion": "1.0.2", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/resources/4374" ], "Notes": "Needs update for SDV 1.2." }, @@ -333,7 +301,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "TimeSpeed", "ID": [ "TimeSpeed.dll", /* since 2.0.3 */ "4108e859-333c-4fec-a1a7-d2e18c1019fe", /*since 2.1*/ "community.TimeSpeed" ], "UpperVersion": "2.2", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/169" ], "Notes": "Needs update for SDV 1.2 and to migrate broken TimeEvents.AfterDayOfMonthChanged." }, @@ -342,7 +309,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "Demiacle.UiModSuite" ], "UpperVersion": "0.5", "UpperVersionLabel": "1.0", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1023" ], "Notes": "Needs update for SDV 1.2. Actual upper version is 1.0, but mod incorrectly sets it to 0.5 in the manifest." }, @@ -350,7 +316,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Weather Controller", "ID": [ "WeatherController.dll" ], "UpperVersion": "1.0.2", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/threads/111526", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, @@ -358,7 +323,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "zDailyIncrease", "ID": [ "zdailyincrease" ], "UpperVersion": "1.2", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/resources/4247" ], "Notes": "Needs update for SDV 1.2." }, @@ -366,7 +330,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Zoom Out Extreme", "ID": [ "ZoomMod" ], "UpperVersion": "0.1", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "http://community.playstarbound.com/threads/115028" ], "Notes": "Needs update for SDV 1.2." }, @@ -374,7 +337,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Zoryn's Better RNG", "ID": [ "76b6d1e1-f7ba-4d72-8c32-5a1e6d2716f6", /*since 1.6*/ "Zoryn.BetterRNG" ], "UpperVersion": "1.6", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." }, @@ -382,7 +344,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Zoryn's Calendar Anywhere", "ID": [ "a41c01cd-0437-43eb-944f-78cb5a53002a", /*since 1.6*/ "Zoryn.CalendarAnywhere" ], "UpperVersion": "1.6", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." }, @@ -390,7 +351,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Zoryn's Health Bars", "ID": [ "HealthBars.dll", /*since 1.6*/ "Zoryn.HealthBars" ], "UpperVersion": "1.6", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." }, @@ -398,7 +358,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Zoryn's Junimo Deposit Anywhere", "ID": [ "f93a4fe8-cade-4146-9335-b5f82fbbf7bc", /*since 1.6*/ "Zoryn.JunimoDepositAnywhere" ], "UpperVersion": "1.7", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." }, @@ -406,7 +365,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Zoryn's Movement Mod", "ID": [ "8a632929-8335-484f-87dd-c29d2ba3215d", /*since 1.6*/ "Zoryn.MovementModifier" ], "UpperVersion": "1.6", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." }, @@ -414,7 +372,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "Name": "Zoryn's Regen Mod", "ID": [ "dfac4383-1b6b-4f33-ae4e-37fc23e5252e", /*since 1.6*/ "Zoryn.RegenMod" ], "UpperVersion": "1.6", - "Compatibility": "AssumeBroken", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], "Notes": "Needs update for SDV 1.2." } -- cgit From 17fec9034cb2715fb3ddc38eecf7c9681048f32c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 20 Jul 2017 01:23:23 -0400 Subject: tweak heuristic skip text, add error if mod doesn't implement Entry --- src/StardewModdingAPI/Program.cs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 08bd2b84..75bb991d 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -649,7 +649,11 @@ namespace StardewModdingAPI } catch (IncompatibleInstructionException ex) { +#if SMAPI_1_x TrackSkip(metadata, $"it's not compatible with the latest version of the game or SMAPI (detected {ex.NounPhrase}). Please check for a newer version of the mod."); +#else + TrackSkip(metadata, $"it's no longer compatible (detected {ex.NounPhrase}). Please check for a newer version of the mod."); +#endif continue; } catch (Exception ex) @@ -791,6 +795,9 @@ namespace StardewModdingAPI // raise deprecation warning for old Entry() methods if (this.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(object[]) })) deprecationWarnings.Add(() => this.DeprecationManager.Warn(metadata.DisplayName, $"{nameof(Mod)}.{nameof(Mod.Entry)}(object[]) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.0", DeprecationLevel.PendingRemoval)); +#else + if (!this.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] {typeof(IModHelper)})) + this.Monitor.Log($"{metadata.DisplayName} doesn't implement Entry() and may not work correctly.", LogLevel.Error); #endif } catch (Exception ex) -- cgit From 1bf8e2b95ccb614580da08cded655ffc67b84651 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 21 Jul 2017 00:28:52 -0400 Subject: change order of broken-in-2.0 checks to show more meaningful matches first --- src/StardewModdingAPI/Constants.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 3bd31c2d..8217a6a5 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -179,15 +179,6 @@ namespace StardewModdingAPI new TypeFinder("StardewModdingAPI.Command"), new TypeFinder("StardewModdingAPI.Config"), new TypeFinder("StardewModdingAPI.Log"), - new TypeFinder("StardewModdingAPI.Events.EventArgsCommand"), - new TypeFinder("StardewModdingAPI.Events.EventArgsFarmerChanged"), - new TypeFinder("StardewModdingAPI.Events.EventArgsLoadedGameChanged"), - new TypeFinder("StardewModdingAPI.Events.EventArgsNewDay"), - new TypeFinder("StardewModdingAPI.Events.EventArgsStringChanged"), - new PropertyFinder("StardewModdingAPI.Mod", "PathOnDisk"), - new PropertyFinder("StardewModdingAPI.Mod", "BaseConfigPath"), - new PropertyFinder("StardewModdingAPI.Mod", "PerSaveConfigFolder"), - new PropertyFinder("StardewModdingAPI.Mod", "PerSaveConfigPath"), new EventFinder("StardewModdingAPI.Events.GameEvents", "Initialize"), new EventFinder("StardewModdingAPI.Events.GameEvents", "LoadContent"), new EventFinder("StardewModdingAPI.Events.GameEvents", "GameLoaded"), @@ -198,6 +189,15 @@ namespace StardewModdingAPI new EventFinder("StardewModdingAPI.Events.TimeEvents", "YearOfGameChanged"), new EventFinder("StardewModdingAPI.Events.TimeEvents", "SeasonOfYearChanged"), new EventFinder("StardewModdingAPI.Events.TimeEvents", "OnNewDay"), + new TypeFinder("StardewModdingAPI.Events.EventArgsCommand"), + new TypeFinder("StardewModdingAPI.Events.EventArgsFarmerChanged"), + new TypeFinder("StardewModdingAPI.Events.EventArgsLoadedGameChanged"), + new TypeFinder("StardewModdingAPI.Events.EventArgsNewDay"), + new TypeFinder("StardewModdingAPI.Events.EventArgsStringChanged"), + new PropertyFinder("StardewModdingAPI.Mod", "PathOnDisk"), + new PropertyFinder("StardewModdingAPI.Mod", "BaseConfigPath"), + new PropertyFinder("StardewModdingAPI.Mod", "PerSaveConfigFolder"), + new PropertyFinder("StardewModdingAPI.Mod", "PerSaveConfigPath"), #endif /**** -- cgit From c20b21bcaa799a9961799316b7f4b7d8f929ca3f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 21 Jul 2017 01:51:05 -0400 Subject: add support for disambiguating IDs in mod compatibility list --- .../Framework/ModLoading/ModResolver.cs | 6 +-- .../Framework/Models/ModCompatibility.cs | 6 +-- .../Framework/Models/ModCompatibilityID.cs | 57 ++++++++++++++++++++++ .../Framework/Serialisation/SFieldConverter.cs | 19 +++++++- src/StardewModdingAPI/StardewModdingAPI.csproj | 1 + 5 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 src/StardewModdingAPI/Framework/Models/ModCompatibilityID.cs (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs index fdcbdaa7..6b19db5c 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs @@ -71,9 +71,9 @@ namespace StardewModdingAPI.Framework.ModLoading compatibility = ( from mod in compatibilityRecords where - mod.ID.Contains(key, StringComparer.InvariantCultureIgnoreCase) - && (mod.LowerVersion == null || !manifest.Version.IsOlderThan(mod.LowerVersion)) - && !manifest.Version.IsNewerThan(mod.UpperVersion) + mod.ID.Any(p => p.Matches(key, manifest)) + && (mod.LowerVersion == null || !manifest.Version.IsOlderThan(mod.LowerVersion)) + && !manifest.Version.IsNewerThan(mod.UpperVersion) select mod ).FirstOrDefault(); } diff --git a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs index 91be1d38..d3a9c533 100644 --- a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs +++ b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs @@ -9,11 +9,9 @@ namespace StardewModdingAPI.Framework.Models /********* ** Accessors *********/ - /**** - ** From config - ****/ /// The unique mod IDs. - public string[] ID { get; set; } + [JsonConverter(typeof(SFieldConverter))] + public ModCompatibilityID[] ID { get; set; } /// The mod name. public string Name { get; set; } diff --git a/src/StardewModdingAPI/Framework/Models/ModCompatibilityID.cs b/src/StardewModdingAPI/Framework/Models/ModCompatibilityID.cs new file mode 100644 index 00000000..98e70116 --- /dev/null +++ b/src/StardewModdingAPI/Framework/Models/ModCompatibilityID.cs @@ -0,0 +1,57 @@ +using System; +using Newtonsoft.Json; + +namespace StardewModdingAPI.Framework.Models +{ + /// Uniquely identifies a mod for compatibility checks. + internal class ModCompatibilityID + { + /********* + ** Accessors + *********/ + /// The unique mod ID. + public string ID { get; set; } + + /// The mod name to disambiguate non-unique IDs, or null to ignore the mod name. + public string Name { get; set; } + + /// The author name to disambiguate non-unique IDs, or null to ignore the author. + public string Author { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + public ModCompatibilityID() { } + + /// Construct an instance. + /// The mod ID or a JSON string matching the fields. + public ModCompatibilityID(string data) + { + // JSON can be stuffed into the ID string as a convenience hack to keep JSON mod lists + // formatted readably. The tradeoff is that the format is a bit more magical, but that's + // probably acceptable since players aren't meant to edit it. It's also fairly clear what + // the JSON strings do, if not necessarily how. + if (data.StartsWith("{")) + JsonConvert.PopulateObject(data, this); + else + this.ID = data; + } + + /// Get whether this ID matches a given mod manifest. + /// The mod's unique ID, or a substitute ID if it isn't set in the manifest. + /// The manifest to check. + public bool Matches(string id, IManifest manifest) + { + return + this.ID.Equals(id, StringComparison.InvariantCultureIgnoreCase) + && ( + this.Author == null + || this.Author.Equals(manifest.Author, StringComparison.InvariantCultureIgnoreCase) + || (manifest.ExtraFields.ContainsKey("Authour") && this.Author.Equals(manifest.ExtraFields["Authour"].ToString(), StringComparison.InvariantCultureIgnoreCase)) + ) + && (this.Name == null || this.Name.Equals(manifest.Name, StringComparison.InvariantCultureIgnoreCase)); + } + } +} diff --git a/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs b/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs index 9dc62b6a..11ffdccb 100644 --- a/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs +++ b/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs @@ -24,7 +24,10 @@ namespace StardewModdingAPI.Framework.Serialisation /// The object type. public override bool CanConvert(Type objectType) { - return objectType == typeof(ISemanticVersion) || objectType == typeof(IManifestDependency[]); + return + objectType == typeof(ISemanticVersion) + || objectType == typeof(IManifestDependency[]) + || objectType == typeof(ModCompatibilityID[]); } /// Reads the JSON representation of the object. @@ -83,6 +86,20 @@ namespace StardewModdingAPI.Framework.Serialisation return result.ToArray(); } + // mod compatibility ID + if (objectType == typeof(ModCompatibilityID[])) + { + List result = new List(); + foreach (JToken child in JArray.Load(reader).Children()) + { + result.Add(child is JValue value + ? new ModCompatibilityID(value.Value()) + : child.ToObject() + ); + } + return result.ToArray(); + } + // unknown throw new NotSupportedException($"Unknown type '{objectType?.FullName}'."); } diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 9047aea4..f7992122 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -128,6 +128,7 @@ + -- cgit From e04fc841909642f2f26f8219e7d46ca7961f9f96 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 13 Jul 2017 20:38:35 -0400 Subject: update compatibility list --- release-notes.md | 5 +++++ src/StardewModdingAPI/StardewModdingAPI.config.json | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index 9d9131ca..ea8b8755 100644 --- a/release-notes.md +++ b/release-notes.md @@ -5,6 +5,7 @@ For players: * The SMAPI console is now much simpler and easier-to-read. * The SMAPI console now uses more readable colors in terminals with a light background. +* Updated compatibility list. For mod developers: * Added APIs to edit or inject XNBs loaded by the game at runtime, which let mods do anything previously only possible with XNB mods. @@ -17,6 +18,10 @@ For mod developers: * Removed support for mods with a non-unique `UniqueID` value in their manifest. * Removed the TrainerMod `save` and `load` commands. +## 1.15.2 +For players: +* Updated compatibility list. + ## 1.15.1 For players: * Fixed controller mod input broken in 1.15. diff --git a/src/StardewModdingAPI/StardewModdingAPI.config.json b/src/StardewModdingAPI/StardewModdingAPI.config.json index ed03d936..72163ec1 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.config.json +++ b/src/StardewModdingAPI/StardewModdingAPI.config.json @@ -31,15 +31,30 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha * recommended and may destabilise your game. */ "DisabledMods": [ + { + "Name": "Animal Mood Fix", + "ID": [ "GPeters-AnimalMoodFix" ], + "ReasonPhrase": "the animal mood bugs were fixed in Stardew Valley 1.2." + }, { "Name": "Modder Serialization Utility", "ID": [ "SerializerUtils-0-1" ], - "ReasonPhrase": "it's no longer used by any mods, and is no longer maintained." + "ReasonPhrase": "it's no longer maintained or used." + }, + { + "Name": "No Debug Mode", + "ID": [ "NoDebugMode" ], + "ReasonPhrase": "debug mode was removed in SMAPI 1.0." }, { "Name": "StarDustCore", "ID": [ "StarDustCore" ], "ReasonPhrase": "it was only used by earlier versions of Save Anywhere (which no longer uses it), and is no longer maintained." + }, + { + "Name": "XmlSerializerRetool", + "ID": [ "XmlSerializerRetool.dll" ], + "ReasonPhrase": "it's no longer maintained or used." } ], -- cgit From 74be6f13114e8e4cb8421a684009d160c4e861f1 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Jul 2017 13:15:28 -0400 Subject: improve handling of legacy non-semantic game versions (#333) --- .../Utilities/SemanticVersionTests.cs | 30 ++++++++++ src/StardewModdingAPI/Constants.cs | 28 ++------- src/StardewModdingAPI/Framework/GameVersion.cs | 68 ++++++++++++++++++++++ src/StardewModdingAPI/Program.cs | 16 ++--- src/StardewModdingAPI/SemanticVersion.cs | 3 +- src/StardewModdingAPI/StardewModdingAPI.csproj | 1 + 6 files changed, 113 insertions(+), 33 deletions(-) create mode 100644 src/StardewModdingAPI/Framework/GameVersion.cs (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI.Tests/Utilities/SemanticVersionTests.cs b/src/StardewModdingAPI.Tests/Utilities/SemanticVersionTests.cs index 95d0d74f..db46aee4 100644 --- a/src/StardewModdingAPI.Tests/Utilities/SemanticVersionTests.cs +++ b/src/StardewModdingAPI.Tests/Utilities/SemanticVersionTests.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using NUnit.Framework; +using StardewModdingAPI.Framework; namespace StardewModdingAPI.Tests.Utilities { @@ -206,6 +207,35 @@ namespace StardewModdingAPI.Tests.Utilities return version.IsBetween(lower, upper); } + /**** + ** GameVersion + ****/ + [Test(Description = "Assert that the GameVersion subclass correctly parses legacy game versions.")] + [TestCase("1.0")] + [TestCase("1.01")] + [TestCase("1.02")] + [TestCase("1.03")] + [TestCase("1.04")] + [TestCase("1.05")] + [TestCase("1.051")] + [TestCase("1.051b")] + [TestCase("1.06")] + [TestCase("1.07")] + [TestCase("1.07a")] + [TestCase("1.1")] + [TestCase("1.11")] + [TestCase("1.2")] + [TestCase("1.2.15")] + public void GameVersion(string versionStr) + { + // act + GameVersion version = new GameVersion(versionStr); + + // assert + Assert.AreEqual(versionStr, version.ToString(), "The game version did not round-trip to the same value."); + Assert.IsTrue(version.IsOlderThan(new SemanticVersion("1.2.30")), "The game version should be considered older than the later semantic versions."); + } + /********* ** Private methods diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 8217a6a5..ee57be0f 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -9,6 +9,7 @@ using StardewModdingAPI.AssemblyRewriters.Finders; using StardewModdingAPI.AssemblyRewriters.Rewriters; using StardewModdingAPI.AssemblyRewriters.Rewriters.Wrappers; using StardewModdingAPI.Events; +using StardewModdingAPI.Framework; using StardewValley; namespace StardewModdingAPI @@ -86,7 +87,7 @@ namespace StardewModdingAPI internal static string ModPath { get; } = Path.Combine(Constants.ExecutionPath, "Mods"); /// The game's current semantic version. - internal static ISemanticVersion GameVersion { get; } = Constants.GetGameVersion(); + internal static ISemanticVersion GameVersion { get; } = new GameVersion(Constants.GetGameVersion()); /// The target game platform. internal static Platform TargetPlatform { get; } = @@ -219,19 +220,6 @@ namespace StardewModdingAPI }; } - /// Get game current version as it should be displayed to players. - /// The semantic game version. - internal static ISemanticVersion GetGameDisplayVersion(ISemanticVersion version) - { - switch (version.ToString()) - { - case "1.1.1": - return new SemanticVersion(1, 11, 0); // The 1.1 patch was released as 1.11 - default: - return version; - } - } - /// Get the name of a save directory for the current player. private static string GetSaveFolderName() { @@ -239,20 +227,14 @@ namespace StardewModdingAPI return $"{prefix}_{Game1.uniqueIDForThisGame}"; } - /// Get the game's current semantic version. - private static ISemanticVersion GetGameVersion() + /// Get the game's current version string. + private static string GetGameVersion() { - // get raw version // we need reflection because it's a constant, so SMAPI's references to it are inlined at compile-time FieldInfo field = typeof(Game1).GetField(nameof(Game1.version), BindingFlags.Public | BindingFlags.Static); if (field == null) throw new InvalidOperationException($"The {nameof(Game1)}.{nameof(Game1.version)} field could not be found."); - string version = (string)field.GetValue(null); - - // get semantic version - if (version == "1.11") - version = "1.1.1"; // The 1.1 patch was released as 1.11, which means it's out of order for semantic version checks - return new SemanticVersion(version); + return (string)field.GetValue(null); } } } diff --git a/src/StardewModdingAPI/Framework/GameVersion.cs b/src/StardewModdingAPI/Framework/GameVersion.cs new file mode 100644 index 00000000..48159f61 --- /dev/null +++ b/src/StardewModdingAPI/Framework/GameVersion.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; + +namespace StardewModdingAPI.Framework +{ + /// An implementation of that correctly handles the non-semantic versions used by older Stardew Valley releases. + internal class GameVersion : SemanticVersion + { + /********* + ** Private methods + *********/ + /// A mapping of game to semantic versions. + private static readonly IDictionary VersionMap = new Dictionary(StringComparer.InvariantCultureIgnoreCase) + { + ["1.01"] = "1.0.1", + ["1.02"] = "1.0.2", + ["1.03"] = "1.0.3", + ["1.04"] = "1.0.4", + ["1.05"] = "1.0.5", + ["1.051"] = "1.0.6-prerelease1", // not a very good mapping, but good enough for SMAPI's purposes. + ["1.051b"] = "1.0.6-prelease2", + ["1.06"] = "1.0.6", + ["1.07"] = "1.0.7", + ["1.07a"] = "1.0.8-prerelease1", + ["1.11"] = "1.1.1" + }; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The game version string. + public GameVersion(string version) + : base(GameVersion.GetSemanticVersionString(version)) { } + + /// Get a string representation of the version. + public override string ToString() + { + return GameVersion.GetGameVersionString(base.ToString()); + } + + + /********* + ** Private methods + *********/ + /// Convert a game version string to a semantic version string. + /// The game version string. + private static string GetSemanticVersionString(string gameVersion) + { + return GameVersion.VersionMap.TryGetValue(gameVersion, out string semanticVersion) + ? semanticVersion + : gameVersion; + } + + /// Convert a game version string to a semantic version string. + /// The game version string. + private static string GetGameVersionString(string gameVersion) + { + foreach (var mapping in GameVersion.VersionMap) + { + if (mapping.Value.Equals(gameVersion, StringComparison.InvariantCultureIgnoreCase)) + return mapping.Key; + } + return gameVersion; + } + } +} diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 75bb991d..ff73962e 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -125,7 +125,7 @@ namespace StardewModdingAPI try { // init logging - this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)} on {this.GetFriendlyPlatformName()}", LogLevel.Info); + this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} on {this.GetFriendlyPlatformName()}", LogLevel.Info); this.Monitor.Log($"Mods go here: {Constants.ModPath}"); #if SMAPI_1_x this.Monitor.Log("Preparing SMAPI..."); @@ -138,13 +138,13 @@ namespace StardewModdingAPI // validate game version if (Constants.GameVersion.IsOlderThan(Constants.MinimumGameVersion)) { - this.Monitor.Log($"Oops! You're running Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)}, but the oldest supported version is {Constants.GetGameDisplayVersion(Constants.MinimumGameVersion)}. Please update your game before using SMAPI. If you have the beta version on Steam, you may need to opt out to get the latest non-beta updates.", LogLevel.Error); + this.Monitor.Log($"Oops! You're running Stardew Valley {Constants.GameVersion}, but the oldest supported version is {Constants.MinimumGameVersion}. Please update your game before using SMAPI. If you have the beta version on Steam, you may need to opt out to get the latest non-beta updates.", LogLevel.Error); this.PressAnyKeyToExit(); return; } if (Constants.MaximumGameVersion != null && Constants.GameVersion.IsNewerThan(Constants.MaximumGameVersion)) { - this.Monitor.Log($"Oops! You're running Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)}, but this version of SMAPI is only compatible up to Stardew Valley {Constants.GetGameDisplayVersion(Constants.MaximumGameVersion)}. Please check for a newer version of SMAPI.", LogLevel.Error); + this.Monitor.Log($"Oops! You're running Stardew Valley {Constants.GameVersion}, but this version of SMAPI is only compatible up to Stardew Valley {Constants.MaximumGameVersion}. Please check for a newer version of SMAPI.", LogLevel.Error); this.PressAnyKeyToExit(); return; } @@ -191,8 +191,8 @@ namespace StardewModdingAPI ContentEvents.AfterLocaleChanged += (sender, e) => this.OnLocaleChanged(); // set window titles - this.GameInstance.Window.Title = $"Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)} - running SMAPI {Constants.ApiVersion}"; - Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)}"; + this.GameInstance.Window.Title = $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion}"; + Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion}"; } catch (Exception ex) { @@ -315,7 +315,7 @@ namespace StardewModdingAPI if (Type.GetType($"StardewValley.LocalizedContentManager+LanguageCode, {gameAssemblyName}", throwOnError: false) == null) { PrintErrorAndExit(Constants.GameVersion.IsOlderThan(Constants.MinimumGameVersion) - ? $"Oops! You're running Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)}, but the oldest supported version is {Constants.GetGameDisplayVersion(Constants.MinimumGameVersion)}. Please update your game before using SMAPI." + ? $"Oops! You're running Stardew Valley {Constants.GameVersion}, but the oldest supported version is {Constants.MinimumGameVersion}. Please update your game before using SMAPI." : "Oops! SMAPI doesn't seem to be compatible with your game. Make sure you're running the latest version of Stardew Valley and SMAPI." ); } @@ -445,8 +445,8 @@ namespace StardewModdingAPI // update window titles int modsLoaded = this.ModRegistry.GetMods().Count(); - this.GameInstance.Window.Title = $"Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)} - running SMAPI {Constants.ApiVersion} with {modsLoaded} mods"; - Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)} with {modsLoaded} mods"; + this.GameInstance.Window.Title = $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion} with {modsLoaded} mods"; + Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion} with {modsLoaded} mods"; // start SMAPI console new Thread(this.RunConsoleLoop).Start(); diff --git a/src/StardewModdingAPI/SemanticVersion.cs b/src/StardewModdingAPI/SemanticVersion.cs index f30c43cd..782a962b 100644 --- a/src/StardewModdingAPI/SemanticVersion.cs +++ b/src/StardewModdingAPI/SemanticVersion.cs @@ -117,8 +117,7 @@ namespace StardewModdingAPI { // compare numerically if possible { - int curNum, otherNum; - if (int.TryParse(curParts[i], out curNum) && int.TryParse(otherParts[i], out otherNum)) + if (int.TryParse(curParts[i], out int curNum) && int.TryParse(otherParts[i], out int otherNum)) return curNum.CompareTo(otherNum); } diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index f7992122..4cef91d9 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -126,6 +126,7 @@ + -- cgit From f7e22b551a353dbc1896add569d8666e4447aace Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Jul 2017 13:16:01 -0400 Subject: remove outdated message about beta release branch (#333) --- src/StardewModdingAPI/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index ff73962e..50ab4e25 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -138,7 +138,7 @@ namespace StardewModdingAPI // validate game version if (Constants.GameVersion.IsOlderThan(Constants.MinimumGameVersion)) { - this.Monitor.Log($"Oops! You're running Stardew Valley {Constants.GameVersion}, but the oldest supported version is {Constants.MinimumGameVersion}. Please update your game before using SMAPI. If you have the beta version on Steam, you may need to opt out to get the latest non-beta updates.", LogLevel.Error); + this.Monitor.Log($"Oops! You're running Stardew Valley {Constants.GameVersion}, but the oldest supported version is {Constants.MinimumGameVersion}. Please update your game before using SMAPI.", LogLevel.Error); this.PressAnyKeyToExit(); return; } -- cgit From 4ea6a4102bb69b72391334c4825bd393eff6ac97 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Jul 2017 15:08:14 -0400 Subject: add support for partial cache invalidation (#335) --- src/StardewModdingAPI/Constants.cs | 68 +++++++- src/StardewModdingAPI/Framework/SContentManager.cs | 173 +++++++++++++++------ src/StardewModdingAPI/Program.cs | 16 +- 3 files changed, 202 insertions(+), 55 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index ee57be0f..e85d7e2b 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -11,6 +11,9 @@ using StardewModdingAPI.AssemblyRewriters.Rewriters.Wrappers; using StardewModdingAPI.Events; using StardewModdingAPI.Framework; using StardewValley; +using StardewValley.BellsAndWhistles; +using StardewValley.Objects; +using StardewValley.Projectiles; namespace StardewModdingAPI { @@ -99,7 +102,7 @@ namespace StardewModdingAPI /********* - ** Protected methods + ** Internal methods *********/ /// Get metadata for mapping assemblies to the current platform. /// The target game platform. @@ -220,6 +223,69 @@ namespace StardewModdingAPI }; } + /// Get the game's static asset setters by (non-normalised) asset name. + /// Derived from . + internal static IDictionary> GetCoreAssetSetters() + { + return new Dictionary> + { + // from Game1.loadContent + ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key), + ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key), + ["LooseSprites\\nightbg"] = (content, key) => Game1.nightbg = content.Load(key), + ["Maps\\MenuTiles"] = (content, key) => Game1.menuTexture = content.Load(key), + ["LooseSprites\\Lighting\\lantern"] = (content, key) => Game1.lantern = content.Load(key), + ["LooseSprites\\Lighting\\windowLight"] = (content, key) => Game1.windowLight = content.Load(key), + ["LooseSprites\\Lighting\\sconceLight"] = (content, key) => Game1.sconceLight = content.Load(key), + ["LooseSprites\\Lighting\\greenLight"] = (content, key) => Game1.cauldronLight = content.Load(key), + ["LooseSprites\\Lighting\\indoorWindowLight"] = (content, key) => Game1.indoorWindowLight = content.Load(key), + ["LooseSprites\\shadow"] = (content, key) => Game1.shadowTexture = content.Load(key), + ["LooseSprites\\Cursors"] = (content, key) => Game1.mouseCursors = content.Load(key), + ["LooseSprites\\ControllerMaps"] = (content, key) => Game1.controllerMaps = content.Load(key), + ["TileSheets\\animations"] = (content, key) => Game1.animations = content.Load(key), + ["Data\\Achievements"] = (content, key) => Game1.achievements = content.Load>(key), + ["Data\\NPCGiftTastes"] = (content, key) => Game1.NPCGiftTastes = content.Load>(key), + ["Fonts\\SpriteFont1"] = (content, key) => Game1.dialogueFont = content.Load(key), + ["Fonts\\SmallFont"] = (content, key) => Game1.smallFont = content.Load(key), + ["Fonts\\tinyFont"] = (content, key) => Game1.tinyFont = content.Load(key), + ["Fonts\\tinyFontBorder"] = (content, key) => Game1.tinyFontBorder = content.Load(key), + ["Maps\\springobjects"] = (content, key) => Game1.objectSpriteSheet = content.Load(key), + ["TileSheets\\crops"] = (content, key) => Game1.cropSpriteSheet = content.Load(key), + ["TileSheets\\emotes"] = (content, key) => Game1.emoteSpriteSheet = content.Load(key), + ["TileSheets\\debris"] = (content, key) => Game1.debrisSpriteSheet = content.Load(key), + ["TileSheets\\Craftables"] = (content, key) => Game1.bigCraftableSpriteSheet = content.Load(key), + ["TileSheets\\rain"] = (content, key) => Game1.rainTexture = content.Load(key), + ["TileSheets\\BuffsIcons"] = (content, key) => Game1.buffsIcons = content.Load(key), + ["Data\\ObjectInformation"] = (content, key) => Game1.objectInformation = content.Load>(key), + ["Data\\BigCraftablesInformation"] = (content, key) => Game1.bigCraftablesInformation = content.Load>(key), + ["Characters\\Farmer\\hairstyles"] = (content, key) => FarmerRenderer.hairStylesTexture = content.Load(key), + ["Characters\\Farmer\\shirts"] = (content, key) => FarmerRenderer.shirtsTexture = content.Load(key), + ["Characters\\Farmer\\hats"] = (content, key) => FarmerRenderer.hatsTexture = content.Load(key), + ["Characters\\Farmer\\accessories"] = (content, key) => FarmerRenderer.accessoriesTexture = content.Load(key), + ["TileSheets\\furniture"] = (content, key) => Furniture.furnitureTexture = content.Load(key), + ["LooseSprites\\font_bold"] = (content, key) => SpriteText.spriteTexture = content.Load(key), + ["LooseSprites\\font_colored"] = (content, key) => SpriteText.coloredTexture = content.Load(key), + ["TileSheets\\weapons"] = (content, key) => Tool.weaponsTexture = content.Load(key), + ["TileSheets\\Projectiles"] = (content, key) => Projectile.projectileSheet = content.Load(key), + + // from Farmer constructor + ["Characters\\Farmer\\farmer_base"] = (content, key) => + { + if (Game1.player != null && Game1.player.isMale) + Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key)); + }, + ["Characters\\Farmer\\farmer_girl_base"] = (content, key) => + { + if (Game1.player != null && !Game1.player.isMale) + Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key)); + } + }; + } + + + /********* + ** Private methods + *********/ /// Get the name of a save directory for the current player. private static string GetSaveFolderName() { diff --git a/src/StardewModdingAPI/Framework/SContentManager.cs b/src/StardewModdingAPI/Framework/SContentManager.cs index 669b0e7a..e6b0cac2 100644 --- a/src/StardewModdingAPI/Framework/SContentManager.cs +++ b/src/StardewModdingAPI/Framework/SContentManager.cs @@ -5,14 +5,10 @@ using System.IO; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; -using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.Framework.Content; using StardewModdingAPI.Framework.Reflection; using StardewValley; -using StardewValley.BellsAndWhistles; -using StardewValley.Objects; -using StardewValley.Projectiles; namespace StardewModdingAPI.Framework { @@ -40,6 +36,12 @@ namespace StardewModdingAPI.Framework /// The private method which generates the locale portion of an asset name. private readonly IPrivateMethod GetKeyLocale; + /// The language codes used in asset keys. + private IDictionary KeyLocales; + + /// The game's static asset setters by normalised asset name. + private readonly IDictionary CoreAssetSetters; + /********* ** Accessors @@ -86,6 +88,48 @@ namespace StardewModdingAPI.Framework } else this.NormaliseAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load logic + + // get asset key locales + this.KeyLocales = this.GetKeyLocales(reflection); + this.CoreAssetSetters = this.GetCoreAssetSetters(); + + } + + /// Get methods to reload core game assets by normalised key. + private IDictionary GetCoreAssetSetters() + { + return Constants.GetCoreAssetSetters() + .ToDictionary>, string, Action>( + p => this.NormaliseAssetName(p.Key), + p => () => p.Value(this, p.Key) + ); + } + + /// Get the locale codes (like ja-JP) used in asset keys. + /// Simplifies access to private game code. + private IDictionary GetKeyLocales(Reflector reflection) + { + // get the private code field directly to avoid changed-code logic + IPrivateField codeField = reflection.GetPrivateField(typeof(LocalizedContentManager), "_currentLangCode"); + + // remember previous settings + LanguageCode previousCode = codeField.GetValue(); + string previousOverride = this.LanguageCodeOverride; + + // create locale => code map + IDictionary map = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + this.LanguageCodeOverride = null; + foreach (LanguageCode code in Enum.GetValues(typeof(LanguageCode))) + { + codeField.SetValue(code); + map[this.GetKeyLocale.Invoke()] = code; + } + + // restore previous settings + codeField.SetValue(previousCode); + this.LanguageCodeOverride = previousOverride; + + return map; } /// Normalise path separators in a file path. For asset keys, see instead. @@ -159,54 +203,55 @@ namespace StardewModdingAPI.Framework return this.GetKeyLocale.Invoke(); } + /// Get the cached asset keys. + public IEnumerable GetAssetKeys() + { + IEnumerable GetAllAssetKeys() + { + foreach (string cacheKey in this.Cache.Keys) + { + this.ParseCacheKey(cacheKey, out string assetKey, out string _); + yield return assetKey; + } + } + + return GetAllAssetKeys().Distinct(); + } + /// Reset the asset cache and reload the game's static assets. + /// Matches the asset keys to invalidate. /// This implementation is derived from . - public void Reset() + public void InvalidateCache(Func predicate) { - this.Monitor.Log("Resetting asset cache...", LogLevel.Trace); - this.Cache.Clear(); - - // from Game1.LoadContent - Game1.daybg = this.Load("LooseSprites\\daybg"); - Game1.nightbg = this.Load("LooseSprites\\nightbg"); - Game1.menuTexture = this.Load("Maps\\MenuTiles"); - Game1.lantern = this.Load("LooseSprites\\Lighting\\lantern"); - Game1.windowLight = this.Load("LooseSprites\\Lighting\\windowLight"); - Game1.sconceLight = this.Load("LooseSprites\\Lighting\\sconceLight"); - Game1.cauldronLight = this.Load("LooseSprites\\Lighting\\greenLight"); - Game1.indoorWindowLight = this.Load("LooseSprites\\Lighting\\indoorWindowLight"); - Game1.shadowTexture = this.Load("LooseSprites\\shadow"); - Game1.mouseCursors = this.Load("LooseSprites\\Cursors"); - Game1.controllerMaps = this.Load("LooseSprites\\ControllerMaps"); - Game1.animations = this.Load("TileSheets\\animations"); - Game1.achievements = this.Load>("Data\\Achievements"); - Game1.NPCGiftTastes = this.Load>("Data\\NPCGiftTastes"); - Game1.dialogueFont = this.Load("Fonts\\SpriteFont1"); - Game1.smallFont = this.Load("Fonts\\SmallFont"); - Game1.tinyFont = this.Load("Fonts\\tinyFont"); - Game1.tinyFontBorder = this.Load("Fonts\\tinyFontBorder"); - Game1.objectSpriteSheet = this.Load("Maps\\springobjects"); - Game1.cropSpriteSheet = this.Load("TileSheets\\crops"); - Game1.emoteSpriteSheet = this.Load("TileSheets\\emotes"); - Game1.debrisSpriteSheet = this.Load("TileSheets\\debris"); - Game1.bigCraftableSpriteSheet = this.Load("TileSheets\\Craftables"); - Game1.rainTexture = this.Load("TileSheets\\rain"); - Game1.buffsIcons = this.Load("TileSheets\\BuffsIcons"); - Game1.objectInformation = this.Load>("Data\\ObjectInformation"); - Game1.bigCraftablesInformation = this.Load>("Data\\BigCraftablesInformation"); - FarmerRenderer.hairStylesTexture = this.Load("Characters\\Farmer\\hairstyles"); - FarmerRenderer.shirtsTexture = this.Load("Characters\\Farmer\\shirts"); - FarmerRenderer.hatsTexture = this.Load("Characters\\Farmer\\hats"); - FarmerRenderer.accessoriesTexture = this.Load("Characters\\Farmer\\accessories"); - Furniture.furnitureTexture = this.Load("TileSheets\\furniture"); - SpriteText.spriteTexture = this.Load("LooseSprites\\font_bold"); - SpriteText.coloredTexture = this.Load("LooseSprites\\font_colored"); - Tool.weaponsTexture = this.Load("TileSheets\\weapons"); - Projectile.projectileSheet = this.Load("TileSheets\\Projectiles"); - - // from Farmer constructor - if (Game1.player != null) - Game1.player.FarmerRenderer = new FarmerRenderer(this.Load("Characters\\Farmer\\farmer_" + (Game1.player.isMale ? "" : "girl_") + "base")); + // find matching asset keys + HashSet purgeCacheKeys = new HashSet(StringComparer.InvariantCultureIgnoreCase); + HashSet purgeAssetKeys = new HashSet(StringComparer.InvariantCultureIgnoreCase); + foreach (string cacheKey in this.Cache.Keys) + { + this.ParseCacheKey(cacheKey, out string assetKey, out string localeCode); + if (predicate(assetKey)) + { + purgeAssetKeys.Add(assetKey); + purgeCacheKeys.Add(cacheKey); + } + } + + // purge from cache + foreach (string key in purgeCacheKeys) + this.Cache.Remove(key); + + // reload core game assets + int reloaded = 0; + foreach (string key in purgeAssetKeys) + { + if (this.CoreAssetSetters.TryGetValue(key, out Action reloadAsset)) + { + reloadAsset(); + reloaded++; + } + } + + this.Monitor.Log($"Invalidated {purgeCacheKeys.Count} cache entries for {purgeAssetKeys.Count} asset keys: {string.Join(", ", purgeCacheKeys.OrderBy(p => p, StringComparer.InvariantCultureIgnoreCase))}. Reloaded {reloaded} core assets.", LogLevel.Trace); } @@ -221,6 +266,33 @@ namespace StardewModdingAPI.Framework || this.Cache.ContainsKey($"{normalisedAssetName}.{this.GetKeyLocale.Invoke()}"); // translated asset } + /// Parse a cache key into its component parts. + /// The input cache key. + /// The original asset key. + /// The asset locale code (or null if not localised). + private void ParseCacheKey(string cacheKey, out string assetKey, out string localeCode) + { + // handle localised key + if (!string.IsNullOrWhiteSpace(cacheKey)) + { + int lastSepIndex = cacheKey.LastIndexOf(".", StringComparison.InvariantCulture); + if (lastSepIndex >= 0) + { + string suffix = cacheKey.Substring(lastSepIndex + 1, cacheKey.Length - lastSepIndex - 1); + if (this.KeyLocales.ContainsKey(suffix)) + { + assetKey = cacheKey.Substring(0, lastSepIndex); + localeCode = cacheKey.Substring(lastSepIndex + 1, cacheKey.Length - lastSepIndex - 1); + return; + } + } + } + + // handle simple key + assetKey = cacheKey; + localeCode = null; + } + /// Load the initial asset from the registered . /// The basic asset metadata. /// Returns the loaded asset metadata, or null if no loader matched. @@ -365,7 +437,8 @@ namespace StardewModdingAPI.Framework // can't know which assets are meant to be disposed. Here we remove current assets from // the cache, but don't dispose them to avoid crashing any code that still references // them. The garbage collector will eventually clean up any unused assets. - this.Reset(); + this.Monitor.Log("Content manager disposed, resetting cache.", LogLevel.Trace); + this.InvalidateCache(p => true); } } } diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 50ab4e25..56c56431 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -796,7 +796,7 @@ namespace StardewModdingAPI if (this.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(object[]) })) deprecationWarnings.Add(() => this.DeprecationManager.Warn(metadata.DisplayName, $"{nameof(Mod)}.{nameof(Mod.Entry)}(object[]) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.0", DeprecationLevel.PendingRemoval)); #else - if (!this.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] {typeof(IModHelper)})) + if (!this.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(IModHelper) })) this.Monitor.Log($"{metadata.DisplayName} doesn't implement Entry() and may not work correctly.", LogLevel.Error); #endif } @@ -812,19 +812,27 @@ namespace StardewModdingAPI { if (metadata.Mod.Helper.Content is ContentHelper helper) { + // TODO: optimise by only reloading assets the new editors/loaders can intercept helper.ObservableAssetEditors.CollectionChanged += (sender, e) => { if (e.NewItems.Count > 0) - this.ContentManager.Reset(); + { + this.Monitor.Log("Detected new asset editor, resetting cache...", LogLevel.Trace); + this.ContentManager.InvalidateCache(p => true); + } }; helper.ObservableAssetLoaders.CollectionChanged += (sender, e) => { if (e.NewItems.Count > 0) - this.ContentManager.Reset(); + { + this.Monitor.Log("Detected new asset loader, resetting cache...", LogLevel.Trace); + this.ContentManager.InvalidateCache(p => true); + } }; } } - this.ContentManager.Reset(); + this.Monitor.Log("Resetting cache to enable interception...", LogLevel.Trace); + this.ContentManager.InvalidateCache(p => true); } /// Reload translations for all mods. -- cgit From 467ad2ffd45f7c034b89b668883bb5271524821d Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Jul 2017 17:36:31 -0400 Subject: let mods invalidate cached assets by name or type (#335) --- .../Framework/ModHelpers/ContentHelper.cs | 27 +++++++++++++++++++++- src/StardewModdingAPI/Framework/SContentManager.cs | 17 ++++++++++---- src/StardewModdingAPI/IContentHelper.cs | 14 +++++++++++ src/StardewModdingAPI/Program.cs | 11 +++++---- 4 files changed, 59 insertions(+), 10 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs index 5f72176e..c052759f 100644 --- a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs @@ -33,6 +33,9 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The friendly mod name for use in errors. private readonly string ModName; + /// Encapsulates monitoring and logging for a given module. + private readonly IMonitor Monitor; + /********* ** Accessors @@ -58,13 +61,15 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The absolute path to the mod folder. /// The unique ID of the relevant mod. /// The friendly mod name for use in errors. - public ContentHelper(SContentManager contentManager, string modFolderPath, string modID, string modName) + /// Encapsulates monitoring and logging. + public ContentHelper(SContentManager contentManager, string modFolderPath, string modID, string modName, IMonitor monitor) : base(modID) { this.ContentManager = contentManager; this.ModFolderPath = modFolderPath; this.ModName = modName; this.ModFolderPathFromContent = this.GetRelativePath(contentManager.FullRootDirectory, modFolderPath); + this.Monitor = monitor; } /// Load content from the game folder or mod folder (if not already cached), and return it. When loading a .png file, this must be called outside the game's draw loop. @@ -176,6 +181,26 @@ namespace StardewModdingAPI.Framework.ModHelpers } } + /// Remove an asset from the content cache so it's reloaded on the next request. This will reload core game assets if needed, but references to the former asset will still show the previous content. + /// The asset key to fetch (if the is ), or the local path to a content file relative to the mod folder. + /// Where to search for a matching content asset. + /// The is empty or contains invalid characters. + /// Returns whether the given asset key was cached. + public bool InvalidateCache(string key, ContentSource source = ContentSource.ModFolder) + { + this.Monitor.Log($"Requested cache invalidation for '{key}' in {source}.", LogLevel.Trace); + string actualKey = this.GetActualAssetKey(key, source); + return this.ContentManager.InvalidateCache((otherKey, type) => otherKey.Equals(actualKey, StringComparison.InvariantCultureIgnoreCase)); + } + + /// Remove all assets of the given type from the cache so they're reloaded on the next request. This can be a very expensive operation and should only be used in very specific cases. This will reload core game assets if needed, but references to the former assets will still show the previous content. + /// The asset type to remove from the cache. + /// Returns whether any assets were invalidated. + public bool InvalidateCache() + { + this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible.", LogLevel.Trace); + return this.ContentManager.InvalidateCache((key, type) => typeof(T).IsAssignableFrom(type)); + } /********* ** Private methods diff --git a/src/StardewModdingAPI/Framework/SContentManager.cs b/src/StardewModdingAPI/Framework/SContentManager.cs index e6b0cac2..0741d04d 100644 --- a/src/StardewModdingAPI/Framework/SContentManager.cs +++ b/src/StardewModdingAPI/Framework/SContentManager.cs @@ -220,8 +220,9 @@ namespace StardewModdingAPI.Framework /// Reset the asset cache and reload the game's static assets. /// Matches the asset keys to invalidate. + /// Returns whether any cache entries were invalidated. /// This implementation is derived from . - public void InvalidateCache(Func predicate) + public bool InvalidateCache(Func predicate) { // find matching asset keys HashSet purgeCacheKeys = new HashSet(StringComparer.InvariantCultureIgnoreCase); @@ -229,7 +230,8 @@ namespace StardewModdingAPI.Framework foreach (string cacheKey in this.Cache.Keys) { this.ParseCacheKey(cacheKey, out string assetKey, out string localeCode); - if (predicate(assetKey)) + Type type = this.Cache[cacheKey].GetType(); + if (predicate(assetKey, type)) { purgeAssetKeys.Add(assetKey); purgeCacheKeys.Add(cacheKey); @@ -251,7 +253,14 @@ namespace StardewModdingAPI.Framework } } - this.Monitor.Log($"Invalidated {purgeCacheKeys.Count} cache entries for {purgeAssetKeys.Count} asset keys: {string.Join(", ", purgeCacheKeys.OrderBy(p => p, StringComparer.InvariantCultureIgnoreCase))}. Reloaded {reloaded} core assets.", LogLevel.Trace); + // report result + if (purgeCacheKeys.Any()) + { + this.Monitor.Log($"Invalidated {purgeCacheKeys.Count} cache entries for {purgeAssetKeys.Count} asset keys: {string.Join(", ", purgeCacheKeys.OrderBy(p => p, StringComparer.InvariantCultureIgnoreCase))}. Reloaded {reloaded} core assets.", LogLevel.Trace); + return true; + } + this.Monitor.Log("Invalidated 0 cache entries.", LogLevel.Trace); + return false; } @@ -438,7 +447,7 @@ namespace StardewModdingAPI.Framework // the cache, but don't dispose them to avoid crashing any code that still references // them. The garbage collector will eventually clean up any unused assets. this.Monitor.Log("Content manager disposed, resetting cache.", LogLevel.Trace); - this.InvalidateCache(p => true); + this.InvalidateCache((key, type) => true); } } } diff --git a/src/StardewModdingAPI/IContentHelper.cs b/src/StardewModdingAPI/IContentHelper.cs index 32a9ff19..9fe29e4d 100644 --- a/src/StardewModdingAPI/IContentHelper.cs +++ b/src/StardewModdingAPI/IContentHelper.cs @@ -20,5 +20,19 @@ namespace StardewModdingAPI /// Where to search for a matching content asset. /// The is empty or contains invalid characters. string GetActualAssetKey(string key, ContentSource source = ContentSource.ModFolder); + +#if !SMAPI_1_x + /// Remove an asset from the content cache so it's reloaded on the next request. This will reload core game assets if needed, but references to the former asset will still show the previous content. + /// The asset key to fetch (if the is ), or the local path to a content file relative to the mod folder. + /// Where to search for a matching content asset. + /// The is empty or contains invalid characters. + /// Returns whether the given asset key was cached. + bool InvalidateCache(string key, ContentSource source = ContentSource.ModFolder); + + /// Remove all assets of the given type from the cache so they're reloaded on the next request. This can be a very expensive operation and should only be used in very specific cases. This will reload core game assets if needed, but references to the former assets will still show the previous content. + /// The asset type to remove from the cache. + /// Returns whether any assets were invalidated. + bool InvalidateCache(); +#endif } } diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 56c56431..969695aa 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -707,15 +707,16 @@ namespace StardewModdingAPI // inject data { + IMonitor monitor = this.GetSecondaryMonitor(metadata.DisplayName); ICommandHelper commandHelper = new CommandHelper(manifest.UniqueID, metadata.DisplayName, this.CommandManager); - IContentHelper contentHelper = new ContentHelper(contentManager, metadata.DirectoryPath, manifest.UniqueID, metadata.DisplayName); + IContentHelper contentHelper = new ContentHelper(contentManager, metadata.DirectoryPath, manifest.UniqueID, metadata.DisplayName, monitor); IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, this.Reflection); IModRegistry modRegistryHelper = new ModRegistryHelper(manifest.UniqueID, this.ModRegistry); ITranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, manifest.Name, contentManager.GetLocale(), contentManager.GetCurrentLanguage()); mod.ModManifest = manifest; mod.Helper = new ModHelper(manifest.UniqueID, metadata.DirectoryPath, jsonHelper, contentHelper, commandHelper, modRegistryHelper, reflectionHelper, translationHelper); - mod.Monitor = this.GetSecondaryMonitor(metadata.DisplayName); + mod.Monitor = monitor; #if SMAPI_1_x mod.PathOnDisk = metadata.DirectoryPath; #endif @@ -818,7 +819,7 @@ namespace StardewModdingAPI if (e.NewItems.Count > 0) { this.Monitor.Log("Detected new asset editor, resetting cache...", LogLevel.Trace); - this.ContentManager.InvalidateCache(p => true); + this.ContentManager.InvalidateCache((key, type) => true); } }; helper.ObservableAssetLoaders.CollectionChanged += (sender, e) => @@ -826,13 +827,13 @@ namespace StardewModdingAPI if (e.NewItems.Count > 0) { this.Monitor.Log("Detected new asset loader, resetting cache...", LogLevel.Trace); - this.ContentManager.InvalidateCache(p => true); + this.ContentManager.InvalidateCache((key, type) => true); } }; } } this.Monitor.Log("Resetting cache to enable interception...", LogLevel.Trace); - this.ContentManager.InvalidateCache(p => true); + this.ContentManager.InvalidateCache((key, type) => true); } /// Reload translations for all mods. -- cgit From eeee6b185d5438e5d44ed0da7c23cf19813b29ea Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Jul 2017 19:39:17 -0400 Subject: use more flexible approach to core asset reloading (#335) --- src/StardewModdingAPI/Constants.cs | 62 ------------ src/StardewModdingAPI/Framework/SContentManager.cs | 26 ++--- src/StardewModdingAPI/Metadata/CoreAssets.cs | 107 +++++++++++++++++++++ src/StardewModdingAPI/StardewModdingAPI.csproj | 1 + 4 files changed, 115 insertions(+), 81 deletions(-) create mode 100644 src/StardewModdingAPI/Metadata/CoreAssets.cs (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index e85d7e2b..af120850 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -11,9 +11,6 @@ using StardewModdingAPI.AssemblyRewriters.Rewriters.Wrappers; using StardewModdingAPI.Events; using StardewModdingAPI.Framework; using StardewValley; -using StardewValley.BellsAndWhistles; -using StardewValley.Objects; -using StardewValley.Projectiles; namespace StardewModdingAPI { @@ -223,65 +220,6 @@ namespace StardewModdingAPI }; } - /// Get the game's static asset setters by (non-normalised) asset name. - /// Derived from . - internal static IDictionary> GetCoreAssetSetters() - { - return new Dictionary> - { - // from Game1.loadContent - ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key), - ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key), - ["LooseSprites\\nightbg"] = (content, key) => Game1.nightbg = content.Load(key), - ["Maps\\MenuTiles"] = (content, key) => Game1.menuTexture = content.Load(key), - ["LooseSprites\\Lighting\\lantern"] = (content, key) => Game1.lantern = content.Load(key), - ["LooseSprites\\Lighting\\windowLight"] = (content, key) => Game1.windowLight = content.Load(key), - ["LooseSprites\\Lighting\\sconceLight"] = (content, key) => Game1.sconceLight = content.Load(key), - ["LooseSprites\\Lighting\\greenLight"] = (content, key) => Game1.cauldronLight = content.Load(key), - ["LooseSprites\\Lighting\\indoorWindowLight"] = (content, key) => Game1.indoorWindowLight = content.Load(key), - ["LooseSprites\\shadow"] = (content, key) => Game1.shadowTexture = content.Load(key), - ["LooseSprites\\Cursors"] = (content, key) => Game1.mouseCursors = content.Load(key), - ["LooseSprites\\ControllerMaps"] = (content, key) => Game1.controllerMaps = content.Load(key), - ["TileSheets\\animations"] = (content, key) => Game1.animations = content.Load(key), - ["Data\\Achievements"] = (content, key) => Game1.achievements = content.Load>(key), - ["Data\\NPCGiftTastes"] = (content, key) => Game1.NPCGiftTastes = content.Load>(key), - ["Fonts\\SpriteFont1"] = (content, key) => Game1.dialogueFont = content.Load(key), - ["Fonts\\SmallFont"] = (content, key) => Game1.smallFont = content.Load(key), - ["Fonts\\tinyFont"] = (content, key) => Game1.tinyFont = content.Load(key), - ["Fonts\\tinyFontBorder"] = (content, key) => Game1.tinyFontBorder = content.Load(key), - ["Maps\\springobjects"] = (content, key) => Game1.objectSpriteSheet = content.Load(key), - ["TileSheets\\crops"] = (content, key) => Game1.cropSpriteSheet = content.Load(key), - ["TileSheets\\emotes"] = (content, key) => Game1.emoteSpriteSheet = content.Load(key), - ["TileSheets\\debris"] = (content, key) => Game1.debrisSpriteSheet = content.Load(key), - ["TileSheets\\Craftables"] = (content, key) => Game1.bigCraftableSpriteSheet = content.Load(key), - ["TileSheets\\rain"] = (content, key) => Game1.rainTexture = content.Load(key), - ["TileSheets\\BuffsIcons"] = (content, key) => Game1.buffsIcons = content.Load(key), - ["Data\\ObjectInformation"] = (content, key) => Game1.objectInformation = content.Load>(key), - ["Data\\BigCraftablesInformation"] = (content, key) => Game1.bigCraftablesInformation = content.Load>(key), - ["Characters\\Farmer\\hairstyles"] = (content, key) => FarmerRenderer.hairStylesTexture = content.Load(key), - ["Characters\\Farmer\\shirts"] = (content, key) => FarmerRenderer.shirtsTexture = content.Load(key), - ["Characters\\Farmer\\hats"] = (content, key) => FarmerRenderer.hatsTexture = content.Load(key), - ["Characters\\Farmer\\accessories"] = (content, key) => FarmerRenderer.accessoriesTexture = content.Load(key), - ["TileSheets\\furniture"] = (content, key) => Furniture.furnitureTexture = content.Load(key), - ["LooseSprites\\font_bold"] = (content, key) => SpriteText.spriteTexture = content.Load(key), - ["LooseSprites\\font_colored"] = (content, key) => SpriteText.coloredTexture = content.Load(key), - ["TileSheets\\weapons"] = (content, key) => Tool.weaponsTexture = content.Load(key), - ["TileSheets\\Projectiles"] = (content, key) => Projectile.projectileSheet = content.Load(key), - - // from Farmer constructor - ["Characters\\Farmer\\farmer_base"] = (content, key) => - { - if (Game1.player != null && Game1.player.isMale) - Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key)); - }, - ["Characters\\Farmer\\farmer_girl_base"] = (content, key) => - { - if (Game1.player != null && !Game1.player.isMale) - Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key)); - } - }; - } - /********* ** Private methods diff --git a/src/StardewModdingAPI/Framework/SContentManager.cs b/src/StardewModdingAPI/Framework/SContentManager.cs index 0741d04d..0854c379 100644 --- a/src/StardewModdingAPI/Framework/SContentManager.cs +++ b/src/StardewModdingAPI/Framework/SContentManager.cs @@ -8,6 +8,7 @@ using Microsoft.Xna.Framework.Content; using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.Framework.Content; using StardewModdingAPI.Framework.Reflection; +using StardewModdingAPI.Metadata; using StardewValley; namespace StardewModdingAPI.Framework @@ -37,10 +38,10 @@ namespace StardewModdingAPI.Framework private readonly IPrivateMethod GetKeyLocale; /// The language codes used in asset keys. - private IDictionary KeyLocales; + private readonly IDictionary KeyLocales; - /// The game's static asset setters by normalised asset name. - private readonly IDictionary CoreAssetSetters; + /// Provides metadata for core game assets. + private readonly CoreAssets CoreAssets; /********* @@ -89,22 +90,12 @@ namespace StardewModdingAPI.Framework else this.NormaliseAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load logic - // get asset key locales + // get asset data + this.CoreAssets = new CoreAssets(this.NormaliseAssetName); this.KeyLocales = this.GetKeyLocales(reflection); - this.CoreAssetSetters = this.GetCoreAssetSetters(); } - /// Get methods to reload core game assets by normalised key. - private IDictionary GetCoreAssetSetters() - { - return Constants.GetCoreAssetSetters() - .ToDictionary>, string, Action>( - p => this.NormaliseAssetName(p.Key), - p => () => p.Value(this, p.Key) - ); - } - /// Get the locale codes (like ja-JP) used in asset keys. /// Simplifies access to private game code. private IDictionary GetKeyLocales(Reflector reflection) @@ -246,11 +237,8 @@ namespace StardewModdingAPI.Framework int reloaded = 0; foreach (string key in purgeAssetKeys) { - if (this.CoreAssetSetters.TryGetValue(key, out Action reloadAsset)) - { - reloadAsset(); + if(this.CoreAssets.ReloadForKey(this, key)) reloaded++; - } } // report result diff --git a/src/StardewModdingAPI/Metadata/CoreAssets.cs b/src/StardewModdingAPI/Metadata/CoreAssets.cs new file mode 100644 index 00000000..256a911a --- /dev/null +++ b/src/StardewModdingAPI/Metadata/CoreAssets.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework.Graphics; +using StardewModdingAPI.Framework; +using StardewValley; +using StardewValley.BellsAndWhistles; +using StardewValley.Objects; +using StardewValley.Projectiles; + +namespace StardewModdingAPI.Metadata +{ + /// Provides metadata about core assets in the game. + internal class CoreAssets + { + /********* + ** Properties + *********/ + /// Normalises an asset key to match the cache key. + protected readonly Func GetNormalisedPath; + + /// The static asset setters. + private readonly IDictionary> StaticSetters; + + + /********* + ** Public methods + *********/ + /// Initialise the core asset data. + /// Normalises an asset key to match the cache key. + public CoreAssets(Func getNormalisedPath) + { + this.GetNormalisedPath = getNormalisedPath; + this.StaticSetters = + new Dictionary> + { + // from Game1.loadContent + ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key), + ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key), + ["LooseSprites\\nightbg"] = (content, key) => Game1.nightbg = content.Load(key), + ["Maps\\MenuTiles"] = (content, key) => Game1.menuTexture = content.Load(key), + ["LooseSprites\\Lighting\\lantern"] = (content, key) => Game1.lantern = content.Load(key), + ["LooseSprites\\Lighting\\windowLight"] = (content, key) => Game1.windowLight = content.Load(key), + ["LooseSprites\\Lighting\\sconceLight"] = (content, key) => Game1.sconceLight = content.Load(key), + ["LooseSprites\\Lighting\\greenLight"] = (content, key) => Game1.cauldronLight = content.Load(key), + ["LooseSprites\\Lighting\\indoorWindowLight"] = (content, key) => Game1.indoorWindowLight = content.Load(key), + ["LooseSprites\\shadow"] = (content, key) => Game1.shadowTexture = content.Load(key), + ["LooseSprites\\Cursors"] = (content, key) => Game1.mouseCursors = content.Load(key), + ["LooseSprites\\ControllerMaps"] = (content, key) => Game1.controllerMaps = content.Load(key), + ["TileSheets\\animations"] = (content, key) => Game1.animations = content.Load(key), + ["Data\\Achievements"] = (content, key) => Game1.achievements = content.Load>(key), + ["Data\\NPCGiftTastes"] = (content, key) => Game1.NPCGiftTastes = content.Load>(key), + ["Fonts\\SpriteFont1"] = (content, key) => Game1.dialogueFont = content.Load(key), + ["Fonts\\SmallFont"] = (content, key) => Game1.smallFont = content.Load(key), + ["Fonts\\tinyFont"] = (content, key) => Game1.tinyFont = content.Load(key), + ["Fonts\\tinyFontBorder"] = (content, key) => Game1.tinyFontBorder = content.Load(key), + ["Maps\\springobjects"] = (content, key) => Game1.objectSpriteSheet = content.Load(key), + ["TileSheets\\crops"] = (content, key) => Game1.cropSpriteSheet = content.Load(key), + ["TileSheets\\emotes"] = (content, key) => Game1.emoteSpriteSheet = content.Load(key), + ["TileSheets\\debris"] = (content, key) => Game1.debrisSpriteSheet = content.Load(key), + ["TileSheets\\Craftables"] = (content, key) => Game1.bigCraftableSpriteSheet = content.Load(key), + ["TileSheets\\rain"] = (content, key) => Game1.rainTexture = content.Load(key), + ["TileSheets\\BuffsIcons"] = (content, key) => Game1.buffsIcons = content.Load(key), + ["Data\\ObjectInformation"] = (content, key) => Game1.objectInformation = content.Load>(key), + ["Data\\BigCraftablesInformation"] = (content, key) => Game1.bigCraftablesInformation = content.Load>(key), + ["Characters\\Farmer\\hairstyles"] = (content, key) => FarmerRenderer.hairStylesTexture = content.Load(key), + ["Characters\\Farmer\\shirts"] = (content, key) => FarmerRenderer.shirtsTexture = content.Load(key), + ["Characters\\Farmer\\hats"] = (content, key) => FarmerRenderer.hatsTexture = content.Load(key), + ["Characters\\Farmer\\accessories"] = (content, key) => FarmerRenderer.accessoriesTexture = content.Load(key), + ["TileSheets\\furniture"] = (content, key) => Furniture.furnitureTexture = content.Load(key), + ["LooseSprites\\font_bold"] = (content, key) => SpriteText.spriteTexture = content.Load(key), + ["LooseSprites\\font_colored"] = (content, key) => SpriteText.coloredTexture = content.Load(key), + ["TileSheets\\weapons"] = (content, key) => Tool.weaponsTexture = content.Load(key), + ["TileSheets\\Projectiles"] = (content, key) => Projectile.projectileSheet = content.Load(key), + + // from Farmer constructor + ["Characters\\Farmer\\farmer_base"] = (content, key) => + { + if (Game1.player != null && Game1.player.isMale) + Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key)); + }, + ["Characters\\Farmer\\farmer_girl_base"] = (content, key) => + { + if (Game1.player != null && !Game1.player.isMale) + Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key)); + } + } + .ToDictionary(p => getNormalisedPath(p.Key), p => p.Value); + } + + /// Reload one of the game's core assets (if applicable). + /// The content manager through which to reload the asset. + /// The asset key to reload. + /// Returns whether an asset was reloaded. + public bool ReloadForKey(SContentManager content, string key) + { + // static assets + if (this.StaticSetters.TryGetValue(key, out Action reload)) + { + reload(content, key); + return true; + } + + return false; + } + } +} diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 4cef91d9..8bbafca1 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -91,6 +91,7 @@ Properties\GlobalAssemblyInfo.cs + -- cgit From e8648e217a3c0e45bf78e903623e6f250f92e88d Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Jul 2017 20:22:33 -0400 Subject: add support for reloading wallpaper and building textures (#335) --- src/StardewModdingAPI/Metadata/CoreAssets.cs | 33 +++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Metadata/CoreAssets.cs b/src/StardewModdingAPI/Metadata/CoreAssets.cs index 256a911a..c350b0da 100644 --- a/src/StardewModdingAPI/Metadata/CoreAssets.cs +++ b/src/StardewModdingAPI/Metadata/CoreAssets.cs @@ -5,6 +5,8 @@ using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.Framework; using StardewValley; using StardewValley.BellsAndWhistles; +using StardewValley.Buildings; +using StardewValley.Locations; using StardewValley.Objects; using StardewValley.Projectiles; @@ -83,7 +85,10 @@ namespace StardewModdingAPI.Metadata { if (Game1.player != null && !Game1.player.isMale) Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key)); - } + }, + + // from Wallpaper constructor + ["Maps\\walls_and_floors"] = (content, key) => Wallpaper.wallpaperTexture = content.Load(key) } .ToDictionary(p => getNormalisedPath(p.Key), p => p.Value); } @@ -101,7 +106,33 @@ namespace StardewModdingAPI.Metadata return true; } + // building textures + if (key.StartsWith(this.GetNormalisedPath("Buildings\\"))) + { + Building[] buildings = this.GetAllBuildings().Where(p => key == this.GetNormalisedPath($"Buildings\\{p.buildingType}")).ToArray(); + if (buildings.Any()) + { + Texture2D texture = content.Load(key); + foreach (Building building in buildings) + building.texture = texture; + return true; + } + return false; + } + return false; } + + + /********* + ** Private methods + *********/ + /// Get all player-constructed buildings in the world. + private IEnumerable GetAllBuildings() + { + return Game1.locations + .OfType() + .SelectMany(p => p.buildings); + } } } -- cgit From 64facdd439b4d924d7214bb2d9f6fd72e009dd42 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Jul 2017 20:44:04 -0400 Subject: add support for reloading more singleton assets (#335) --- src/StardewModdingAPI/Metadata/CoreAssets.cs | 39 +++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 7 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Metadata/CoreAssets.cs b/src/StardewModdingAPI/Metadata/CoreAssets.cs index c350b0da..3818314d 100644 --- a/src/StardewModdingAPI/Metadata/CoreAssets.cs +++ b/src/StardewModdingAPI/Metadata/CoreAssets.cs @@ -9,6 +9,7 @@ using StardewValley.Buildings; using StardewValley.Locations; using StardewValley.Objects; using StardewValley.Projectiles; +using StardewValley.TerrainFeatures; namespace StardewModdingAPI.Metadata { @@ -21,8 +22,8 @@ namespace StardewModdingAPI.Metadata /// Normalises an asset key to match the cache key. protected readonly Func GetNormalisedPath; - /// The static asset setters. - private readonly IDictionary> StaticSetters; + /// Setters which update static or singleton texture fields indexed by normalised asset key. + private readonly IDictionary> SingletonSetters; /********* @@ -33,12 +34,11 @@ namespace StardewModdingAPI.Metadata public CoreAssets(Func getNormalisedPath) { this.GetNormalisedPath = getNormalisedPath; - this.StaticSetters = + this.SingletonSetters = new Dictionary> { // from Game1.loadContent ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key), - ["LooseSprites\\daybg"] = (content, key) => Game1.daybg = content.Load(key), ["LooseSprites\\nightbg"] = (content, key) => Game1.nightbg = content.Load(key), ["Maps\\MenuTiles"] = (content, key) => Game1.menuTexture = content.Load(key), ["LooseSprites\\Lighting\\lantern"] = (content, key) => Game1.lantern = content.Load(key), @@ -75,7 +75,21 @@ namespace StardewModdingAPI.Metadata ["TileSheets\\weapons"] = (content, key) => Tool.weaponsTexture = content.Load(key), ["TileSheets\\Projectiles"] = (content, key) => Projectile.projectileSheet = content.Load(key), - // from Farmer constructor + // from Bush + ["TileSheets\\bushes"] = (content, key) => Bush.texture = content.Load(key), + + // from Critter + ["TileSheets\\critters"] = (content, key) => Critter.critterTexture = content.Load(key), + + // from Farm + ["Buildings\\houses"] = (content, key) => + { + Farm farm = Game1.getFarm(); + if (farm != null) + farm.houseTextures = content.Load(key); + }, + + // from Farmer ["Characters\\Farmer\\farmer_base"] = (content, key) => { if (Game1.player != null && Game1.player.isMale) @@ -87,7 +101,18 @@ namespace StardewModdingAPI.Metadata Game1.player.FarmerRenderer = new FarmerRenderer(content.Load(key)); }, - // from Wallpaper constructor + // from Flooring + ["TerrainFeatures\\Flooring"] = (content, key) => Flooring.floorsTexture = content.Load(key), + + // from FruitTree + ["TileSheets\\fruitTrees"] = (content, key) => FruitTree.texture = content.Load(key), + + // from HoeDirt + ["TerrainFeatures\\hoeDirt"] = (content, key) => HoeDirt.lightTexture = content.Load(key), + ["TerrainFeatures\\hoeDirtDark"] = (content, key) => HoeDirt.darkTexture = content.Load(key), + ["TerrainFeatures\\hoeDirtSnow"] = (content, key) => HoeDirt.snowTexture = content.Load(key), + + // from Wallpaper ["Maps\\walls_and_floors"] = (content, key) => Wallpaper.wallpaperTexture = content.Load(key) } .ToDictionary(p => getNormalisedPath(p.Key), p => p.Value); @@ -100,7 +125,7 @@ namespace StardewModdingAPI.Metadata public bool ReloadForKey(SContentManager content, string key) { // static assets - if (this.StaticSetters.TryGetValue(key, out Action reload)) + if (this.SingletonSetters.TryGetValue(key, out Action reload)) { reload(content, key); return true; -- cgit From 17acf248b66861217d48826e77f24cc311b4a22e Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 31 Jul 2017 21:54:46 -0400 Subject: prevent mods from accessing SMAPI internals using its own reflection helper (#334) --- release-notes.md | 8 ++-- .../Framework/ModHelpers/ReflectionHelper.cs | 46 +++++++++++++++++++++- src/StardewModdingAPI/Program.cs | 2 +- 3 files changed, 50 insertions(+), 6 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index 75b9bbd1..bb7b7dfa 100644 --- a/release-notes.md +++ b/release-notes.md @@ -17,10 +17,12 @@ For mod developers: * Added support for optional dependencies. * Added support for string versions (like `"1.0-alpha"`) in `manifest.json`. * Added `IEquatable` to `ISemanticVersion`. -* Removed all deprecated code. -* Removed support for mods with no `Name`, `Version`, or `UniqueID` in their manifest. -* Removed support for mods with a non-unique `UniqueID` value in their manifest. * Removed the TrainerMod `save` and `load` commands. +* **Breaking changes:** + * Removed all deprecated code. + * Removed support for mods with no `Name`, `Version`, or `UniqueID` in their manifest. + * Removed support for mods with a non-unique `UniqueID` value in their manifest. + * Mods can no longer access SMAPI internals using the reflection helper, to discourage fragile mods. ## 1.15.2 For players: diff --git a/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs b/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs index 9411a97a..14a339da 100644 --- a/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs +++ b/src/StardewModdingAPI/Framework/ModHelpers/ReflectionHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using StardewModdingAPI.Framework.Reflection; namespace StardewModdingAPI.Framework.ModHelpers @@ -13,16 +13,21 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The underlying reflection helper. private readonly Reflector Reflector; + /// The mod name for error messages. + private readonly string ModName; + /********* ** Public methods *********/ /// Construct an instance. /// The unique ID of the relevant mod. + /// The mod name for error messages. /// The underlying reflection helper. - public ReflectionHelper(string modID, Reflector reflector) + public ReflectionHelper(string modID, string modName, Reflector reflector) : base(modID) { + this.ModName = modName; this.Reflector = reflector; } @@ -37,6 +42,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Returns the field wrapper, or null if the field doesn't exist and is false. public IPrivateField GetPrivateField(object obj, string name, bool required = true) { + this.AssertAccessAllowed(obj); return this.Reflector.GetPrivateField(obj, name, required); } @@ -47,6 +53,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the private field is not found. public IPrivateField GetPrivateField(Type type, string name, bool required = true) { + this.AssertAccessAllowed(type); return this.Reflector.GetPrivateField(type, name, required); } @@ -60,6 +67,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the private property is not found. public IPrivateProperty GetPrivateProperty(object obj, string name, bool required = true) { + this.AssertAccessAllowed(obj); return this.Reflector.GetPrivateProperty(obj, name, required); } @@ -70,6 +78,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the private property is not found. public IPrivateProperty GetPrivateProperty(Type type, string name, bool required = true) { + this.AssertAccessAllowed(type); return this.Reflector.GetPrivateProperty(type, name, required); } @@ -89,6 +98,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// public TValue GetPrivateValue(object obj, string name, bool required = true) { + this.AssertAccessAllowed(obj); IPrivateField field = this.GetPrivateField(obj, name, required); return field != null ? field.GetValue() @@ -107,6 +117,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// public TValue GetPrivateValue(Type type, string name, bool required = true) { + this.AssertAccessAllowed(type); IPrivateField field = this.GetPrivateField(type, name, required); return field != null ? field.GetValue() @@ -122,6 +133,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the private field is not found. public IPrivateMethod GetPrivateMethod(object obj, string name, bool required = true) { + this.AssertAccessAllowed(obj); return this.Reflector.GetPrivateMethod(obj, name, required); } @@ -131,6 +143,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the private field is not found. public IPrivateMethod GetPrivateMethod(Type type, string name, bool required = true) { + this.AssertAccessAllowed(type); return this.Reflector.GetPrivateMethod(type, name, required); } @@ -144,6 +157,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the private field is not found. public IPrivateMethod GetPrivateMethod(object obj, string name, Type[] argumentTypes, bool required = true) { + this.AssertAccessAllowed(obj); return this.Reflector.GetPrivateMethod(obj, name, argumentTypes, required); } @@ -154,7 +168,35 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Whether to throw an exception if the private field is not found. public IPrivateMethod GetPrivateMethod(Type type, string name, Type[] argumentTypes, bool required = true) { + this.AssertAccessAllowed(type); return this.Reflector.GetPrivateMethod(type, name, argumentTypes, required); } + + + /********* + ** Private methods + *********/ + /// Assert that mods can use the reflection helper to access the given type. + /// The type being accessed. + private void AssertAccessAllowed(Type type) + { +#if !SMAPI_1_x + // validate type namespace + if (type.Namespace != null) + { + string rootSmapiNamespace = typeof(Program).Namespace; + if (type.Namespace == rootSmapiNamespace || type.Namespace.StartsWith(rootSmapiNamespace + ".")) + throw new InvalidOperationException($"SMAPI blocked access by {this.ModName} to its internals through the reflection API. Accessing the SMAPI internals is strongly discouraged since they're subject to change, which means the mod can break without warning."); + } +#endif + } + + /// Assert that mods can use the reflection helper to access the given type. + /// The object being accessed. + private void AssertAccessAllowed(object obj) + { + if (obj != null) + this.AssertAccessAllowed(obj.GetType()); + } } } diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 969695aa..b51917d9 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -710,7 +710,7 @@ namespace StardewModdingAPI IMonitor monitor = this.GetSecondaryMonitor(metadata.DisplayName); ICommandHelper commandHelper = new CommandHelper(manifest.UniqueID, metadata.DisplayName, this.CommandManager); IContentHelper contentHelper = new ContentHelper(contentManager, metadata.DirectoryPath, manifest.UniqueID, metadata.DisplayName, monitor); - IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, this.Reflection); + IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, metadata.DisplayName, this.Reflection); IModRegistry modRegistryHelper = new ModRegistryHelper(manifest.UniqueID, this.ModRegistry); ITranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, manifest.Name, contentManager.GetLocale(), contentManager.GetCurrentLanguage()); -- cgit From 7992b52f035be5c6229ff0912bfd91084d41d5dc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 31 Jul 2017 23:18:49 -0400 Subject: fix AfterDayStarted event being raised during the new-game intro (#332) --- release-notes.md | 10 +++++----- src/StardewModdingAPI/Framework/SGame.cs | 7 +++++-- 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index bb7b7dfa..4da9132d 100644 --- a/release-notes.md +++ b/release-notes.md @@ -18,11 +18,11 @@ For mod developers: * Added support for string versions (like `"1.0-alpha"`) in `manifest.json`. * Added `IEquatable` to `ISemanticVersion`. * Removed the TrainerMod `save` and `load` commands. -* **Breaking changes:** - * Removed all deprecated code. - * Removed support for mods with no `Name`, `Version`, or `UniqueID` in their manifest. - * Removed support for mods with a non-unique `UniqueID` value in their manifest. - * Mods can no longer access SMAPI internals using the reflection helper, to discourage fragile mods. +* Removed all deprecated code. +* Removed support for mods with no `Name`, `Version`, or `UniqueID` in their manifest. +* Removed support for mods with a non-unique `UniqueID` value in their manifest. +* Restrict mods from accessing SMAPI internals using its reflection helper, to discourage fragile mods. +* Fixed issue where `TimeEvents.AfterDayStarted` was raised during the new-game intro. ## 1.15.2 For players: diff --git a/src/StardewModdingAPI/Framework/SGame.cs b/src/StardewModdingAPI/Framework/SGame.cs index d6f1a05b..bec6538b 100644 --- a/src/StardewModdingAPI/Framework/SGame.cs +++ b/src/StardewModdingAPI/Framework/SGame.cs @@ -19,7 +19,6 @@ using StardewValley.Menus; using StardewValley.Tools; using xTile.Dimensions; using xTile.Layers; -using SFarmer = StardewValley.Farmer; namespace StardewModdingAPI.Framework { @@ -318,6 +317,11 @@ namespace StardewModdingAPI.Framework *********/ if (Context.IsSaveLoaded && !SaveGame.IsProcessing /*still loading save*/ && this.AfterLoadTimer >= 0) { +#if !SMAPI_1_x + if (Game1.dayOfMonth != 0) // wait until new-game intro finishes (world not fully initialised yet) +#endif + this.AfterLoadTimer--; + if (this.AfterLoadTimer == 0) { this.Monitor.Log($"Context: loaded saved game '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace); @@ -329,7 +333,6 @@ namespace StardewModdingAPI.Framework #endif TimeEvents.InvokeAfterDayStarted(this.Monitor); } - this.AfterLoadTimer--; } /********* -- cgit From 9b22f3e004b35f66d9be6af211f20fe126fae209 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 31 Jul 2017 23:48:53 -0400 Subject: fix GraphicsEvents.Resize being raised before the game updates its window data (#328) --- release-notes.md | 3 ++- src/StardewModdingAPI/Events/GraphicsEvents.cs | 6 ++---- src/StardewModdingAPI/Framework/SGame.cs | 23 ++++++++++++++++++----- src/StardewModdingAPI/Program.cs | 1 - 4 files changed, 22 insertions(+), 11 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index 4da9132d..14d0179c 100644 --- a/release-notes.md +++ b/release-notes.md @@ -22,7 +22,8 @@ For mod developers: * Removed support for mods with no `Name`, `Version`, or `UniqueID` in their manifest. * Removed support for mods with a non-unique `UniqueID` value in their manifest. * Restrict mods from accessing SMAPI internals using its reflection helper, to discourage fragile mods. -* Fixed issue where `TimeEvents.AfterDayStarted` was raised during the new-game intro. +* Fixed `GraphicsEvents.Resize` being raised before the game updates its window data. +* Fixed `TimeEvents.AfterDayStarted` being raised during the new-game intro. ## 1.15.2 For players: diff --git a/src/StardewModdingAPI/Events/GraphicsEvents.cs b/src/StardewModdingAPI/Events/GraphicsEvents.cs index 25b976f1..fff51bed 100644 --- a/src/StardewModdingAPI/Events/GraphicsEvents.cs +++ b/src/StardewModdingAPI/Events/GraphicsEvents.cs @@ -51,11 +51,9 @@ namespace StardewModdingAPI.Events ****/ /// Raise a event. /// Encapsulates monitoring and logging. - /// The object which raised the event. - /// The event arguments. - internal static void InvokeResize(IMonitor monitor, object sender, EventArgs e) + internal static void InvokeResize(IMonitor monitor) { - monitor.SafelyRaisePlainEvent($"{nameof(GraphicsEvents)}.{nameof(GraphicsEvents.Resize)}", GraphicsEvents.Resize?.GetInvocationList(), sender, e); + monitor.SafelyRaisePlainEvent($"{nameof(GraphicsEvents)}.{nameof(GraphicsEvents.Resize)}", GraphicsEvents.Resize?.GetInvocationList()); } /**** diff --git a/src/StardewModdingAPI/Framework/SGame.cs b/src/StardewModdingAPI/Framework/SGame.cs index bec6538b..65191931 100644 --- a/src/StardewModdingAPI/Framework/SGame.cs +++ b/src/StardewModdingAPI/Framework/SGame.cs @@ -53,10 +53,6 @@ namespace StardewModdingAPI.Framework /// Whether the game is saving and SMAPI has already raised . private bool IsBetweenSaveEvents; - /// Whether the game's zoom level is at 100% (i.e. nothing should be scaled). - public bool ZoomLevelIsOne => Game1.options.zoomLevel.Equals(1.0f); - - /**** ** Game state ****/ @@ -75,7 +71,10 @@ namespace StardewModdingAPI.Framework /// The previous mouse position on the screen adjusted for the zoom level. private Point PreviousMousePosition; - /// The previous save ID at last check. + /// The window size value at last check. + private Point PreviousWindowSize; + + /// The save ID at last check. private ulong PreviousSaveID; /// A hash of at last check. @@ -352,6 +351,20 @@ namespace StardewModdingAPI.Framework SaveEvents.InvokeAfterReturnToTitle(this.Monitor); } + /********* + ** Window events + *********/ + // Here we depend on the game's viewport instead of listening to the Window.Resize + // event because we need to notify mods after the game handles the resize, so the + // game's metadata (like Game1.viewport) are updated. That's a bit complicated + // since the game adds & removes its own handler on the fly. + if (Game1.viewport.Width != this.PreviousWindowSize.X || Game1.viewport.Height != this.PreviousWindowSize.Y) + { + Point size = new Point(Game1.viewport.Width, Game1.viewport.Height); + GraphicsEvents.InvokeResize(this.Monitor); + this.PreviousWindowSize = size; + } + /********* ** Input events (if window has focus) *********/ diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index b51917d9..0e1930ac 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -185,7 +185,6 @@ namespace StardewModdingAPI ((Form)Control.FromHandle(this.GameInstance.Window.Handle)).FormClosing += (sender, args) => this.Dispose(); #endif this.GameInstance.Exiting += (sender, e) => this.Dispose(); - this.GameInstance.Window.ClientSizeChanged += (sender, e) => GraphicsEvents.InvokeResize(this.Monitor, sender, e); GameEvents.InitializeInternal += (sender, e) => this.InitialiseAfterGameStart(); GameEvents.GameLoadedInternal += (sender, e) => this.CheckForUpdateAsync(); ContentEvents.AfterLocaleChanged += (sender, e) => this.OnLocaleChanged(); -- cgit From 3599daee459ee27bfe9374c675eb71d086fcfd81 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 1 Aug 2017 00:51:27 -0400 Subject: remove support for invalidating mod assets per discussion (#335) --- src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs | 9 ++++----- src/StardewModdingAPI/IContentHelper.cs | 5 ++--- 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs index c052759f..0456ce14 100644 --- a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs @@ -182,14 +182,13 @@ namespace StardewModdingAPI.Framework.ModHelpers } /// Remove an asset from the content cache so it's reloaded on the next request. This will reload core game assets if needed, but references to the former asset will still show the previous content. - /// The asset key to fetch (if the is ), or the local path to a content file relative to the mod folder. - /// Where to search for a matching content asset. + /// The asset key to invalidate in the content folder. /// The is empty or contains invalid characters. /// Returns whether the given asset key was cached. - public bool InvalidateCache(string key, ContentSource source = ContentSource.ModFolder) + public bool InvalidateCache(string key) { - this.Monitor.Log($"Requested cache invalidation for '{key}' in {source}.", LogLevel.Trace); - string actualKey = this.GetActualAssetKey(key, source); + this.Monitor.Log($"Requested cache invalidation for '{key}'.", LogLevel.Trace); + string actualKey = this.GetActualAssetKey(key, ContentSource.GameContent); return this.ContentManager.InvalidateCache((otherKey, type) => otherKey.Equals(actualKey, StringComparison.InvariantCultureIgnoreCase)); } diff --git a/src/StardewModdingAPI/IContentHelper.cs b/src/StardewModdingAPI/IContentHelper.cs index 9fe29e4d..beaaf5d4 100644 --- a/src/StardewModdingAPI/IContentHelper.cs +++ b/src/StardewModdingAPI/IContentHelper.cs @@ -23,11 +23,10 @@ namespace StardewModdingAPI #if !SMAPI_1_x /// Remove an asset from the content cache so it's reloaded on the next request. This will reload core game assets if needed, but references to the former asset will still show the previous content. - /// The asset key to fetch (if the is ), or the local path to a content file relative to the mod folder. - /// Where to search for a matching content asset. + /// The asset key to invalidate in the content folder. /// The is empty or contains invalid characters. /// Returns whether the given asset key was cached. - bool InvalidateCache(string key, ContentSource source = ContentSource.ModFolder); + bool InvalidateCache(string key); /// Remove all assets of the given type from the cache so they're reloaded on the next request. This can be a very expensive operation and should only be used in very specific cases. This will reload core game assets if needed, but references to the former assets will still show the previous content. /// The asset type to remove from the cache. -- cgit From 4ff15b9da965c541c40a11a79cb521cad744900c Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Tue, 1 Aug 2017 12:27:02 -0700 Subject: add weekday property to SDate (#339) --- src/StardewModdingAPI/Utilities/SDate.cs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Utilities/SDate.cs b/src/StardewModdingAPI/Utilities/SDate.cs index e0613491..f6650231 100644 --- a/src/StardewModdingAPI/Utilities/SDate.cs +++ b/src/StardewModdingAPI/Utilities/SDate.cs @@ -19,6 +19,8 @@ namespace StardewModdingAPI.Utilities /// The number of days in a season. private readonly int DaysInSeason = 28; + /// The Day of the Week this date has + public DayOfWeek Weekday; /********* ** Accessors @@ -32,7 +34,6 @@ namespace StardewModdingAPI.Utilities /// The year. public int Year { get; } - /********* ** Public methods *********/ @@ -64,6 +65,8 @@ namespace StardewModdingAPI.Utilities this.Day = day; this.Season = season; this.Year = year; + + this.Weekday = GetDayOfWeek(); } /// Get the current in-game date. @@ -114,6 +117,33 @@ namespace StardewModdingAPI.Utilities return $"{this.Day:00} {this.Season} Y{this.Year}"; } + /// + /// This gets the day of the week from the date + /// + /// A constant describing the day + private DayOfWeek GetDayOfWeek() + { + switch (this.Day % 7) + { + case 0: + return DayOfWeek.Sunday; + case 1: + return DayOfWeek.Monday; + case 2: + return DayOfWeek.Tuesday; + case 3: + return DayOfWeek.Wednesday; + case 4: + return DayOfWeek.Thursday; + case 5: + return DayOfWeek.Friday; + case 6: + return DayOfWeek.Saturday; + default: + return 0; + } + } + /**** ** IEquatable ****/ -- cgit From 201d54bbeb2af5d44c35ad4cf77aadb84f09e135 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 1 Aug 2017 15:35:06 -0400 Subject: standardise code & update release notes (#339) --- release-notes.md | 1 + src/StardewModdingAPI/Utilities/SDate.cs | 62 ++++++++++++++++---------------- 2 files changed, 32 insertions(+), 31 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index 14d0179c..a1d78334 100644 --- a/release-notes.md +++ b/release-notes.md @@ -17,6 +17,7 @@ For mod developers: * Added support for optional dependencies. * Added support for string versions (like `"1.0-alpha"`) in `manifest.json`. * Added `IEquatable` to `ISemanticVersion`. +* Added `SDate.DayOfWeek` field. * Removed the TrainerMod `save` and `load` commands. * Removed all deprecated code. * Removed support for mods with no `Name`, `Version`, or `UniqueID` in their manifest. diff --git a/src/StardewModdingAPI/Utilities/SDate.cs b/src/StardewModdingAPI/Utilities/SDate.cs index f6650231..bc3a2b38 100644 --- a/src/StardewModdingAPI/Utilities/SDate.cs +++ b/src/StardewModdingAPI/Utilities/SDate.cs @@ -19,8 +19,6 @@ namespace StardewModdingAPI.Utilities /// The number of days in a season. private readonly int DaysInSeason = 28; - /// The Day of the Week this date has - public DayOfWeek Weekday; /********* ** Accessors @@ -34,6 +32,12 @@ namespace StardewModdingAPI.Utilities /// The year. public int Year { get; } +#if !SMAPI_1_x + /// The day of week. + public DayOfWeek DayOfWeek { get; } +#endif + + /********* ** Public methods *********/ @@ -65,8 +69,7 @@ namespace StardewModdingAPI.Utilities this.Day = day; this.Season = season; this.Year = year; - - this.Weekday = GetDayOfWeek(); + this.DayOfWeek = this.GetDayOfWeek(); } /// Get the current in-game date. @@ -117,33 +120,6 @@ namespace StardewModdingAPI.Utilities return $"{this.Day:00} {this.Season} Y{this.Year}"; } - /// - /// This gets the day of the week from the date - /// - /// A constant describing the day - private DayOfWeek GetDayOfWeek() - { - switch (this.Day % 7) - { - case 0: - return DayOfWeek.Sunday; - case 1: - return DayOfWeek.Monday; - case 2: - return DayOfWeek.Tuesday; - case 3: - return DayOfWeek.Wednesday; - case 4: - return DayOfWeek.Thursday; - case 5: - return DayOfWeek.Friday; - case 6: - return DayOfWeek.Saturday; - default: - return 0; - } - } - /**** ** IEquatable ****/ @@ -228,6 +204,30 @@ namespace StardewModdingAPI.Utilities /********* ** Private methods *********/ + /// Get the day of week for the current date. + private DayOfWeek GetDayOfWeek() + { + switch (this.Day % 7) + { + case 0: + return DayOfWeek.Sunday; + case 1: + return DayOfWeek.Monday; + case 2: + return DayOfWeek.Tuesday; + case 3: + return DayOfWeek.Wednesday; + case 4: + return DayOfWeek.Thursday; + case 5: + return DayOfWeek.Friday; + case 6: + return DayOfWeek.Saturday; + default: + return 0; + } + } + /// Get the current season index. /// The current season wasn't recognised. private int GetSeasonIndex() -- cgit From 3ffcac3f1f46d696056b665e0f485b0d6f7da53c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 2 Aug 2017 00:26:56 -0400 Subject: fix Context.IsPlayerFree being false when player can't move (#330) For example, the value was false when the player used a tool which wasn't intended. --- release-notes.md | 1 + src/StardewModdingAPI/Context.cs | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index a1d78334..f1e75153 100644 --- a/release-notes.md +++ b/release-notes.md @@ -25,6 +25,7 @@ For mod developers: * Restrict mods from accessing SMAPI internals using its reflection helper, to discourage fragile mods. * Fixed `GraphicsEvents.Resize` being raised before the game updates its window data. * Fixed `TimeEvents.AfterDayStarted` being raised during the new-game intro. +* Fixed `Context.IsPlayerFree` being incorrectly false in some cases (e.g. when using a tool). ## 1.15.2 For players: diff --git a/src/StardewModdingAPI/Context.cs b/src/StardewModdingAPI/Context.cs index 6c5ae40e..119e14c8 100644 --- a/src/StardewModdingAPI/Context.cs +++ b/src/StardewModdingAPI/Context.cs @@ -16,8 +16,11 @@ namespace StardewModdingAPI /// Whether the player has loaded a save and the world has finished initialising. public static bool IsWorldReady { get; internal set; } - /// Whether the player is free to move around (e.g. save is loaded, no menu is displayed, no cutscene is in progress, etc). - public static bool IsPlayerFree => Context.IsWorldReady && Game1.activeClickableMenu == null && Game1.player.CanMove && !Game1.dialogueUp && !Game1.eventUp; + /// Whether is true and the player is free to act in the world (no menu is displayed, no cutscene is in progress, etc). + public static bool IsPlayerFree => Context.IsWorldReady && Game1.activeClickableMenu == null && !Game1.dialogueUp && !Game1.eventUp; + + /// Whether is true and the player is free to move (e.g. not using a tool). + public static bool CanPlayerMove => Context.IsPlayerFree && Game1.player.CanMove; /// Whether the game is currently running the draw loop. This isn't relevant to most mods, since you should use to draw to the screen. public static bool IsInDrawLoop { get; internal set; } -- cgit From a15a2c5d092ca4608e8b85520443aae39063a11f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 2 Aug 2017 02:24:02 -0400 Subject: tweak semantic version regex to fix unnecessary capturing groups --- src/StardewModdingAPI/SemanticVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/SemanticVersion.cs b/src/StardewModdingAPI/SemanticVersion.cs index 782a962b..58f24503 100644 --- a/src/StardewModdingAPI/SemanticVersion.cs +++ b/src/StardewModdingAPI/SemanticVersion.cs @@ -17,7 +17,7 @@ namespace StardewModdingAPI /// - allows hyphens in prerelease tags as synonyms for dots (like "-unofficial-update.3"); /// - doesn't allow '+build' suffixes. /// - private static readonly Regex Regex = new Regex(@"^(?0|[1-9]\d*)\.(?0|[1-9]\d*)(\.(?0|[1-9]\d*))?(?:-(?([a-z0-9]+[\-\.]?)+))?$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture); + private static readonly Regex Regex = new Regex(@"^(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:\.(?0|[1-9]\d*))?(?:-(?(?:[a-z0-9]+[\-\.]?)+))?$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture); /********* -- cgit From 7976df856578045b87e17a3d0ea5074715445622 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 2 Aug 2017 02:36:55 -0400 Subject: optimise semver regex using atomic groups --- src/StardewModdingAPI/SemanticVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/SemanticVersion.cs b/src/StardewModdingAPI/SemanticVersion.cs index 58f24503..26de3aa8 100644 --- a/src/StardewModdingAPI/SemanticVersion.cs +++ b/src/StardewModdingAPI/SemanticVersion.cs @@ -17,7 +17,7 @@ namespace StardewModdingAPI /// - allows hyphens in prerelease tags as synonyms for dots (like "-unofficial-update.3"); /// - doesn't allow '+build' suffixes. /// - private static readonly Regex Regex = new Regex(@"^(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:\.(?0|[1-9]\d*))?(?:-(?(?:[a-z0-9]+[\-\.]?)+))?$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture); + private static readonly Regex Regex = new Regex(@"^(?>(?0|[1-9]\d*))\.(?>(?0|[1-9]\d*))(?>(?:\.(?0|[1-9]\d*))?)(?:-(?(?>[a-z0-9]+[\-\.]?)+))?$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture); /********* -- cgit From baeaf826a9bb185c78732b5f2b91c3f499246f1a Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 7 Aug 2017 14:12:18 -0400 Subject: add asset editors & loaders to content API in 2.0 (#255) --- .../Framework/ModHelpers/ContentHelper.cs | 4 ++-- src/StardewModdingAPI/IContentHelper.cs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs index 0456ce14..1e987f00 100644 --- a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs @@ -47,10 +47,10 @@ namespace StardewModdingAPI.Framework.ModHelpers internal ObservableCollection ObservableAssetLoaders { get; } = new ObservableCollection(); /// Interceptors which provide the initial versions of matching content assets. - internal IList AssetLoaders => this.ObservableAssetLoaders; + public IList AssetLoaders => this.ObservableAssetLoaders; /// Interceptors which edit matching content assets after they're loaded. - internal IList AssetEditors => this.ObservableAssetEditors; + public IList AssetEditors => this.ObservableAssetEditors; /********* diff --git a/src/StardewModdingAPI/IContentHelper.cs b/src/StardewModdingAPI/IContentHelper.cs index beaaf5d4..f32c1d18 100644 --- a/src/StardewModdingAPI/IContentHelper.cs +++ b/src/StardewModdingAPI/IContentHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; @@ -7,6 +8,21 @@ namespace StardewModdingAPI /// Provides an API for loading content assets. public interface IContentHelper : IModLinked { + /********* + ** Accessors + *********/ +#if !SMAPI_1_x + /// Interceptors which provide the initial versions of matching content assets. + IList AssetLoaders { get; } + + /// Interceptors which edit matching content assets after they're loaded. + IList AssetEditors { get; } +#endif + + + /********* + ** Public methods + *********/ /// Load content from the game folder or mod folder (if not already cached), and return it. When loading a .png file, this must be called outside the game's draw loop. /// The expected data type. The main supported types are and dictionaries; other types may be supported by the game's content pipeline. /// The asset key to fetch (if the is ), or the local path to a content file relative to the mod folder. -- cgit From dafebd1626b6c3b34a818f5f161289a6e32fe4af Mon Sep 17 00:00:00 2001 From: spacechase0 Date: Wed, 9 Aug 2017 16:40:10 -0400 Subject: Fix building SMAPI 1.x --- src/StardewModdingAPI/Framework/SGame.cs | 3 +++ src/StardewModdingAPI/Utilities/SDate.cs | 2 ++ 2 files changed, 5 insertions(+) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/SGame.cs b/src/StardewModdingAPI/Framework/SGame.cs index 65191931..755f0274 100644 --- a/src/StardewModdingAPI/Framework/SGame.cs +++ b/src/StardewModdingAPI/Framework/SGame.cs @@ -19,6 +19,9 @@ using StardewValley.Menus; using StardewValley.Tools; using xTile.Dimensions; using xTile.Layers; +#if SMAPI_1_x +using SFarmer = StardewValley.Farmer; +#endif namespace StardewModdingAPI.Framework { diff --git a/src/StardewModdingAPI/Utilities/SDate.cs b/src/StardewModdingAPI/Utilities/SDate.cs index bc3a2b38..d7631598 100644 --- a/src/StardewModdingAPI/Utilities/SDate.cs +++ b/src/StardewModdingAPI/Utilities/SDate.cs @@ -69,7 +69,9 @@ namespace StardewModdingAPI.Utilities this.Day = day; this.Season = season; this.Year = year; +#if !SMAPI_1_x this.DayOfWeek = this.GetDayOfWeek(); +#endif } /// Get the current in-game date. -- cgit From dc99ae761f54dcf8a7eb04e32619788325dd3fc5 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 14 Aug 2017 00:14:45 -0400 Subject: blacklist old versions of Entoarox mods (overhauled for SMAPI 1.11+ compatibility) --- .../StardewModdingAPI.config.json | 39 ++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/StardewModdingAPI.config.json b/src/StardewModdingAPI/StardewModdingAPI.config.json index 72163ec1..7aaf8478 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.config.json +++ b/src/StardewModdingAPI/StardewModdingAPI.config.json @@ -72,6 +72,13 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/257", "http://www.nexusmods.com/stardewvalley/mods/518" ], "Notes": "Needs update for SDV 1.1." }, + { + "Name": "Advanced Location Loader", + "ID": [ "Entoarox.AdvancedLocationLoader" ], + "UpperVersion": "1.2.10", + "UpdateUrls": [ "http://community.playstarbound.com/resources/3619" ], + "Notes": "Overhauled for SMAPI 1.11+ compatibility." + }, { "Name": "Almighty Tool", "ID": [ "AlmightyTool.dll" ], @@ -166,9 +173,9 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha { "Name": "Entoarox Framework", "ID": [ "eacdb74b-4080-4452-b16b-93773cda5cf9", /*since ???*/ "Entoarox.EntoaroxFramework" ], - "UpperVersion": "1.7.5", + "UpperVersion": "1.7.10", "UpdateUrls": [ "http://community.playstarbound.com/resources/4228" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Overhauled for SMAPI 1.11+ compatibility." }, { "Name": "Extended Fridge", @@ -227,6 +234,13 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/501" ], "Notes": "Needs update for SDV 1.2." }, + { + "Name": "More Pets", + "ID": [ "821ce8f6-e629-41ad-9fde-03b54f68b0b6MOREPETS", /* since 1.3 */ "Entoarox.MorePets" ], + "UpperVersion": "1.3.2", + "UpdateUrls": [ "http://community.playstarbound.com/resources/4288" ], + "Notes": "Overhauled for SMAPI 1.11+ compatibility." + }, { "Name": "NoSoilDecay", "ID": [ "289dee03-5f38-4d8e-8ffc-e440198e8610" ], @@ -277,6 +291,20 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/444" ], "Notes": "Needs update for SDV 1.2." }, + { + "Name": "Seasonal Immersion", + "ID": [ "EntoaroxSeasonalHouse", /*since 1.1.0*/ "EntoaroxSeasonalBuildings", /* since 1.6 or earlier*/ "EntoaroxSeasonalImmersion", /*since 1.7*/ "Entoarox.SeasonalImmersion" ], + "UpperVersion": "1.8.2", + "UpdateUrls": [ "http://community.playstarbound.com/resources/4262" ], + "Notes": "Overhauled for SMAPI 1.11+ compatibility." + }, + { + "Name": "Shop Expander", + "ID": [ /*since at least 1.4*/ "821ce8f6-e629-41ad-9fde-03b54f68b0b6", /*since 1.5*/ "EntoaroxShopExpander", /*since 1.5.2*/ "Entoarox.ShopExpander" ], + "UpperVersion": "1.5.3", + "UpdateUrls": [ "http://community.playstarbound.com/resources/4381" ], + "Notes": "Overhauled for SMAPI 1.11+ compatibility." + }, { "Name": "Simple Sprinklers", "ID": [ "SimpleSprinkler.dll" ], @@ -334,6 +362,13 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "UpdateUrls": [ "http://community.playstarbound.com/threads/111526", "http://community.playstarbound.com/threads/132096" ], "Notes": "Needs update for SDV 1.2." }, + { + "Name": "Xnb Loader", + "ID": [ "Entoarox.XnbLoader" ], + "UpperVersion": "1.0.6", + "UpdateUrls": [ "http://community.playstarbound.com/resources/4506" ], + "Notes": "Overhauled for SMAPI 1.11+ compatibility." + }, { "Name": "zDailyIncrease", "ID": [ "zdailyincrease" ], -- cgit From f89d41cdadd8d93f31c9321929d14c88a72fb83c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 14 Aug 2017 00:15:16 -0400 Subject: bump version for upcoming SMAPI 1.15.2 release --- src/StardewModdingAPI/Constants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index af120850..57b86b0d 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -36,7 +36,7 @@ namespace StardewModdingAPI /// SMAPI's current semantic version. public static ISemanticVersion ApiVersion { get; } = #if SMAPI_1_x - new SemanticVersion(1, 15, 1); // alpha-{DateTime.UtcNow:yyyyMMddHHmm} + new SemanticVersion(1, 15, 2); // alpha-{DateTime.UtcNow:yyyyMMddHHmm} #else new SemanticVersion(2, 0, 0, $"alpha-{DateTime.UtcNow:yyyyMMddHHmm}"); #endif -- cgit From 021672e43db021ae436e23ff45f3d85e2a595cb0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 14 Aug 2017 01:57:11 -0400 Subject: add content helper properties for the current language --- release-notes.md | 1 + src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs | 6 ++++++ src/StardewModdingAPI/IContentHelper.cs | 7 +++++++ 3 files changed, 14 insertions(+) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index 90ca4e2c..9bfd1981 100644 --- a/release-notes.md +++ b/release-notes.md @@ -33,6 +33,7 @@ For players: For mod developers: * Added `Context.CanPlayerMove` value for mod convenience. +* Added `helper.Content` properties for the current language. * Fixed `GraphicsEvents.Resize` being raised before the game updates its window data. * Fixed `Context.IsPlayerFree` being incorrectly false in some cases (e.g. when using a tool). diff --git a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs index 1e987f00..e94d309e 100644 --- a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs @@ -40,6 +40,12 @@ namespace StardewModdingAPI.Framework.ModHelpers /********* ** Accessors *********/ + /// The game's current locale code (like pt-BR). + public string CurrentLocale => this.ContentManager.GetLocale(); + + /// The game's current locale as an enum value. + public LocalizedContentManager.LanguageCode CurrentLocaleConstant => this.ContentManager.GetCurrentLanguage(); + /// The observable implementation of . internal ObservableCollection ObservableAssetEditors { get; } = new ObservableCollection(); diff --git a/src/StardewModdingAPI/IContentHelper.cs b/src/StardewModdingAPI/IContentHelper.cs index f32c1d18..b4557134 100644 --- a/src/StardewModdingAPI/IContentHelper.cs +++ b/src/StardewModdingAPI/IContentHelper.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; +using StardewValley; namespace StardewModdingAPI { @@ -19,6 +20,12 @@ namespace StardewModdingAPI IList AssetEditors { get; } #endif + /// The game's current locale code (like pt-BR). + string CurrentLocale { get; } + + /// The game's current locale as an enum value. + LocalizedContentManager.LanguageCode CurrentLocaleConstant { get; } + /********* ** Public methods -- cgit From 56c66ca4e550ae482ad82dd7f87b0b1fa6adfb60 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 14 Aug 2017 02:14:25 -0400 Subject: update compatibility list --- .../StardewModdingAPI.config.json | 179 ++++++++++++++------- 1 file changed, 120 insertions(+), 59 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/StardewModdingAPI.config.json b/src/StardewModdingAPI/StardewModdingAPI.config.json index 7aaf8478..d393f5a9 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.config.json +++ b/src/StardewModdingAPI/StardewModdingAPI.config.json @@ -36,6 +36,11 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "GPeters-AnimalMoodFix" ], "ReasonPhrase": "the animal mood bugs were fixed in Stardew Valley 1.2." }, + { + "Name": "Colored Chests", + "ID": [ "4befde5c-731c-4853-8e4b-c5cdf946805f" ], + "ReasonPhrase": "colored chests were added in Stardew Valley 1.1." + }, { "Name": "Modder Serialization Utility", "ID": [ "SerializerUtils-0-1" ], @@ -49,7 +54,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha { "Name": "StarDustCore", "ID": [ "StarDustCore" ], - "ReasonPhrase": "it was only used by earlier versions of Save Anywhere (which no longer uses it), and is no longer maintained." + "ReasonPhrase": "it was only used by earlier versions of Save Anywhere, and is no longer used or maintained." }, { "Name": "XmlSerializerRetool", @@ -70,7 +75,15 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "AccessChestAnywhere" ], "UpperVersion": "1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/257", "http://www.nexusmods.com/stardewvalley/mods/518" ], - "Notes": "Needs update for SDV 1.1." + "Notes": "Broke in SDV 1.1." + }, + { + "Name": "AdjustArtisanPrices", + "ID": [ "1e36d4ca-c7ef-4dfb-9927-d27a6c3c8bdc" ], + "UpperVersion": "0.0", + "UpperVersionLabel": "0.01", + "UpdateUrls": [ "http://community.playstarbound.com/resources/3532", "http://community.playstarbound.com/threads/132096" ], + "Notes": "Broke in SMAPI 1.9." }, { "Name": "Advanced Location Loader", @@ -84,91 +97,84 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "AlmightyTool.dll" ], "UpperVersion": "1.1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/439" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Better Sprinklers", "ID": [ "SPDSprinklersMod", /*since 2.3*/ "Speeder.BetterSprinklers" ], "UpperVersion": "2.3.1-pathoschild-update", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/41", "http://community.playstarbound.com/threads/132096" ], - "Notes": "Needs update for SDV 1.2 and to migrate broken TimeEvents.AfterDayOfMonthChanged." + "Notes": "Broke in SDV 1.2." }, { "Name": "Birthday Mail", "ID": [ "005e02dc-d900-425c-9c68-1ff55c5a295d" ], "UpperVersion": "1.2.2", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/276", "http://community.playstarbound.com/threads/132096" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Chest Label System", "ID": [ "SPDChestLabel" ], "UpperVersion": "1.6", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/242", "http://community.playstarbound.com/threads/132096" ], - "Notes": "Needs update for SDV 1.1." + "Notes": "Broke in SDV 1.1." }, { "Name": "Chest Pooling", "ID": [ "ChestPooling.dll" ], "UpperVersion": "1.2", "UpdateUrls": [ "http://community.playstarbound.com/threads/111988" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Chests Anywhere", "ID": [ "ChestsAnywhere", /*since 1.9*/ "Pathoschild.ChestsAnywhere" ], "UpperVersion": "1.9-beta", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/518" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "CJB Automation", "ID": [ "CJBAutomation" ], "UpperVersion": "1.4", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/211", "http://www.nexusmods.com/stardewvalley/mods/1063" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "CJB Cheats Menu", "ID": [ "CJBCheatsMenu" ], "UpperVersion": "1.12", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/4" ], - "Notes": "Needs update for SDV 1.1." + "Notes": "Broke in SDV 1.1." }, { "Name": "CJB Item Spawner", "ID": [ "CJBItemSpawner" ], "UpperVersion": "1.5", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/93" ], - "Notes": "Needs update for SDV 1.1." + "Notes": "Broke in SDV 1.1." }, { "Name": "CJB Show Item Sell Price", "ID": [ "CJBShowItemSellPrice" ], "UpperVersion": "1.6", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/93" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Cooking Skill", "ID": [ "CookingSkill" ], "UpperVersion": "1.0.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/522" ], - "Notes": "Needs update for SDV 1.2." - }, - { - "Name": "Cooking Skill Prestige Adapter", - "ID": [ "20d6b8a3-b6e7-460b-a6e4-07c2b0cb6c63" ], - "UpperVersion": "1.0.4", - "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/569", "http://community.playstarbound.com/threads/132096" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Enemy Health Bars", "ID": [ "SPDHealthBar" ], "UpperVersion": "1.7", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/193", "http://community.playstarbound.com/threads/132096" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Entoarox Framework", @@ -183,56 +189,63 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "UpperVersion": "1.0", "UpperVersionLabel": "0.94", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/485" ], - "Notes": "Needs update for SDV 1.2. Actual upper version is 0.94, but mod incorrectly sets it to 1.0 in the manifest." + "Notes": "Broke in SDV 1.2. Actual upper version is 0.94, but mod incorrectly sets it to 1.0 in the manifest." }, { - "Name": "FarmAutomation.ItemCollector", - "ID": [ "FarmAutomation.ItemCollector.dll", /*since 0.4*/ "Maddy99.FarmAutomation.ItemCollector" ], + "Name": "Farm Automation: Item Collector", + "ID": [ "FarmAutomation.ItemCollector.dll" ], + "UpperVersion": "1.0", + "UpdateUrls": [ "http://community.playstarbound.com/threads/111931", "http://community.playstarbound.com/threads/125172" ], + "Notes": "Broke in SDV 1.2." + }, + { + "Name": "Farm Automation Unofficial: Item Collector", + "ID": [ "Maddy99.FarmAutomation.ItemCollector" ], "UpperVersion": "0.4", "UpdateUrls": [ "http://community.playstarbound.com/threads/125172" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Instant Geode", "ID": [ "InstantGeode" ], "UpperVersion": "1.12", "UpdateUrls": [ "http://community.playstarbound.com/threads/109038", "http://community.playstarbound.com/threads/132096" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Gate Opener", "ID": [ "GateOpener.dll", /*since 1.1*/ "mralbobo.GateOpener" ], "UpperVersion": "1.0.1", "UpdateUrls": [ "http://community.playstarbound.com/threads/111988" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Get Dressed", "ID": [ "GetDressed.dll", /*since 3.3*/ "Advize.GetDressed" ], "UpperVersion": "3.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/331" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Gift Taste Helper", "ID": [ "8008db57-fa67-4730-978e-34b37ef191d6" ], "UpperVersion": "2.3.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/229" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Lookup Anything", "ID": [ "LookupAnything", /*since 1.10.1*/ "Pathoschild.LookupAnything" ], "UpperVersion": "1.10.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/541" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Makeshift Multiplayer", "ID": [ "StardewValleyMP", /*since 0.3*/ "spacechase0.StardewValleyMP" ], "UpperVersion": "0.3.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/501" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "More Pets", @@ -246,7 +259,7 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "289dee03-5f38-4d8e-8ffc-e440198e8610" ], "UpperVersion": "0.5", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/237", "http://community.playstarbound.com/threads/132096" ], - "Notes": "Needs update for SDV 1.2, and uses Assembly.GetExecutingAssembly().Location." + "Notes": "Broke in SDV 1.2, and uses Assembly.GetExecutingAssembly().Location." }, { "Name": "NPC Map Locations", @@ -254,42 +267,49 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "LowerVersion": "1.42", "UpperVersion": "1.43", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/239" ], - "ReasonPhrase": "These versions have an update check error which crash the game." + "ReasonPhrase": "this version has an update check error which crashes the game." }, { "Name": "Part of the Community", "ID": [ "SB_PotC" ], "UpperVersion": "1.0.8", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/923" ], - "ReasonPhrase": "Needs update for SDV 1.2." + "ReasonPhrase": "Broke in SDV 1.2." + }, + { + "Name": "Persival's BundleMod", + "ID": [ "BundleMod.dll" ], + "UpperVersion": "1.0", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/438", "http://community.playstarbound.com/threads/132096" ], + "Notes": "Broke in SDV 1.1." }, { "Name": "Point-and-Plant", "ID": [ "PointAndPlant.dll" ], "UpperVersion": "1.0.2", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/572" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "PrairieKingMadeEasy", "ID": [ "PrairieKingMadeEasy.dll" ], - "UpperVersion": "1.0.0", + "UpperVersion": "1.0", "UpdateUrls": [ "http://community.playstarbound.com/resources/3594", "http://community.playstarbound.com/threads/132096" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Rush Orders", "ID": [ "RushOrders", /*since 1.1*/ "spacechase0.RushOrders" ], "UpperVersion": "1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/605" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Save Anywhere", - "ID": [ "SaveAnywhere" ], + "ID": [ "{'ID':'SaveAnywhere', 'Name':'Save Anywhere'}" ], // disambiguate from Night Owl "UpperVersion": "2.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/444" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Seasonal Immersion", @@ -310,57 +330,98 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "SimpleSprinkler.dll" ], "UpperVersion": "1.4", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/76" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." + }, + { + "Name": "Siv's Marriage Mod", + "ID": [ "6266959802" ], + "UpperVersion": "1.2.2", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/366", "http://community.playstarbound.com/threads/132096" ], + "Notes": "Broke in SMAPI 1.9 (has multiple Mod instances)." + }, + { + "Name": "Skill Prestige: Cooking Adapter", + "ID": [ "20d6b8a3-b6e7-460b-a6e4-07c2b0cb6c63" ], + "UpperVersion": "1.0.4", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/569", "http://community.playstarbound.com/threads/132096" ], + "Notes": "Broke in SDV 1.2." }, { "Name": "Sprint and Dash", "ID": [ "SPDSprintAndDash" ], "UpperVersion": "1.0", "UpdateUrls": [ "http://community.playstarbound.com/resources/3531", "http://community.playstarbound.com/resources/4201" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Sprint and Dash Redux", "ID": [ "SPDSprintAndDash" ], "UpperVersion": "1.2", "UpdateUrls": [ "http://community.playstarbound.com/resources/4201" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." + }, + { + "Name": "Sprinting Mod", + "ID": [ "a10d3097-b073-4185-98ba-76b586cba00c" ], + "UpperVersion": "2.1", + "UpdateUrls": [ "http://community.playstarbound.com/threads/108313", "http://community.playstarbound.com/threads/132096" ], + "Notes": "Broke in SDV 1.2." }, { "Name": "StackSplitX", "ID": [ "StackSplitX.dll" ], "UpperVersion": "1.2", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/798" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." + }, + { + "Name": "Tainted Cellar", + "ID": [ "TaintedCellar.dll" ], + "UpperVersion": "1.0", + "UpdateUrls": [ "http://community.playstarbound.com/threads/115735", "http://community.playstarbound.com/threads/132096" ], + "Notes": "Broke in SDV 1.1 or 1.11." }, { "Name": "Teleporter", "ID": [ "Teleporter" ], "UpperVersion": "1.0.2", "UpdateUrls": [ "http://community.playstarbound.com/resources/4374" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." + }, + { + "Name": "Three-heart Dance Partner", + "ID": [ "ThreeHeartDancePartner" ], + "UpperVersion": "1.0.1", + "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/500", "http://community.playstarbound.com/threads/132096" ], + "Notes": "Broke in SDV 1.2." }, { "Name": "TimeSpeed", - "ID": [ "TimeSpeed.dll", /* since 2.0.3 */ "4108e859-333c-4fec-a1a7-d2e18c1019fe", /*since 2.1*/ "community.TimeSpeed" ], + "ID": [ "TimeSpeed.dll", /* since 2.0.3 */ "{ID:'4108e859-333c-4fec-a1a7-d2e18c1019fe', Name:'TimeSpeed'}", /* since 2.0.3 */ "{ID:'4108e859-333c-4fec-a1a7-d2e18c1019fe', Name:'TimeSpeed Mod (unofficial)'}", /*since 2.1*/ "community.TimeSpeed" ], // disambiguate other mods by Alpha_Omegasis "UpperVersion": "2.2", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/169" ], - "Notes": "Needs update for SDV 1.2 and to migrate broken TimeEvents.AfterDayOfMonthChanged." + "Notes": "Broke in SDV 1.2." }, { "Name": "UiModSuite", "ID": [ "Demiacle.UiModSuite" ], - "UpperVersion": "0.5", - "UpperVersionLabel": "1.0", + "UpperVersion": "1.0", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1023" ], - "Notes": "Needs update for SDV 1.2. Actual upper version is 1.0, but mod incorrectly sets it to 0.5 in the manifest." + "Notes": "Broke in SDV 1.2." }, { "Name": "Weather Controller", "ID": [ "WeatherController.dll" ], "UpperVersion": "1.0.2", "UpdateUrls": [ "http://community.playstarbound.com/threads/111526", "http://community.playstarbound.com/threads/132096" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." + }, + { + "Name": "Wonderful Farm Life", + "ID": [ "WonderfulFarmLife.dll" ], + "UpperVersion": "1.0", + "UpdateUrls": [ "http://community.playstarbound.com/threads/115384", "http://community.playstarbound.com/threads/132096" ], + "Notes": "Broke in SDV 1.1 or 1.11." }, { "Name": "Xnb Loader", @@ -374,56 +435,56 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "ID": [ "zdailyincrease" ], "UpperVersion": "1.2", "UpdateUrls": [ "http://community.playstarbound.com/resources/4247" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Zoom Out Extreme", "ID": [ "ZoomMod" ], "UpperVersion": "0.1", "UpdateUrls": [ "http://community.playstarbound.com/threads/115028" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Zoryn's Better RNG", "ID": [ "76b6d1e1-f7ba-4d72-8c32-5a1e6d2716f6", /*since 1.6*/ "Zoryn.BetterRNG" ], "UpperVersion": "1.6", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Zoryn's Calendar Anywhere", "ID": [ "a41c01cd-0437-43eb-944f-78cb5a53002a", /*since 1.6*/ "Zoryn.CalendarAnywhere" ], "UpperVersion": "1.6", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Zoryn's Health Bars", "ID": [ "HealthBars.dll", /*since 1.6*/ "Zoryn.HealthBars" ], "UpperVersion": "1.6", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Zoryn's Junimo Deposit Anywhere", "ID": [ "f93a4fe8-cade-4146-9335-b5f82fbbf7bc", /*since 1.6*/ "Zoryn.JunimoDepositAnywhere" ], "UpperVersion": "1.7", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Zoryn's Movement Mod", "ID": [ "8a632929-8335-484f-87dd-c29d2ba3215d", /*since 1.6*/ "Zoryn.MovementModifier" ], "UpperVersion": "1.6", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." }, { "Name": "Zoryn's Regen Mod", "ID": [ "dfac4383-1b6b-4f33-ae4e-37fc23e5252e", /*since 1.6*/ "Zoryn.RegenMod" ], "UpperVersion": "1.6", "UpdateUrls": [ "https://github.com/Zoryn4163/SMAPI-Mods/releases" ], - "Notes": "Needs update for SDV 1.2." + "Notes": "Broke in SDV 1.2." } ] } -- cgit From 67b1a8398f665a745f57f6d53532e504979ea40f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 14 Aug 2017 07:55:21 -0400 Subject: fix SemanticVersion not being deserialisable through Json.NET --- release-notes.md | 7 ++++--- .../Utilities/SemanticVersionTests.cs | 17 +++++++++++++++++ src/StardewModdingAPI/SemanticVersion.cs | 16 +++++++++------- 3 files changed, 30 insertions(+), 10 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index 9bfd1981..f8980af4 100644 --- a/release-notes.md +++ b/release-notes.md @@ -32,10 +32,11 @@ For players: * Updated compatibility list. For mod developers: -* Added `Context.CanPlayerMove` value for mod convenience. -* Added `helper.Content` properties for the current language. +* Added `Context.CanPlayerMove` property for mod convenience. +* Added content helper properties for the game's current language. +* Fixed `Context.IsPlayerFree` being false if the player is performing an action. * Fixed `GraphicsEvents.Resize` being raised before the game updates its window data. -* Fixed `Context.IsPlayerFree` being incorrectly false in some cases (e.g. when using a tool). +* Fixed `SemanticVersion` not being deserialisable through Json.NET. For SMAPI developers: * Internal changes to support the upcoming SMAPI 2.0 release. diff --git a/src/StardewModdingAPI.Tests/Utilities/SemanticVersionTests.cs b/src/StardewModdingAPI.Tests/Utilities/SemanticVersionTests.cs index db46aee4..03cd26c9 100644 --- a/src/StardewModdingAPI.Tests/Utilities/SemanticVersionTests.cs +++ b/src/StardewModdingAPI.Tests/Utilities/SemanticVersionTests.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; using NUnit.Framework; using StardewModdingAPI.Framework; @@ -207,6 +208,22 @@ namespace StardewModdingAPI.Tests.Utilities return version.IsBetween(lower, upper); } + /**** + ** Serialisable + ****/ + [Test(Description = "Assert that SemanticVersion can be round-tripped through JSON with no special configuration.")] + [TestCase("1.0")] + public void Serialisable(string versionStr) + { + // act + string json = JsonConvert.SerializeObject(new SemanticVersion(versionStr)); + SemanticVersion after = JsonConvert.DeserializeObject(json); + + // assert + Assert.IsNotNull(after, "The semantic version after deserialisation is unexpectedly null."); + Assert.AreEqual(versionStr, after.ToString(), "The semantic version after deserialisation doesn't match the input version."); + } + /**** ** GameVersion ****/ diff --git a/src/StardewModdingAPI/SemanticVersion.cs b/src/StardewModdingAPI/SemanticVersion.cs index 26de3aa8..e448eae1 100644 --- a/src/StardewModdingAPI/SemanticVersion.cs +++ b/src/StardewModdingAPI/SemanticVersion.cs @@ -1,5 +1,6 @@ using System; using System.Text.RegularExpressions; +using Newtonsoft.Json; namespace StardewModdingAPI { @@ -40,15 +41,16 @@ namespace StardewModdingAPI ** Public methods *********/ /// Construct an instance. - /// The major version incremented for major API changes. - /// The minor version incremented for backwards-compatible changes. - /// The patch version for backwards-compatible bug fixes. + /// The major version incremented for major API changes. + /// The minor version incremented for backwards-compatible changes. + /// The patch version for backwards-compatible bug fixes. /// An optional build tag. - public SemanticVersion(int major, int minor, int patch, string build = null) + [JsonConstructor] + public SemanticVersion(int majorVersion, int minorVersion, int patchVersion, string build = null) { - this.MajorVersion = major; - this.MinorVersion = minor; - this.PatchVersion = patch; + this.MajorVersion = majorVersion; + this.MinorVersion = minorVersion; + this.PatchVersion = patchVersion; this.Build = this.GetNormalisedTag(build); } -- cgit From c47e43a1e92cc8389ef06bd800251fbe0458fb76 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 14 Aug 2017 08:11:11 -0400 Subject: rename crossplatform.targets to common.targets for reuse --- .../StardewModdingAPI.AssemblyRewriters.csproj | 2 +- .../StardewModdingAPI.Installer.csproj | 2 +- .../StardewModdingAPI.Tests.csproj | 2 +- src/StardewModdingAPI.sln | 4 +- src/StardewModdingAPI/StardewModdingAPI.csproj | 2 +- src/TrainerMod/TrainerMod.csproj | 6 +- src/common.targets | 73 ++++++++++++++++++++++ src/crossplatform.targets | 68 -------------------- 8 files changed, 80 insertions(+), 79 deletions(-) create mode 100644 src/common.targets delete mode 100644 src/crossplatform.targets (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 7a12a8e9..95705a48 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -68,6 +68,6 @@ - + \ No newline at end of file diff --git a/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj b/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj index 765364dc..58ce519c 100644 --- a/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj +++ b/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj @@ -51,6 +51,6 @@ - + \ No newline at end of file diff --git a/src/StardewModdingAPI.Tests/StardewModdingAPI.Tests.csproj b/src/StardewModdingAPI.Tests/StardewModdingAPI.Tests.csproj index 9bfd7567..7602b2e1 100644 --- a/src/StardewModdingAPI.Tests/StardewModdingAPI.Tests.csproj +++ b/src/StardewModdingAPI.Tests/StardewModdingAPI.Tests.csproj @@ -64,6 +64,6 @@ StardewModdingAPI - + \ No newline at end of file diff --git a/src/StardewModdingAPI.sln b/src/StardewModdingAPI.sln index 4d27e51b..9c3f18f8 100644 --- a/src/StardewModdingAPI.sln +++ b/src/StardewModdingAPI.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.14 +VisualStudioVersion = 15.0.26430.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrainerMod", "TrainerMod\TrainerMod.csproj", "{28480467-1A48-46A7-99F8-236D95225359}" EndProject @@ -12,8 +12,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "metadata", "metadata", "{86 .editorconfig = .editorconfig ..\.gitattributes = ..\.gitattributes ..\.gitignore = ..\.gitignore + common.targets = common.targets ..\CONTRIBUTING.md = ..\CONTRIBUTING.md - crossplatform.targets = crossplatform.targets GlobalAssemblyInfo.cs = GlobalAssemblyInfo.cs ..\LICENSE = ..\LICENSE prepare-install-package.targets = prepare-install-package.targets diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 8bbafca1..ee03aea2 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -259,7 +259,7 @@ StardewModdingAPI.AssemblyRewriters - + diff --git a/src/TrainerMod/TrainerMod.csproj b/src/TrainerMod/TrainerMod.csproj index 57c8a907..03b25f8b 100644 --- a/src/TrainerMod/TrainerMod.csproj +++ b/src/TrainerMod/TrainerMod.csproj @@ -99,12 +99,8 @@ - - - - - + diff --git a/src/common.targets b/src/common.targets new file mode 100644 index 00000000..f320cd9a --- /dev/null +++ b/src/common.targets @@ -0,0 +1,73 @@ + + + + + + + + + $(HOME)/GOG Games/Stardew Valley/game + $(HOME)/.local/share/Steam/steamapps/common/Stardew Valley + $(HOME)/.steam/steam/steamapps/common/Stardew Valley + + /Applications/Stardew Valley.app/Contents/MacOS + $(HOME)/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS + + C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley + C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley + $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\GOG.com\Games\1453375253', 'PATH', null, RegistryView.Registry32)) + $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150', 'InstallLocation', null, RegistryView.Registry64, RegistryView.Registry32)) + + + + + + + $(DefineConstants);SMAPI_FOR_WINDOWS + + + + False + + + False + + + False + + + False + + + $(GamePath)\Stardew Valley.exe + False + + + $(GamePath)\xTile.dll + False + False + + + + + + $(DefineConstants);SMAPI_FOR_UNIX + + + + $(GamePath)\MonoGame.Framework.dll + False + False + + + $(GamePath)\StardewValley.exe + False + + + $(GamePath)\xTile.dll + False + + + + + \ No newline at end of file diff --git a/src/crossplatform.targets b/src/crossplatform.targets deleted file mode 100644 index 66e15abd..00000000 --- a/src/crossplatform.targets +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - $(HOME)/GOG Games/Stardew Valley/game - $(HOME)/.local/share/Steam/steamapps/common/Stardew Valley - $(HOME)/.steam/steam/steamapps/common/Stardew Valley - - /Applications/Stardew Valley.app/Contents/MacOS - $(HOME)/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS - - C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley - C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley - $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\GOG.com\Games\1453375253', 'PATH', null, RegistryView.Registry32)) - $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150', 'InstallLocation', null, RegistryView.Registry64, RegistryView.Registry32)) - - - - - $(DefineConstants);SMAPI_FOR_WINDOWS - - - - False - - - False - - - False - - - False - - - $(GamePath)\Stardew Valley.exe - False - - - $(GamePath)\xTile.dll - False - False - - - - - - $(DefineConstants);SMAPI_FOR_UNIX - - - - $(GamePath)\MonoGame.Framework.dll - False - False - - - $(GamePath)\StardewValley.exe - False - - - $(GamePath)\xTile.dll - False - - - - - \ No newline at end of file -- cgit From 72d42ddff8be9bce4e9ab6a1ac04b9cc26deb42f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 14 Aug 2017 08:22:50 -0400 Subject: move custom build configuration into common.targets to simplify maintenance --- src/StardewModdingAPI/StardewModdingAPI.csproj | 30 ------------------------ src/TrainerMod/TrainerMod.csproj | 5 ---- src/common.targets | 32 ++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 35 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index ee03aea2..6691b2b5 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -261,34 +261,4 @@ - - - - - - - - - - - - - - - - - - - - - - - Program - $(GamePath)\StardewModdingAPI.exe - $(GamePath) - - - - - \ No newline at end of file diff --git a/src/TrainerMod/TrainerMod.csproj b/src/TrainerMod/TrainerMod.csproj index 03b25f8b..73b40050 100644 --- a/src/TrainerMod/TrainerMod.csproj +++ b/src/TrainerMod/TrainerMod.csproj @@ -101,9 +101,4 @@ - - - - - \ No newline at end of file diff --git a/src/common.targets b/src/common.targets index f320cd9a..81e7e2a5 100644 --- a/src/common.targets +++ b/src/common.targets @@ -70,4 +70,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Program + $(GamePath)\StardewModdingAPI.exe + $(GamePath) + + + + + \ No newline at end of file -- cgit From 1e48e7d12b4ef3ad9f630f530cc5495eca3593ec Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 16 Aug 2017 21:46:01 -0400 Subject: add tool tilesheet to core assets to reload (#346) --- src/StardewModdingAPI/Metadata/CoreAssets.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Metadata/CoreAssets.cs b/src/StardewModdingAPI/Metadata/CoreAssets.cs index 3818314d..24f23af7 100644 --- a/src/StardewModdingAPI/Metadata/CoreAssets.cs +++ b/src/StardewModdingAPI/Metadata/CoreAssets.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework.Graphics; @@ -75,6 +75,9 @@ namespace StardewModdingAPI.Metadata ["TileSheets\\weapons"] = (content, key) => Tool.weaponsTexture = content.Load(key), ["TileSheets\\Projectiles"] = (content, key) => Projectile.projectileSheet = content.Load(key), + // from Game1.ResetToolSpriteSheet + ["TileSheets\\tools"] = (content, key) => Game1.ResetToolSpriteSheet(), + // from Bush ["TileSheets\\bushes"] = (content, key) => Bush.texture = content.Load(key), -- cgit From 2ebc0714b1bc395a5688b04936bf25aa54581f0b Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 16 Aug 2017 21:51:03 -0400 Subject: add terminal support on Xfce (#345) Thanks to Patronos! --- release-notes.md | 1 + src/StardewModdingAPI/unix-launcher.sh | 2 ++ 2 files changed, 3 insertions(+) (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index f8980af4..803f7354 100644 --- a/release-notes.md +++ b/release-notes.md @@ -37,6 +37,7 @@ For mod developers: * Fixed `Context.IsPlayerFree` being false if the player is performing an action. * Fixed `GraphicsEvents.Resize` being raised before the game updates its window data. * Fixed `SemanticVersion` not being deserialisable through Json.NET. +* Fixed terminal not launching on Xfce Linux. For SMAPI developers: * Internal changes to support the upcoming SMAPI 2.0 release. diff --git a/src/StardewModdingAPI/unix-launcher.sh b/src/StardewModdingAPI/unix-launcher.sh index 39fd4f29..70f1873a 100644 --- a/src/StardewModdingAPI/unix-launcher.sh +++ b/src/StardewModdingAPI/unix-launcher.sh @@ -64,6 +64,8 @@ else # open SMAPI in terminal if $COMMAND x-terminal-emulator 2>/dev/null; then x-terminal-emulator -e "$LAUNCHER" + elif $COMMAND xfce4-terminal 2>/dev/null; then + xfce4-terminal -e "$LAUNCHER" elif $COMMAND gnome-terminal 2>/dev/null; then gnome-terminal -e "$LAUNCHER" elif $COMMAND xterm 2>/dev/null; then -- cgit From 49ff572932f72374ebee73a3ebae6cc2c24bbeed Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 16 Aug 2017 23:03:14 -0400 Subject: fix build config --- .../StardewModdingAPI.AssemblyRewriters.csproj | 4 ++-- .../StardewModdingAPI.Installer.csproj | 2 +- src/StardewModdingAPI/StardewModdingAPI.csproj | 4 ++-- src/common.targets | 12 +++++++----- 4 files changed, 12 insertions(+), 10 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 95705a48..46dfc34e 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -1,4 +1,4 @@ - + @@ -68,6 +68,6 @@ - + \ No newline at end of file diff --git a/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj b/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj index 58ce519c..4ddd6ebe 100644 --- a/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj +++ b/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj @@ -1,4 +1,4 @@ - + diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 6691b2b5..d7e10ca5 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -1,4 +1,4 @@ - + @@ -259,6 +259,6 @@ StardewModdingAPI.AssemblyRewriters - + \ No newline at end of file diff --git a/src/common.targets b/src/common.targets index 81e7e2a5..ee138524 100644 --- a/src/common.targets +++ b/src/common.targets @@ -1,4 +1,4 @@ - + @@ -77,7 +77,10 @@ - + + + + @@ -86,7 +89,7 @@ - + @@ -101,5 +104,4 @@ - - \ No newline at end of file + -- cgit From 2ec0e0e26a16b94ba4a12d3bb4561d64a7411b34 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 16 Aug 2017 23:27:07 -0400 Subject: only invalidate cache entries matched by new interceptors --- .../Framework/ModHelpers/ContentHelper.cs | 2 +- src/StardewModdingAPI/Framework/SContentManager.cs | 94 ++++++++++++++-------- src/StardewModdingAPI/Program.cs | 26 +++--- 3 files changed, 79 insertions(+), 43 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs index e94d309e..ffa78ff6 100644 --- a/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/StardewModdingAPI/Framework/ModHelpers/ContentHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; diff --git a/src/StardewModdingAPI/Framework/SContentManager.cs b/src/StardewModdingAPI/Framework/SContentManager.cs index 0854c379..9e086870 100644 --- a/src/StardewModdingAPI/Framework/SContentManager.cs +++ b/src/StardewModdingAPI/Framework/SContentManager.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Reflection; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using StardewModdingAPI.AssemblyRewriters; @@ -96,33 +97,6 @@ namespace StardewModdingAPI.Framework } - /// Get the locale codes (like ja-JP) used in asset keys. - /// Simplifies access to private game code. - private IDictionary GetKeyLocales(Reflector reflection) - { - // get the private code field directly to avoid changed-code logic - IPrivateField codeField = reflection.GetPrivateField(typeof(LocalizedContentManager), "_currentLangCode"); - - // remember previous settings - LanguageCode previousCode = codeField.GetValue(); - string previousOverride = this.LanguageCodeOverride; - - // create locale => code map - IDictionary map = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - this.LanguageCodeOverride = null; - foreach (LanguageCode code in Enum.GetValues(typeof(LanguageCode))) - { - codeField.SetValue(code); - map[this.GetKeyLocale.Invoke()] = code; - } - - // restore previous settings - codeField.SetValue(previousCode); - this.LanguageCodeOverride = previousOverride; - - return map; - } - /// Normalise path separators in a file path. For asset keys, see instead. /// The file path to normalise. public string NormalisePathSeparators(string path) @@ -209,10 +183,39 @@ namespace StardewModdingAPI.Framework return GetAllAssetKeys().Distinct(); } - /// Reset the asset cache and reload the game's static assets. + /// Purge assets from the cache that match one of the interceptors. + /// The asset editors for which to purge matching assets. + /// The asset loaders for which to purge matching assets. + /// Returns whether any cache entries were invalidated. + public bool InvalidateCacheFor(IAssetEditor[] editors, IAssetLoader[] loaders) + { + if (!editors.Any() && !loaders.Any()) + return false; + + // get CanEdit/Load methods + MethodInfo canEdit = typeof(IAssetEditor).GetMethod(nameof(IAssetEditor.CanEdit)); + MethodInfo canLoad = typeof(IAssetLoader).GetMethod(nameof(IAssetLoader.CanLoad)); + + // invalidate matching keys + return this.InvalidateCache((assetName, assetType) => + { + // get asset metadata + IAssetInfo info = new AssetInfo(this.GetLocale(), assetName, assetType, this.NormaliseAssetName); + + // check loaders + MethodInfo canLoadGeneric = canLoad.MakeGenericMethod(assetType); + if (loaders.Any(loader => (bool)canLoadGeneric.Invoke(loader, new object[] { info }))) + return true; + + // check editors + MethodInfo canEditGeneric = canEdit.MakeGenericMethod(assetType); + return editors.Any(editor => (bool)canEditGeneric.Invoke(editor, new object[] { info })); + }); + } + + /// Purge matched assets from the cache. /// Matches the asset keys to invalidate. /// Returns whether any cache entries were invalidated. - /// This implementation is derived from . public bool InvalidateCache(Func predicate) { // find matching asset keys @@ -220,7 +223,7 @@ namespace StardewModdingAPI.Framework HashSet purgeAssetKeys = new HashSet(StringComparer.InvariantCultureIgnoreCase); foreach (string cacheKey in this.Cache.Keys) { - this.ParseCacheKey(cacheKey, out string assetKey, out string localeCode); + this.ParseCacheKey(cacheKey, out string assetKey, out _); Type type = this.Cache[cacheKey].GetType(); if (predicate(assetKey, type)) { @@ -237,7 +240,7 @@ namespace StardewModdingAPI.Framework int reloaded = 0; foreach (string key in purgeAssetKeys) { - if(this.CoreAssets.ReloadForKey(this, key)) + if (this.CoreAssets.ReloadForKey(this, key)) reloaded++; } @@ -263,6 +266,33 @@ namespace StardewModdingAPI.Framework || this.Cache.ContainsKey($"{normalisedAssetName}.{this.GetKeyLocale.Invoke()}"); // translated asset } + /// Get the locale codes (like ja-JP) used in asset keys. + /// Simplifies access to private game code. + private IDictionary GetKeyLocales(Reflector reflection) + { + // get the private code field directly to avoid changed-code logic + IPrivateField codeField = reflection.GetPrivateField(typeof(LocalizedContentManager), "_currentLangCode"); + + // remember previous settings + LanguageCode previousCode = codeField.GetValue(); + string previousOverride = this.LanguageCodeOverride; + + // create locale => code map + IDictionary map = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + this.LanguageCodeOverride = null; + foreach (LanguageCode code in Enum.GetValues(typeof(LanguageCode))) + { + codeField.SetValue(code); + map[this.GetKeyLocale.Invoke()] = code; + } + + // restore previous settings + codeField.SetValue(previousCode); + this.LanguageCodeOverride = previousOverride; + + return map; + } + /// Parse a cache key into its component parts. /// The input cache key. /// The original asset key. diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 0e1930ac..79f8e801 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -806,33 +806,39 @@ namespace StardewModdingAPI } } - // reset cache when needed - // only register listeners after Entry to avoid repeatedly reloading assets during load + // invalidate cache entries when needed + // (These listeners are registered after Entry to avoid repeatedly reloading assets as mods initialise.) foreach (IModMetadata metadata in loadedMods) { if (metadata.Mod.Helper.Content is ContentHelper helper) { - // TODO: optimise by only reloading assets the new editors/loaders can intercept helper.ObservableAssetEditors.CollectionChanged += (sender, e) => { if (e.NewItems.Count > 0) { - this.Monitor.Log("Detected new asset editor, resetting cache...", LogLevel.Trace); - this.ContentManager.InvalidateCache((key, type) => true); + this.Monitor.Log("Invalidating cache entries for new asset editors...", LogLevel.Trace); + this.ContentManager.InvalidateCacheFor(e.NewItems.Cast().ToArray(), new IAssetLoader[0]); } }; helper.ObservableAssetLoaders.CollectionChanged += (sender, e) => { if (e.NewItems.Count > 0) { - this.Monitor.Log("Detected new asset loader, resetting cache...", LogLevel.Trace); - this.ContentManager.InvalidateCache((key, type) => true); + this.Monitor.Log("Invalidating cache entries for new asset loaders...", LogLevel.Trace); + this.ContentManager.InvalidateCacheFor(new IAssetEditor[0], e.NewItems.Cast().ToArray()); } }; } } - this.Monitor.Log("Resetting cache to enable interception...", LogLevel.Trace); - this.ContentManager.InvalidateCache((key, type) => true); + + // reset cache now if any editors or loaders were added during entry + IAssetEditor[] editors = loadedMods.SelectMany(p => p.Mod.Helper.Content.AssetEditors).ToArray(); + IAssetLoader[] loaders = loadedMods.SelectMany(p => p.Mod.Helper.Content.AssetLoaders).ToArray(); + if (editors.Any() || loaders.Any()) + { + this.Monitor.Log("Invalidating cached assets for new editors & loaders...", LogLevel.Trace); + this.ContentManager.InvalidateCacheFor(editors, loaders); + } } /// Reload translations for all mods. -- cgit From 9e1d01d4feb4107448840f30fa39e1c827bf5fdc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 17 Aug 2017 12:16:39 -0400 Subject: fix 1.x build --- src/StardewModdingAPI/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 79f8e801..57ff011f 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -832,8 +832,8 @@ namespace StardewModdingAPI } // reset cache now if any editors or loaders were added during entry - IAssetEditor[] editors = loadedMods.SelectMany(p => p.Mod.Helper.Content.AssetEditors).ToArray(); - IAssetLoader[] loaders = loadedMods.SelectMany(p => p.Mod.Helper.Content.AssetLoaders).ToArray(); + IAssetEditor[] editors = loadedMods.SelectMany(p => ((ContentHelper)p.Mod.Helper.Content).AssetEditors).ToArray(); + IAssetLoader[] loaders = loadedMods.SelectMany(p => ((ContentHelper)p.Mod.Helper.Content).AssetLoaders).ToArray(); if (editors.Any() || loaders.Any()) { this.Monitor.Log("Invalidating cached assets for new editors & loaders...", LogLevel.Trace); -- cgit From 723ddc255e1c2b399dfb734306fd00912a741e62 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 17 Aug 2017 17:46:45 -0400 Subject: break loops when loading assets through a mod loader --- src/StardewModdingAPI/Framework/Countdown.cs | 44 --------------- src/StardewModdingAPI/Framework/SContentManager.cs | 22 ++++++-- src/StardewModdingAPI/Framework/SGame.cs | 5 +- .../Framework/Utilities/ContextHash.cs | 62 ++++++++++++++++++++++ .../Framework/Utilities/Countdown.cs | 44 +++++++++++++++ src/StardewModdingAPI/StardewModdingAPI.csproj | 5 +- 6 files changed, 130 insertions(+), 52 deletions(-) delete mode 100644 src/StardewModdingAPI/Framework/Countdown.cs create mode 100644 src/StardewModdingAPI/Framework/Utilities/ContextHash.cs create mode 100644 src/StardewModdingAPI/Framework/Utilities/Countdown.cs (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/Framework/Countdown.cs b/src/StardewModdingAPI/Framework/Countdown.cs deleted file mode 100644 index 25ca2546..00000000 --- a/src/StardewModdingAPI/Framework/Countdown.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace StardewModdingAPI.Framework -{ - /// Counts down from a baseline value. - internal class Countdown - { - /********* - ** Accessors - *********/ - /// The initial value from which to count down. - public int Initial { get; } - - /// The current value. - public int Current { get; private set; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The initial value from which to count down. - public Countdown(int initial) - { - this.Initial = initial; - this.Current = initial; - } - - /// Reduce the current value by one. - /// Returns whether the value was decremented (i.e. wasn't already zero). - public bool Decrement() - { - if (this.Current <= 0) - return false; - - this.Current--; - return true; - } - - /// Restart the countdown. - public void Reset() - { - this.Current = this.Initial; - } - } -} diff --git a/src/StardewModdingAPI/Framework/SContentManager.cs b/src/StardewModdingAPI/Framework/SContentManager.cs index 9e086870..25775291 100644 --- a/src/StardewModdingAPI/Framework/SContentManager.cs +++ b/src/StardewModdingAPI/Framework/SContentManager.cs @@ -9,6 +9,7 @@ using Microsoft.Xna.Framework.Content; using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.Framework.Content; using StardewModdingAPI.Framework.Reflection; +using StardewModdingAPI.Framework.Utilities; using StardewModdingAPI.Metadata; using StardewValley; @@ -44,6 +45,9 @@ namespace StardewModdingAPI.Framework /// Provides metadata for core game assets. private readonly CoreAssets CoreAssets; + /// The assets currently being intercepted by instances. This is used to prevent infinite loops when a loader loads a new asset. + private readonly ContextHash AssetsBeingLoaded = new ContextHash(); + /********* ** Accessors @@ -139,11 +143,21 @@ namespace StardewModdingAPI.Framework // load asset T data; + if (this.AssetsBeingLoaded.Contains(assetName)) { - IAssetInfo info = new AssetInfo(this.GetLocale(), assetName, typeof(T), this.NormaliseAssetName); - IAssetData asset = this.ApplyLoader(info) ?? new AssetDataForObject(info, base.Load(assetName), this.NormaliseAssetName); - asset = this.ApplyEditors(info, asset); - data = (T)asset.Data; + this.Monitor.Log($"Broke loop while loading asset '{assetName}'.", LogLevel.Warn); + this.Monitor.Log($"Bypassing mod loaders for this asset. Stack trace:\n{Environment.StackTrace}", LogLevel.Trace); + data = base.Load(assetName); + } + else + { + data = this.AssetsBeingLoaded.Track(assetName, () => + { + IAssetInfo info = new AssetInfo(this.GetLocale(), assetName, typeof(T), this.NormaliseAssetName); + IAssetData asset = this.ApplyLoader(info) ?? new AssetDataForObject(info, base.Load(assetName), this.NormaliseAssetName); + asset = this.ApplyEditors(info, asset); + return (T)asset.Data; + }); } // update cache & return data diff --git a/src/StardewModdingAPI/Framework/SGame.cs b/src/StardewModdingAPI/Framework/SGame.cs index 755f0274..997e0c8c 100644 --- a/src/StardewModdingAPI/Framework/SGame.cs +++ b/src/StardewModdingAPI/Framework/SGame.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -11,6 +11,7 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using StardewModdingAPI.Events; using StardewModdingAPI.Framework.Reflection; +using StardewModdingAPI.Framework.Utilities; using StardewModdingAPI.Utilities; using StardewValley; using StardewValley.BellsAndWhistles; @@ -322,7 +323,7 @@ namespace StardewModdingAPI.Framework #if !SMAPI_1_x if (Game1.dayOfMonth != 0) // wait until new-game intro finishes (world not fully initialised yet) #endif - this.AfterLoadTimer--; + this.AfterLoadTimer--; if (this.AfterLoadTimer == 0) { diff --git a/src/StardewModdingAPI/Framework/Utilities/ContextHash.cs b/src/StardewModdingAPI/Framework/Utilities/ContextHash.cs new file mode 100644 index 00000000..0d8487bb --- /dev/null +++ b/src/StardewModdingAPI/Framework/Utilities/ContextHash.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace StardewModdingAPI.Framework.Utilities +{ + /// A wrapper meant for tracking recursive contexts. + /// The key type. + internal class ContextHash : HashSet + { + /********* + ** Public methods + *********/ + /// Construct an instance. + public ContextHash() { } + + /// Construct an instance. + /// The implementation to use when comparing values in the set, or null to use the default comparer for the set type. + public ContextHash(IEqualityComparer comparer) + : base(comparer) { } + + /// Add a key while an action is in progress, and remove it when it completes. + /// The key to add. + /// The action to perform. + /// The specified key is already added. + public void Track(T key, Action action) + { + if(this.Contains(key)) + throw new InvalidOperationException($"Can't track context for key {key} because it's already added."); + + this.Add(key); + try + { + action(); + } + finally + { + this.Remove(key); + } + } + + /// Add a key while an action is in progress, and remove it when it completes. + /// The value type returned by the method. + /// The key to add. + /// The action to perform. + public TResult Track(T key, Func action) + { + if (this.Contains(key)) + throw new InvalidOperationException($"Can't track context for key {key} because it's already added."); + + this.Add(key); + try + { + return action(); + } + finally + { + this.Remove(key); + } + } + } +} diff --git a/src/StardewModdingAPI/Framework/Utilities/Countdown.cs b/src/StardewModdingAPI/Framework/Utilities/Countdown.cs new file mode 100644 index 00000000..921a35ce --- /dev/null +++ b/src/StardewModdingAPI/Framework/Utilities/Countdown.cs @@ -0,0 +1,44 @@ +namespace StardewModdingAPI.Framework.Utilities +{ + /// Counts down from a baseline value. + internal class Countdown + { + /********* + ** Accessors + *********/ + /// The initial value from which to count down. + public int Initial { get; } + + /// The current value. + public int Current { get; private set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The initial value from which to count down. + public Countdown(int initial) + { + this.Initial = initial; + this.Current = initial; + } + + /// Reduce the current value by one. + /// Returns whether the value was decremented (i.e. wasn't already zero). + public bool Decrement() + { + if (this.Current <= 0) + return false; + + this.Current--; + return true; + } + + /// Restart the countdown. + public void Reset() + { + this.Current = this.Initial; + } + } +} diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index d7e10ca5..73112983 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -1,4 +1,4 @@ - + @@ -91,6 +91,7 @@ Properties\GlobalAssemblyInfo.cs + @@ -126,7 +127,7 @@ - + -- cgit From 80fe706f19d95ef2d00887344bcbc2a064a04541 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 21 Aug 2017 14:22:19 -0400 Subject: show friendlier error when players have two copies of a mod --- release-notes.md | 1 + .../Exceptions/SAssemblyLoadFailedException.cs | 16 +++++++++++++++ .../Framework/ModLoading/AssemblyLoadStatus.cs | 15 ++++++++++++++ .../Framework/ModLoading/AssemblyLoader.cs | 24 ++++++++++++++++------ .../Framework/ModLoading/AssemblyParseResult.cs | 9 ++++++-- src/StardewModdingAPI/Program.cs | 6 ++++++ src/StardewModdingAPI/StardewModdingAPI.csproj | 2 ++ 7 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 src/StardewModdingAPI/Framework/Exceptions/SAssemblyLoadFailedException.cs create mode 100644 src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs (limited to 'src/StardewModdingAPI') diff --git a/release-notes.md b/release-notes.md index 803f7354..7b7e250c 100644 --- a/release-notes.md +++ b/release-notes.md @@ -6,6 +6,7 @@ For players: * The SMAPI console is now much simpler and easier to read. * The SMAPI console now adjusts its colors when you have a light terminal background. * Updated compatibility list. +* Improved errors when a mod DLL can't be loaded. For mod developers: * Added new APIs to edit, inject, and reload XNB assets loaded by the game at any time. diff --git a/src/StardewModdingAPI/Framework/Exceptions/SAssemblyLoadFailedException.cs b/src/StardewModdingAPI/Framework/Exceptions/SAssemblyLoadFailedException.cs new file mode 100644 index 00000000..ec9279f1 --- /dev/null +++ b/src/StardewModdingAPI/Framework/Exceptions/SAssemblyLoadFailedException.cs @@ -0,0 +1,16 @@ +using System; + +namespace StardewModdingAPI.Framework.Exceptions +{ + /// An exception thrown when an assembly can't be loaded by SMAPI, with all the relevant details in the message. + internal class SAssemblyLoadFailedException : Exception + { + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The error message. + public SAssemblyLoadFailedException(string message) + : base(message) { } + } +} diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs new file mode 100644 index 00000000..11be19fc --- /dev/null +++ b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs @@ -0,0 +1,15 @@ +namespace StardewModdingAPI.Framework.ModLoading +{ + /// Indicates the result of an assembly load. + internal enum AssemblyLoadStatus + { + /// The assembly was loaded successfully. + Okay = 1, + + /// The assembly could not be loaded. + Failed = 2, + + /// The assembly is already loaded. + AlreadyLoaded = 3 + } +} diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs index 406d49e1..b14ae56f 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -6,6 +6,7 @@ using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; using StardewModdingAPI.AssemblyRewriters; +using StardewModdingAPI.Framework.Exceptions; namespace StardewModdingAPI.Framework.ModLoading { @@ -65,16 +66,27 @@ namespace StardewModdingAPI.Framework.ModLoading AssemblyDefinitionResolver resolver = new AssemblyDefinitionResolver(); HashSet visitedAssemblyNames = new HashSet(AppDomain.CurrentDomain.GetAssemblies().Select(p => p.GetName().Name)); // don't try loading assemblies that are already loaded assemblies = this.GetReferencedLocalAssemblies(new FileInfo(assemblyPath), visitedAssemblyNames, resolver).ToArray(); - if (!assemblies.Any()) - throw new InvalidOperationException($"Could not load '{assemblyPath}' because it doesn't exist."); - resolver.Add(assemblies.Select(p => p.Definition).ToArray()); } + // validate load + if (!assemblies.Any() || assemblies[0].Status == AssemblyLoadStatus.Failed) + { + throw new SAssemblyLoadFailedException(!File.Exists(assemblyPath) + ? $"Could not load '{assemblyPath}' because it doesn't exist." + : $"Could not load '{assemblyPath}'." + ); + } + if (assemblies[0].Status == AssemblyLoadStatus.AlreadyLoaded) + throw new SAssemblyLoadFailedException($"Could not load '{assemblyPath}' because it was already loaded. Do you have two copies of this mod?"); + // rewrite & load assemblies in leaf-to-root order bool oneAssembly = assemblies.Length == 1; Assembly lastAssembly = null; foreach (AssemblyParseResult assembly in assemblies) { + if (assembly.Status == AssemblyLoadStatus.AlreadyLoaded) + continue; + bool changed = this.RewriteAssembly(assembly.Definition, assumeCompatible, logPrefix: " "); if (changed) { @@ -143,7 +155,7 @@ namespace StardewModdingAPI.Framework.ModLoading // skip if already visited if (visitedAssemblyNames.Contains(assembly.Name.Name)) - yield break; + yield return new AssemblyParseResult(file, null, AssemblyLoadStatus.AlreadyLoaded); visitedAssemblyNames.Add(assembly.Name.Name); // yield referenced assemblies @@ -155,7 +167,7 @@ namespace StardewModdingAPI.Framework.ModLoading } // yield assembly - yield return new AssemblyParseResult(file, assembly); + yield return new AssemblyParseResult(file, assembly, AssemblyLoadStatus.Okay); } /**** diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs index 69c99afe..b56a776c 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs @@ -15,6 +15,9 @@ namespace StardewModdingAPI.Framework.ModLoading /// The assembly definition. public readonly AssemblyDefinition Definition; + /// The result of the assembly load. + public AssemblyLoadStatus Status; + /********* ** Public methods @@ -22,10 +25,12 @@ namespace StardewModdingAPI.Framework.ModLoading /// Construct an instance. /// The original assembly file. /// The assembly definition. - public AssemblyParseResult(FileInfo file, AssemblyDefinition assembly) + /// The result of the assembly load. + public AssemblyParseResult(FileInfo file, AssemblyDefinition assembly, AssemblyLoadStatus status) { this.File = file; this.Definition = assembly; + this.Status = status; } } -} \ No newline at end of file +} diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 57ff011f..108e9273 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -15,6 +15,7 @@ using Newtonsoft.Json; using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.Events; using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.Logging; using StardewModdingAPI.Framework.Models; using StardewModdingAPI.Framework.ModHelpers; @@ -655,6 +656,11 @@ namespace StardewModdingAPI #endif continue; } + catch (SAssemblyLoadFailedException ex) + { + TrackSkip(metadata, $"its DLL '{manifest.EntryDll}' couldn't be loaded: {ex.Message}"); + continue; + } catch (Exception ex) { TrackSkip(metadata, $"its DLL '{manifest.EntryDll}' couldn't be loaded:\n{ex.GetLogSummary()}"); diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 73112983..8c7279a1 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -91,6 +91,8 @@ Properties\GlobalAssemblyInfo.cs + + -- cgit From e42f06c916f753aefaf385e8463df048cbb8b604 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 21 Aug 2017 14:49:09 -0400 Subject: update for 1.15.2 release --- src/GlobalAssemblyInfo.cs | 6 +++--- .../StardewModdingAPI.AssemblyRewriters.csproj | 2 +- src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj | 2 +- src/StardewModdingAPI/Constants.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/GlobalAssemblyInfo.cs b/src/GlobalAssemblyInfo.cs index d9a01635..d496fbd1 100644 --- a/src/GlobalAssemblyInfo.cs +++ b/src/GlobalAssemblyInfo.cs @@ -1,6 +1,6 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; [assembly: ComVisible(false)] -[assembly: AssemblyVersion("1.15.1.0")] -[assembly: AssemblyFileVersion("1.15.1.0")] +[assembly: AssemblyVersion("1.15.2.0")] +[assembly: AssemblyFileVersion("1.15.2.0")] diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 46dfc34e..8416bd51 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -1,4 +1,4 @@ - + diff --git a/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj b/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj index 4ddd6ebe..58ce519c 100644 --- a/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj +++ b/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj @@ -1,4 +1,4 @@ - + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 57b86b0d..7a790f1a 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -36,7 +36,7 @@ namespace StardewModdingAPI /// SMAPI's current semantic version. public static ISemanticVersion ApiVersion { get; } = #if SMAPI_1_x - new SemanticVersion(1, 15, 2); // alpha-{DateTime.UtcNow:yyyyMMddHHmm} + new SemanticVersion(1, 15, 2); #else new SemanticVersion(2, 0, 0, $"alpha-{DateTime.UtcNow:yyyyMMddHHmm}"); #endif -- cgit