summaryrefslogtreecommitdiff
path: root/src/SMAPI/Utilities
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-05-01 18:16:09 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-05-01 18:16:09 -0400
commitc8ad50dad1d706a1901798f9396f6becfea36c0e (patch)
tree28bd818a5db39ec5ece1bd141a28de955950463b /src/SMAPI/Utilities
parent451b70953ff4c0b1b27ae0de203ad99379b45b2a (diff)
parentf78093bdb58d477b400cde3f19b70ffd6ddf833d (diff)
downloadSMAPI-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.cs11
-rw-r--r--src/SMAPI/Utilities/KeybindList.cs15
-rw-r--r--src/SMAPI/Utilities/PathUtilities.cs14
-rw-r--r--src/SMAPI/Utilities/PerScreen.cs37
-rw-r--r--src/SMAPI/Utilities/SDate.cs55
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>