diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-05-01 18:16:09 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-05-01 18:16:09 -0400 |
commit | c8ad50dad1d706a1901798f9396f6becfea36c0e (patch) | |
tree | 28bd818a5db39ec5ece1bd141a28de955950463b /src/SMAPI/Utilities | |
parent | 451b70953ff4c0b1b27ae0de203ad99379b45b2a (diff) | |
parent | f78093bdb58d477b400cde3f19b70ffd6ddf833d (diff) | |
download | SMAPI-c8ad50dad1d706a1901798f9396f6becfea36c0e.tar.gz SMAPI-c8ad50dad1d706a1901798f9396f6becfea36c0e.tar.bz2 SMAPI-c8ad50dad1d706a1901798f9396f6becfea36c0e.zip |
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI/Utilities')
-rw-r--r-- | src/SMAPI/Utilities/Keybind.cs | 11 | ||||
-rw-r--r-- | src/SMAPI/Utilities/KeybindList.cs | 15 | ||||
-rw-r--r-- | src/SMAPI/Utilities/PathUtilities.cs | 14 | ||||
-rw-r--r-- | src/SMAPI/Utilities/PerScreen.cs | 37 | ||||
-rw-r--r-- | src/SMAPI/Utilities/SDate.cs | 55 |
5 files changed, 80 insertions, 52 deletions
diff --git a/src/SMAPI/Utilities/Keybind.cs b/src/SMAPI/Utilities/Keybind.cs index 87b867a9..3455ce77 100644 --- a/src/SMAPI/Utilities/Keybind.cs +++ b/src/SMAPI/Utilities/Keybind.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using StardewModdingAPI.Framework; @@ -42,13 +43,13 @@ namespace StardewModdingAPI.Utilities /// <param name="input">The keybind string. See remarks on <see cref="ToString"/> for format details.</param> /// <param name="parsed">The parsed keybind, if valid.</param> /// <param name="errors">The parse errors, if any.</param> - public static bool TryParse(string input, out Keybind parsed, out string[] errors) + public static bool TryParse(string input, [NotNullWhen(true)] out Keybind? parsed, out string[] errors) { // empty input if (string.IsNullOrWhiteSpace(input)) { parsed = new Keybind(SButton.None); - errors = new string[0]; + errors = Array.Empty<string>(); return true; } @@ -97,7 +98,7 @@ namespace StardewModdingAPI.Utilities else { parsed = new Keybind(buttons); - errors = new string[0]; + errors = Array.Empty<string>(); return true; } } @@ -118,11 +119,11 @@ namespace StardewModdingAPI.Utilities return SButtonState.None; // mix of held + pressed => pressed - if (states.All(p => p == SButtonState.Pressed || p == SButtonState.Held)) + if (states.All(p => p is SButtonState.Pressed or SButtonState.Held)) return SButtonState.Pressed; // mix of held + released => released - if (states.All(p => p == SButtonState.Held || p == SButtonState.Released)) + if (states.All(p => p is SButtonState.Held or SButtonState.Released)) return SButtonState.Released; // not down last tick or now diff --git a/src/SMAPI/Utilities/KeybindList.cs b/src/SMAPI/Utilities/KeybindList.cs index 28cae240..aa12a37a 100644 --- a/src/SMAPI/Utilities/KeybindList.cs +++ b/src/SMAPI/Utilities/KeybindList.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using StardewModdingAPI.Toolkit.Serialization; @@ -40,7 +41,7 @@ namespace StardewModdingAPI.Utilities /// <exception cref="FormatException">The <paramref name="input"/> format is invalid.</exception> public static KeybindList Parse(string input) { - return KeybindList.TryParse(input, out KeybindList parsed, out string[] errors) + return KeybindList.TryParse(input, out KeybindList? parsed, out string[] errors) ? parsed : throw new SParseException($"Can't parse {nameof(Keybind)} from invalid value '{input}'.\n{string.Join("\n", errors)}"); } @@ -49,13 +50,13 @@ namespace StardewModdingAPI.Utilities /// <param name="input">The keybind string. See remarks on <see cref="ToString"/> for format details.</param> /// <param name="parsed">The parsed keybind list, if valid.</param> /// <param name="errors">The errors that occurred while parsing the input, if any.</param> - public static bool TryParse(string input, out KeybindList parsed, out string[] errors) + public static bool TryParse(string? input, [NotNullWhen(true)] out KeybindList? parsed, out string[] errors) { // empty input if (string.IsNullOrWhiteSpace(input)) { parsed = new KeybindList(); - errors = new string[0]; + errors = Array.Empty<string>(); return true; } @@ -67,7 +68,7 @@ namespace StardewModdingAPI.Utilities if (string.IsNullOrWhiteSpace(rawSet)) continue; - if (!Keybind.TryParse(rawSet, out Keybind keybind, out string[] curErrors)) + if (!Keybind.TryParse(rawSet, out Keybind? keybind, out string[] curErrors)) rawErrors.AddRange(curErrors); else keybinds.Add(keybind); @@ -83,7 +84,7 @@ namespace StardewModdingAPI.Utilities else { parsed = new KeybindList(keybinds.ToArray()); - errors = new string[0]; + errors = Array.Empty<string>(); return true; } } @@ -139,7 +140,7 @@ namespace StardewModdingAPI.Utilities public bool IsDown() { SButtonState state = this.GetState(); - return state == SButtonState.Pressed || state == SButtonState.Held; + return state is SButtonState.Pressed or SButtonState.Held; } /// <summary>Get whether the input binding was just pressed this tick.</summary> @@ -149,7 +150,7 @@ namespace StardewModdingAPI.Utilities } /// <summary>Get the keybind which is currently down, if any. If there are multiple keybinds down, the first one is returned.</summary> - public Keybind GetKeybindCurrentlyDown() + public Keybind? GetKeybindCurrentlyDown() { return this.Keybinds.FirstOrDefault(p => p.GetState().IsDown()); } diff --git a/src/SMAPI/Utilities/PathUtilities.cs b/src/SMAPI/Utilities/PathUtilities.cs index 541b163c..4350f441 100644 --- a/src/SMAPI/Utilities/PathUtilities.cs +++ b/src/SMAPI/Utilities/PathUtilities.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using ToolkitPathUtilities = StardewModdingAPI.Toolkit.Utilities.PathUtilities; @@ -20,14 +21,16 @@ namespace StardewModdingAPI.Utilities /// <param name="path">The path to split.</param> /// <param name="limit">The number of segments to match. Any additional segments will be merged into the last returned part.</param> [Pure] - public static string[] GetSegments(string path, int? limit = null) + public static string[] GetSegments(string? path, int? limit = null) { return ToolkitPathUtilities.GetSegments(path, limit); } /// <summary>Normalize an asset name to match how MonoGame's content APIs would normalize and cache it.</summary> /// <param name="assetName">The asset name to normalize.</param> - public static string NormalizeAssetName(string assetName) + [Pure] + [return: NotNullIfNotNull("assetName")] + public static string? NormalizeAssetName(string? assetName) { return ToolkitPathUtilities.NormalizeAssetName(assetName); } @@ -36,7 +39,8 @@ namespace StardewModdingAPI.Utilities /// <param name="path">The file path to normalize.</param> /// <remarks>This should only be used for file paths. For asset names, use <see cref="NormalizeAssetName"/> instead.</remarks> [Pure] - public static string NormalizePath(string path) + [return: NotNullIfNotNull("path")] + public static string? NormalizePath(string? path) { return ToolkitPathUtilities.NormalizePath(path); } @@ -44,7 +48,7 @@ namespace StardewModdingAPI.Utilities /// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary> /// <param name="path">The path to check.</param> [Pure] - public static bool IsSafeRelativePath(string path) + public static bool IsSafeRelativePath(string? path) { return ToolkitPathUtilities.IsSafeRelativePath(path); } @@ -52,7 +56,7 @@ namespace StardewModdingAPI.Utilities /// <summary>Get whether a string is a valid 'slug', containing only basic characters that are safe in all contexts (e.g. filenames, URLs, etc).</summary> /// <param name="str">The string to check.</param> [Pure] - public static bool IsSlug(string str) + public static bool IsSlug(string? str) { return ToolkitPathUtilities.IsSlug(str); } diff --git a/src/SMAPI/Utilities/PerScreen.cs b/src/SMAPI/Utilities/PerScreen.cs index 20b8fbce..1c4c56fe 100644 --- a/src/SMAPI/Utilities/PerScreen.cs +++ b/src/SMAPI/Utilities/PerScreen.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Deprecations; namespace StardewModdingAPI.Utilities { @@ -37,15 +39,14 @@ namespace StardewModdingAPI.Utilities ** Public methods *********/ /// <summary>Construct an instance.</summary> + /// <remarks><strong>Limitation with nullable reference types:</strong> when the underlying type <typeparamref name="T"/> is nullable, this sets the default value to null regardless of whether you marked the type parameter nullable. To avoid that, set the default value with the 'createNewState' argument instead.</remarks> public PerScreen() - : this(null) { } + : this(null, nullExpected: true) { } /// <summary>Construct an instance.</summary> /// <param name="createNewState">Create the initial state for a screen.</param> public PerScreen(Func<T> createNewState) - { - this.CreateNewState = createNewState ?? (() => default); - } + : this(createNewState, nullExpected: false) { } /// <summary>Get all active values by screen ID. This doesn't initialize the value for a screen ID if it's not created yet.</summary> public IEnumerable<KeyValuePair<int, T>> GetActiveValues() @@ -59,7 +60,7 @@ namespace StardewModdingAPI.Utilities public T GetValueForScreen(int screenId) { this.RemoveDeadScreens(); - return this.States.TryGetValue(screenId, out T state) + return this.States.TryGetValue(screenId, out T? state) ? state : this.States[screenId] = this.CreateNewState(); } @@ -76,13 +77,37 @@ namespace StardewModdingAPI.Utilities /// <summary>Remove all active values.</summary> public void ResetAllScreens() { - this.RemoveScreens(p => true); + this.RemoveScreens(_ => true); } /********* ** Private methods *********/ + /// <summary>Construct an instance.</summary> + /// <param name="createNewState">Create the initial state for a screen.</param> + /// <param name="nullExpected">Whether a null <paramref name="createNewState"/> value is expected.</param> + /// <remarks>This constructor only exists to maintain backwards compatibility. In SMAPI 4.0.0, the overload that passes <c>nullExpected: false</c> should throw an exception instead.</remarks> + private PerScreen(Func<T>? createNewState, bool nullExpected) + { + if (createNewState is null) + { + createNewState = (() => default!); + + if (!nullExpected) + { + SCore.DeprecationManager.Warn( + SCore.DeprecationManager.GetModFromStack(), + $"calling the {nameof(PerScreen<T>)} constructor with null", + "3.14.0", + DeprecationLevel.Notice + ); + } + } + + this.CreateNewState = createNewState; + } + /// <summary>Remove screens which are no longer active.</summary> private void RemoveDeadScreens() { diff --git a/src/SMAPI/Utilities/SDate.cs b/src/SMAPI/Utilities/SDate.cs index cd075dcc..1d4e4489 100644 --- a/src/SMAPI/Utilities/SDate.cs +++ b/src/SMAPI/Utilities/SDate.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Newtonsoft.Json; using StardewModdingAPI.Framework; @@ -22,7 +23,7 @@ namespace StardewModdingAPI.Utilities private readonly int DaysInSeason = 28; /// <summary>The core SMAPI translations.</summary> - internal static Translator Translations; + internal static Translator? Translations; /********* @@ -92,7 +93,8 @@ namespace StardewModdingAPI.Utilities /// <summary>Get a date from a game date instance.</summary> /// <param name="date">The world date.</param> - public static SDate From(WorldDate date) + [return: NotNullIfNotNull("date")] + public static SDate? From(WorldDate? date) { if (date == null) return null; @@ -168,14 +170,14 @@ namespace StardewModdingAPI.Utilities ****/ /// <summary>Get whether this instance is equal to another.</summary> /// <param name="other">The other value to compare.</param> - public bool Equals(SDate other) + public bool Equals(SDate? other) { return this == other; } /// <summary>Get whether this instance is equal to another.</summary> /// <param name="obj">The other value to compare.</param> - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is SDate other && this == other; } @@ -193,7 +195,7 @@ namespace StardewModdingAPI.Utilities /// <param name="date">The base date to compare.</param> /// <param name="other">The other date to compare.</param> /// <returns>The equality of the dates</returns> - public static bool operator ==(SDate date, SDate other) + public static bool operator ==(SDate? date, SDate? other) { return date?.DaysSinceStart == other?.DaysSinceStart; } @@ -201,7 +203,7 @@ namespace StardewModdingAPI.Utilities /// <summary>Get whether one date is not equal to another.</summary> /// <param name="date">The base date to compare.</param> /// <param name="other">The other date to compare.</param> - public static bool operator !=(SDate date, SDate other) + public static bool operator !=(SDate? date, SDate? other) { return date?.DaysSinceStart != other?.DaysSinceStart; } @@ -209,7 +211,7 @@ namespace StardewModdingAPI.Utilities /// <summary>Get whether one date is more than another.</summary> /// <param name="date">The base date to compare.</param> /// <param name="other">The other date to compare.</param> - public static bool operator >(SDate date, SDate other) + public static bool operator >(SDate? date, SDate? other) { return date?.DaysSinceStart > other?.DaysSinceStart; } @@ -217,7 +219,7 @@ namespace StardewModdingAPI.Utilities /// <summary>Get whether one date is more than or equal to another.</summary> /// <param name="date">The base date to compare.</param> /// <param name="other">The other date to compare.</param> - public static bool operator >=(SDate date, SDate other) + public static bool operator >=(SDate? date, SDate? other) { return date?.DaysSinceStart >= other?.DaysSinceStart; } @@ -225,7 +227,7 @@ namespace StardewModdingAPI.Utilities /// <summary>Get whether one date is less than or equal to another.</summary> /// <param name="date">The base date to compare.</param> /// <param name="other">The other date to compare.</param> - public static bool operator <=(SDate date, SDate other) + public static bool operator <=(SDate? date, SDate? other) { return date?.DaysSinceStart <= other?.DaysSinceStart; } @@ -233,7 +235,7 @@ namespace StardewModdingAPI.Utilities /// <summary>Get whether one date is less than another.</summary> /// <param name="date">The base date to compare.</param> /// <param name="other">The other date to compare.</param> - public static bool operator <(SDate date, SDate other) + public static bool operator <(SDate? date, SDate? other) { return date?.DaysSinceStart < other?.DaysSinceStart; } @@ -248,8 +250,11 @@ namespace StardewModdingAPI.Utilities /// <param name="year">The year.</param> /// <param name="allowDayZero">Whether to allow 0 spring Y1 as a valid date.</param> /// <exception cref="ArgumentException">One of the arguments has an invalid value (like day 35).</exception> + [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "The nullability is validated in this constructor.")] private SDate(int day, string season, int year, bool allowDayZero) { + season = season?.Trim().ToLowerInvariant()!; // null-checked below + // validate if (season == null) throw new ArgumentNullException(nameof(season)); @@ -277,32 +282,24 @@ namespace StardewModdingAPI.Utilities /// <param name="year">The year.</param> private bool IsDayZero(int day, string season, int year) { - return day == 0 && season == "spring" && year == 1; + return day == 0 && season?.Trim().ToLower() == "spring" && year == 1; } /// <summary>Get the day of week for a given date.</summary> /// <param name="day">The day of month.</param> private DayOfWeek GetDayOfWeek(int day) { - switch (day % 7) + return (day % 7) switch { - 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; - } + 0 => DayOfWeek.Sunday, + 1 => DayOfWeek.Monday, + 2 => DayOfWeek.Tuesday, + 3 => DayOfWeek.Wednesday, + 4 => DayOfWeek.Thursday, + 5 => DayOfWeek.Friday, + 6 => DayOfWeek.Saturday, + _ => 0 + }; } /// <summary>Get the number of days since the game began (starting at 1 for the first day of spring in Y1).</summary> |