summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-04-13 20:24:14 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-04-13 20:24:14 -0400
commitf39da383a17b368e92fd243cf155b27ba42671f3 (patch)
tree56c215dfb34da270a7714afd141e76a94c69a2c0 /src/SMAPI/Framework
parent6e9e8aef1ef97e1a4ef4410ce300cb1c47eca986 (diff)
downloadSMAPI-f39da383a17b368e92fd243cf155b27ba42671f3.tar.gz
SMAPI-f39da383a17b368e92fd243cf155b27ba42671f3.tar.bz2
SMAPI-f39da383a17b368e92fd243cf155b27ba42671f3.zip
enable nullable annotations in SMAPI where no logic changes are needed (#837)
Diffstat (limited to 'src/SMAPI/Framework')
-rw-r--r--src/SMAPI/Framework/Command.cs6
-rw-r--r--src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs20
-rw-r--r--src/SMAPI/Framework/Content/AssetData.cs7
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForDictionary.cs4
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForImage.cs4
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForObject.cs4
-rw-r--r--src/SMAPI/Framework/Content/AssetEditOperation.cs6
-rw-r--r--src/SMAPI/Framework/Content/AssetInfo.cs12
-rw-r--r--src/SMAPI/Framework/Content/AssetInterceptorChange.cs7
-rw-r--r--src/SMAPI/Framework/Content/AssetLoadOperation.cs6
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs35
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManager.cs34
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs4
-rw-r--r--src/SMAPI/Framework/ContentManagers/IContentManager.cs13
-rw-r--r--src/SMAPI/Framework/ContentPack.cs9
-rw-r--r--src/SMAPI/Framework/CursorPosition.cs4
-rw-r--r--src/SMAPI/Framework/Events/ManagedEvent.cs8
-rw-r--r--src/SMAPI/Framework/Events/ManagedEventHandler.cs4
-rw-r--r--src/SMAPI/Framework/Exceptions/SContentLoadException.cs4
-rw-r--r--src/SMAPI/Framework/GameVersion.cs10
-rw-r--r--src/SMAPI/Framework/IModMetadata.cs26
-rw-r--r--src/SMAPI/Framework/Logging/LogFileManager.cs4
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentHelper.cs9
-rw-r--r--src/SMAPI/Framework/ModHelpers/DataHelper.cs28
-rw-r--r--src/SMAPI/Framework/ModHelpers/GameContentHelper.cs8
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModContentHelper.cs6
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs13
-rw-r--r--src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs8
-rw-r--r--src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs26
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs8
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs4
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs4
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs4
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs4
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs15
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs13
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs6
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs8
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs4
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs25
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs10
-rw-r--r--src/SMAPI/Framework/ModLoading/InvalidModStateException.cs4
-rw-r--r--src/SMAPI/Framework/ModLoading/ModMetadata.cs38
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs6
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs9
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs6
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/HarmonyRewriter.cs16
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs19
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs15
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs13
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs6
-rw-r--r--src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs6
-rw-r--r--src/SMAPI/Framework/ModLoading/TypeReferenceComparer.cs14
-rw-r--r--src/SMAPI/Framework/ModRegistry.cs16
-rw-r--r--src/SMAPI/Framework/Reflection/ReflectedField.cs16
-rw-r--r--src/SMAPI/Framework/Reflection/ReflectedMethod.cs18
-rw-r--r--src/SMAPI/Framework/Reflection/ReflectedProperty.cs18
-rw-r--r--src/SMAPI/Framework/Rendering/SDisplayDevice.cs8
-rw-r--r--src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs4
-rw-r--r--src/SMAPI/Framework/SGame.cs27
-rw-r--r--src/SMAPI/Framework/SModHooks.cs4
-rw-r--r--src/SMAPI/Framework/SnapshotDiff.cs6
-rw-r--r--src/SMAPI/Framework/SnapshotItemListDiff.cs5
-rw-r--r--src/SMAPI/Framework/SnapshotListDiff.cs4
-rw-r--r--src/SMAPI/Framework/StateTracking/ChestTracker.cs5
-rw-r--r--src/SMAPI/Framework/StateTracking/Comparers/EquatableComparer.cs4
-rw-r--r--src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs4
-rw-r--r--src/SMAPI/Framework/StateTracking/Comparers/ObjectReferenceComparer.cs4
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs3
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs8
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs15
-rw-r--r--src/SMAPI/Framework/StateTracking/LocationTracker.cs14
-rw-r--r--src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs4
-rw-r--r--src/SMAPI/Framework/StateTracking/Snapshots/WatcherSnapshot.cs6
-rw-r--r--src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs4
-rw-r--r--src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs23
-rw-r--r--src/SMAPI/Framework/Translator.cs20
-rw-r--r--src/SMAPI/Framework/Utilities/TickCacheDictionary.cs5
-rw-r--r--src/SMAPI/Framework/WatcherCore.cs4
79 files changed, 369 insertions, 466 deletions
diff --git a/src/SMAPI/Framework/Command.cs b/src/SMAPI/Framework/Command.cs
index 776ba238..dca1dd09 100644
--- a/src/SMAPI/Framework/Command.cs
+++ b/src/SMAPI/Framework/Command.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
namespace StardewModdingAPI.Framework
@@ -11,7 +9,7 @@ namespace StardewModdingAPI.Framework
** Accessor
*********/
/// <summary>The mod that registered the command (or <c>null</c> if registered by SMAPI).</summary>
- public IModMetadata Mod { get; }
+ public IModMetadata? Mod { get; }
/// <summary>The command name, which the user must type to trigger it.</summary>
public string Name { get; }
@@ -31,7 +29,7 @@ namespace StardewModdingAPI.Framework
/// <param name="name">The command name, which the user must type to trigger it.</param>
/// <param name="documentation">The human-readable documentation shown when the player runs the built-in 'help' command.</param>
/// <param name="callback">The method to invoke when the command is triggered. This method is passed the command name and arguments submitted by the user.</param>
- public Command(IModMetadata mod, string name, string documentation, Action<string, string[]> callback)
+ public Command(IModMetadata? mod, string name, string documentation, Action<string, string[]> callback)
{
this.Mod = mod;
this.Name = name;
diff --git a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs
index fcfa928e..6dc6f131 100644
--- a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs
+++ b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Linq;
@@ -73,9 +71,9 @@ namespace StardewModdingAPI.Framework.Commands
private IEnumerable<SearchResult> FilterPatches(string[] searchTerms)
{
bool hasSearch = searchTerms.Any();
- bool IsMatch(string target) => !hasSearch || searchTerms.Any(search => target != null && target.IndexOf(search, StringComparison.OrdinalIgnoreCase) > -1);
+ bool IsMatch(string? target) => !hasSearch || searchTerms.Any(search => target != null && target.IndexOf(search, StringComparison.OrdinalIgnoreCase) > -1);
- foreach (var patch in this.GetAllPatches())
+ foreach (SearchResult patch in this.GetAllPatches())
{
// matches entire patch
if (IsMatch(patch.MethodDescription))
@@ -85,10 +83,10 @@ namespace StardewModdingAPI.Framework.Commands
}
// matches individual patchers
- foreach (var pair in patch.PatchTypesByOwner.ToArray())
+ foreach ((string patcherId, ISet<PatchType> patchTypes) in patch.PatchTypesByOwner.ToArray())
{
- if (!IsMatch(pair.Key) && !pair.Value.Any(type => IsMatch(type.ToString())))
- patch.PatchTypesByOwner.Remove(pair.Key);
+ if (!IsMatch(patcherId) && !patchTypes.Any(type => IsMatch(type.ToString())))
+ patch.PatchTypesByOwner.Remove(patcherId);
}
if (patch.PatchTypesByOwner.Any())
@@ -114,13 +112,13 @@ namespace StardewModdingAPI.Framework.Commands
// get patch types by owner
var typesByOwner = new Dictionary<string, ISet<PatchType>>();
- foreach (var group in patchGroups)
+ foreach ((PatchType type, IReadOnlyCollection<Patch> patches) in patchGroups)
{
- foreach (var patch in group.Value)
+ foreach (Patch patch in patches)
{
- if (!typesByOwner.TryGetValue(patch.owner, out ISet<PatchType> patchTypes))
+ if (!typesByOwner.TryGetValue(patch.owner, out ISet<PatchType>? patchTypes))
typesByOwner[patch.owner] = patchTypes = new HashSet<PatchType>();
- patchTypes.Add(group.Key);
+ patchTypes.Add(type);
}
}
diff --git a/src/SMAPI/Framework/Content/AssetData.cs b/src/SMAPI/Framework/Content/AssetData.cs
index be4a7ce6..0367e999 100644
--- a/src/SMAPI/Framework/Content/AssetData.cs
+++ b/src/SMAPI/Framework/Content/AssetData.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
namespace StardewModdingAPI.Framework.Content
@@ -7,12 +5,13 @@ namespace StardewModdingAPI.Framework.Content
/// <summary>Base implementation for a content helper which encapsulates access and changes to content being read from a data file.</summary>
/// <typeparam name="TValue">The interface value type.</typeparam>
internal class AssetData<TValue> : AssetInfo, IAssetData<TValue>
+ where TValue : notnull
{
/*********
** Fields
*********/
/// <summary>A callback to invoke when the data is replaced (if any).</summary>
- private readonly Action<TValue> OnDataReplaced;
+ private readonly Action<TValue>? OnDataReplaced;
/*********
@@ -31,7 +30,7 @@ namespace StardewModdingAPI.Framework.Content
/// <param name="data">The content data being read.</param>
/// <param name="getNormalizedPath">Normalizes an asset key to match the cache key.</param>
/// <param name="onDataReplaced">A callback to invoke when the data is replaced (if any).</param>
- public AssetData(string locale, IAssetName assetName, TValue data, Func<string, string> getNormalizedPath, Action<TValue> onDataReplaced)
+ public AssetData(string? locale, IAssetName assetName, TValue data, Func<string, string> getNormalizedPath, Action<TValue>? onDataReplaced)
: base(locale, assetName, data.GetType(), getNormalizedPath)
{
this.Data = data;
diff --git a/src/SMAPI/Framework/Content/AssetDataForDictionary.cs b/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
index 06dbe259..d9bfa7bf 100644
--- a/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
@@ -17,7 +15,7 @@ namespace StardewModdingAPI.Framework.Content
/// <param name="data">The content data being read.</param>
/// <param name="getNormalizedPath">Normalizes an asset key to match the cache key.</param>
/// <param name="onDataReplaced">A callback to invoke when the data is replaced (if any).</param>
- public AssetDataForDictionary(string locale, IAssetName assetName, IDictionary<TKey, TValue> data, Func<string, string> getNormalizedPath, Action<IDictionary<TKey, TValue>> onDataReplaced)
+ public AssetDataForDictionary(string? locale, IAssetName assetName, IDictionary<TKey, TValue> data, Func<string, string> getNormalizedPath, Action<IDictionary<TKey, TValue>> onDataReplaced)
: base(locale, assetName, data, getNormalizedPath, onDataReplaced) { }
}
}
diff --git a/src/SMAPI/Framework/Content/AssetDataForImage.cs b/src/SMAPI/Framework/Content/AssetDataForImage.cs
index 8e59cd27..97729c95 100644
--- a/src/SMAPI/Framework/Content/AssetDataForImage.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForImage.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -27,7 +25,7 @@ namespace StardewModdingAPI.Framework.Content
/// <param name="data">The content data being read.</param>
/// <param name="getNormalizedPath">Normalizes an asset key to match the cache key.</param>
/// <param name="onDataReplaced">A callback to invoke when the data is replaced (if any).</param>
- public AssetDataForImage(string locale, IAssetName assetName, Texture2D data, Func<string, string> getNormalizedPath, Action<Texture2D> onDataReplaced)
+ public AssetDataForImage(string? locale, IAssetName assetName, Texture2D data, Func<string, string> getNormalizedPath, Action<Texture2D> onDataReplaced)
: base(locale, assetName, data, getNormalizedPath, onDataReplaced) { }
/// <inheritdoc />
diff --git a/src/SMAPI/Framework/Content/AssetDataForObject.cs b/src/SMAPI/Framework/Content/AssetDataForObject.cs
index bb3966b9..e508ca30 100644
--- a/src/SMAPI/Framework/Content/AssetDataForObject.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForObject.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
@@ -27,7 +25,7 @@ namespace StardewModdingAPI.Framework.Content
/// <param name="data">The content data being read.</param>
/// <param name="getNormalizedPath">Normalizes an asset key to match the cache key.</param>
/// <param name="reflection">Simplifies access to private code.</param>
- public AssetDataForObject(string locale, IAssetName assetName, object data, Func<string, string> getNormalizedPath, Reflector reflection)
+ public AssetDataForObject(string? locale, IAssetName assetName, object data, Func<string, string> getNormalizedPath, Reflector reflection)
: base(locale, assetName, data, getNormalizedPath, onDataReplaced: null)
{
this.Reflection = reflection;
diff --git a/src/SMAPI/Framework/Content/AssetEditOperation.cs b/src/SMAPI/Framework/Content/AssetEditOperation.cs
index 1b7d0c93..464948b0 100644
--- a/src/SMAPI/Framework/Content/AssetEditOperation.cs
+++ b/src/SMAPI/Framework/Content/AssetEditOperation.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using StardewModdingAPI.Events;
@@ -18,7 +16,7 @@ namespace StardewModdingAPI.Framework.Content
public AssetEditPriority Priority { get; }
/// <summary>The content pack on whose behalf the edit is being applied, if any.</summary>
- public IModMetadata OnBehalfOf { get; }
+ public IModMetadata? OnBehalfOf { get; }
/// <summary>Apply the edit to an asset.</summary>
public Action<IAssetData> ApplyEdit { get; }
@@ -32,7 +30,7 @@ namespace StardewModdingAPI.Framework.Content
/// <param name="priority">If there are multiple edits that apply to the same asset, the priority with which this one should be applied.</param>
/// <param name="onBehalfOf">The content pack on whose behalf the edit is being applied, if any.</param>
/// <param name="applyEdit">Apply the edit to an asset.</param>
- public AssetEditOperation(IModMetadata mod, AssetEditPriority priority, IModMetadata onBehalfOf, Action<IAssetData> applyEdit)
+ public AssetEditOperation(IModMetadata mod, AssetEditPriority priority, IModMetadata? onBehalfOf, Action<IAssetData> applyEdit)
{
this.Mod = mod;
this.Priority = priority;
diff --git a/src/SMAPI/Framework/Content/AssetInfo.cs b/src/SMAPI/Framework/Content/AssetInfo.cs
index 51dcc61f..0f0e9bf3 100644
--- a/src/SMAPI/Framework/Content/AssetInfo.cs
+++ b/src/SMAPI/Framework/Content/AssetInfo.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
@@ -19,7 +17,7 @@ namespace StardewModdingAPI.Framework.Content
** Accessors
*********/
/// <inheritdoc />
- public string Locale { get; }
+ public string? Locale { get; }
/// <inheritdoc />
public IAssetName Name { get; }
@@ -28,7 +26,7 @@ namespace StardewModdingAPI.Framework.Content
public IAssetName NameWithoutLocale { get; }
/// <inheritdoc />
- [Obsolete($"Use {nameof(Name)} or {nameof(NameWithoutLocale)} instead. This property will be removed in SMAPI 4.0.0.")]
+ [Obsolete($"Use {nameof(AssetInfo.Name)} or {nameof(AssetInfo.NameWithoutLocale)} instead. This property will be removed in SMAPI 4.0.0.")]
public string AssetName
{
get
@@ -56,7 +54,7 @@ namespace StardewModdingAPI.Framework.Content
/// <param name="assetName">The asset name being read.</param>
/// <param name="type">The content type being read.</param>
/// <param name="getNormalizedPath">Normalizes an asset key to match the cache key.</param>
- public AssetInfo(string locale, IAssetName assetName, Type type, Func<string, string> getNormalizedPath)
+ public AssetInfo(string? locale, IAssetName assetName, Type type, Func<string, string> getNormalizedPath)
{
this.Locale = locale;
this.Name = assetName;
@@ -66,7 +64,7 @@ namespace StardewModdingAPI.Framework.Content
}
/// <inheritdoc />
- [Obsolete($"Use {nameof(Name)}.{nameof(IAssetName.IsEquivalentTo)} or {nameof(NameWithoutLocale)}.{nameof(IAssetName.IsEquivalentTo)} instead. This method will be removed in SMAPI 4.0.0.")]
+ [Obsolete($"Use {nameof(Name)}.{nameof(IAssetName.IsEquivalentTo)} or {nameof(AssetInfo.NameWithoutLocale)}.{nameof(IAssetName.IsEquivalentTo)} instead. This method will be removed in SMAPI 4.0.0.")]
public bool AssetNameEquals(string path)
{
SCore.DeprecationManager.Warn(
@@ -106,7 +104,7 @@ namespace StardewModdingAPI.Framework.Content
return "string";
// default
- return type.FullName;
+ return type.FullName!;
}
}
}
diff --git a/src/SMAPI/Framework/Content/AssetInterceptorChange.cs b/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
index 7f53db9b..fc8199e8 100644
--- a/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
+++ b/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
@@ -1,9 +1,8 @@
-#nullable disable
-
using System;
using System.Reflection;
using StardewModdingAPI.Internal;
+#pragma warning disable CS0618 // obsolete asset interceptors deliberately supported here
namespace StardewModdingAPI.Framework.Content
{
/// <summary>A wrapper for <see cref="IAssetEditor"/> and <see cref="IAssetLoader"/> for internal cache invalidation.</summary>
@@ -46,11 +45,11 @@ namespace StardewModdingAPI.Framework.Content
/// <param name="asset">Basic metadata about the asset being loaded.</param>
public bool CanIntercept(IAssetInfo asset)
{
- MethodInfo canIntercept = this.GetType().GetMethod(nameof(this.CanInterceptImpl), BindingFlags.Instance | BindingFlags.NonPublic);
+ MethodInfo? canIntercept = this.GetType().GetMethod(nameof(this.CanInterceptImpl), BindingFlags.Instance | BindingFlags.NonPublic);
if (canIntercept == null)
throw new InvalidOperationException($"SMAPI couldn't access the {nameof(AssetInterceptorChange)}.{nameof(this.CanInterceptImpl)} implementation.");
- return (bool)canIntercept.MakeGenericMethod(asset.DataType).Invoke(this, new object[] { asset });
+ return (bool)canIntercept.MakeGenericMethod(asset.DataType).Invoke(this, new object[] { asset })!;
}
diff --git a/src/SMAPI/Framework/Content/AssetLoadOperation.cs b/src/SMAPI/Framework/Content/AssetLoadOperation.cs
index 73e60e24..b6cdec27 100644
--- a/src/SMAPI/Framework/Content/AssetLoadOperation.cs
+++ b/src/SMAPI/Framework/Content/AssetLoadOperation.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using StardewModdingAPI.Events;
@@ -15,7 +13,7 @@ namespace StardewModdingAPI.Framework.Content
public IModMetadata Mod { get; }
/// <summary>The content pack on whose behalf the asset is being loaded, if any.</summary>
- public IModMetadata OnBehalfOf { get; }
+ public IModMetadata? OnBehalfOf { get; }
/// <summary>If there are multiple loads that apply to the same asset, the priority with which this one should be applied.</summary>
public AssetLoadPriority Priority { get; }
@@ -32,7 +30,7 @@ namespace StardewModdingAPI.Framework.Content
/// <param name="priority">If there are multiple loads that apply to the same asset, the priority with which this one should be applied.</param>
/// <param name="onBehalfOf">The content pack on whose behalf the asset is being loaded, if any.</param>
/// <param name="getData">Load the initial value for an asset.</param>
- public AssetLoadOperation(IModMetadata mod, AssetLoadPriority priority, IModMetadata onBehalfOf, Func<IAssetInfo, object> getData)
+ public AssetLoadOperation(IModMetadata mod, AssetLoadPriority priority, IModMetadata? onBehalfOf, Func<IAssetInfo, object> getData)
{
this.Mod = mod;
this.Priority = priority;
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs
index 4e48b08c..92452224 100644
--- a/src/SMAPI/Framework/ContentCoordinator.cs
+++ b/src/SMAPI/Framework/ContentCoordinator.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -71,7 +69,7 @@ namespace StardewModdingAPI.Framework
private readonly ReaderWriterLockSlim ContentManagerLock = new();
/// <summary>A cache of ordered tilesheet IDs used by vanilla maps.</summary>
- private readonly Dictionary<string, TilesheetReference[]> VanillaTilesheets = new(StringComparer.OrdinalIgnoreCase);
+ private readonly Dictionary<string, TilesheetReference[]?> VanillaTilesheets = new(StringComparer.OrdinalIgnoreCase);
/// <summary>An unmodified content manager which doesn't intercept assets, used to compare asset data.</summary>
private readonly LocalizedContentManager VanillaContentManager;
@@ -230,7 +228,7 @@ namespace StardewModdingAPI.Framework
public void OnAdditionalLanguagesInitialized()
{
// update locale cache for custom languages, and load it now (since languages added later won't work)
- var customLanguages = this.MainContentManager.Load<List<ModLanguage>>("Data/AdditionalLanguages");
+ var customLanguages = this.MainContentManager.Load<List<ModLanguage?>>("Data/AdditionalLanguages");
this.LocaleCodes = new Lazy<Dictionary<string, LocalizedContentManager.LanguageCode>>(() => this.GetLocaleCodes(customLanguages));
_ = this.LocaleCodes.Value;
}
@@ -303,7 +301,7 @@ namespace StardewModdingAPI.Framework
/// <param name="contentManagerID">The unique name for the content manager which should load this asset.</param>
/// <param name="relativePath">The asset name within the mod folder.</param>
/// <returns>Returns whether the asset was parsed successfully.</returns>
- public bool TryParseManagedAssetKey(string key, out string contentManagerID, out IAssetName relativePath)
+ public bool TryParseManagedAssetKey(string key, [NotNullWhen(true)] out string? contentManagerID, [NotNullWhen(true)] out IAssetName? relativePath)
{
contentManagerID = null;
relativePath = null;
@@ -333,9 +331,10 @@ namespace StardewModdingAPI.Framework
/// <param name="contentManagerID">The unique name for the content manager which should load this asset.</param>
/// <param name="assetName">The asset name within the mod folder.</param>
public bool DoesManagedAssetExist<T>(string contentManagerID, IAssetName assetName)
+ where T : notnull
{
// get content manager
- IContentManager contentManager = this.ContentManagerLock.InReadLock(() =>
+ IContentManager? contentManager = this.ContentManagerLock.InReadLock(() =>
this.ContentManagers.FirstOrDefault(p => p.IsNamespaced && p.Name == contentManagerID)
);
if (contentManager == null)
@@ -350,9 +349,10 @@ namespace StardewModdingAPI.Framework
/// <param name="contentManagerID">The unique name for the content manager which should load this asset.</param>
/// <param name="relativePath">The asset name within the mod folder.</param>
public T LoadManagedAsset<T>(string contentManagerID, IAssetName relativePath)
+ where T : notnull
{
// get content manager
- IContentManager contentManager = this.ContentManagerLock.InReadLock(() =>
+ IContentManager? contentManager = this.ContentManagerLock.InReadLock(() =>
this.ContentManagers.FirstOrDefault(p => p.IsNamespaced && p.Name == contentManagerID)
);
if (contentManager == null)
@@ -461,6 +461,7 @@ namespace StardewModdingAPI.Framework
/// <typeparam name="T">The asset type.</typeparam>
/// <param name="info">The asset info to load or edit.</param>
public IEnumerable<AssetOperationGroup> GetAssetOperations<T>(IAssetInfo info)
+ where T : notnull
{
return this.AssetOperationsByKey.GetOrSet(
info.Name,
@@ -491,7 +492,7 @@ namespace StardewModdingAPI.Framework
{
rootPath = PathUtilities.NormalizePath(rootPath);
- if (!this.CaseInsensitivePathCaches.TryGetValue(rootPath, out CaseInsensitivePathCache cache))
+ if (!this.CaseInsensitivePathCaches.TryGetValue(rootPath, out CaseInsensitivePathCache? cache))
this.CaseInsensitivePathCaches[rootPath] = cache = new CaseInsensitivePathCache(rootPath);
return cache;
@@ -501,9 +502,9 @@ namespace StardewModdingAPI.Framework
/// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
public TilesheetReference[] GetVanillaTilesheetIds(string assetName)
{
- if (!this.VanillaTilesheets.TryGetValue(assetName, out TilesheetReference[] tilesheets))
+ if (!this.VanillaTilesheets.TryGetValue(assetName, out TilesheetReference[]? tilesheets))
{
- tilesheets = this.TryLoadVanillaAsset(assetName, out Map map)
+ tilesheets = this.TryLoadVanillaAsset(assetName, out Map? map)
? map.TileSheets.Select((sheet, index) => new TilesheetReference(index, sheet.Id, sheet.ImageSource, sheet.SheetSize, sheet.TileSize)).ToArray()
: null;
@@ -516,7 +517,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Get the locale code which corresponds to a language enum (e.g. <c>fr-FR</c> given <see cref="LocalizedContentManager.LanguageCode.fr"/>).</summary>
/// <param name="language">The language enum to search.</param>
- public string GetLocaleCode(LocalizedContentManager.LanguageCode language)
+ public string? GetLocaleCode(LocalizedContentManager.LanguageCode language)
{
if (language == LocalizedContentManager.LanguageCode.mod && LocalizedContentManager.CurrentModLanguage == null)
return null;
@@ -535,7 +536,7 @@ namespace StardewModdingAPI.Framework
foreach (IContentManager contentManager in this.ContentManagers)
contentManager.Dispose();
this.ContentManagers.Clear();
- this.MainContentManager = null;
+ this.MainContentManager = null!; // instance no longer usable
this.ContentManagerLock.Dispose();
}
@@ -560,7 +561,8 @@ namespace StardewModdingAPI.Framework
/// <typeparam name="T">The type of asset to load.</typeparam>
/// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
/// <param name="asset">The loaded asset data.</param>
- private bool TryLoadVanillaAsset<T>(string assetName, out T asset)
+ private bool TryLoadVanillaAsset<T>(string assetName, [NotNullWhen(true)] out T? asset)
+ where T : notnull
{
try
{
@@ -576,12 +578,12 @@ namespace StardewModdingAPI.Framework
/// <summary>Get the language enums (like <see cref="LocalizedContentManager.LanguageCode.ja"/>) indexed by locale code (like <c>ja-JP</c>).</summary>
/// <param name="customLanguages">The custom languages to add to the lookup.</param>
- private Dictionary<string, LocalizedContentManager.LanguageCode> GetLocaleCodes(IEnumerable<ModLanguage> customLanguages)
+ private Dictionary<string, LocalizedContentManager.LanguageCode> GetLocaleCodes(IEnumerable<ModLanguage?> customLanguages)
{
var map = new Dictionary<string, LocalizedContentManager.LanguageCode>(StringComparer.OrdinalIgnoreCase);
// custom languages
- foreach (ModLanguage language in customLanguages)
+ foreach (ModLanguage? language in customLanguages)
{
if (!string.IsNullOrWhiteSpace(language?.LanguageCode))
map[language.LanguageCode] = LocalizedContentManager.LanguageCode.mod;
@@ -590,7 +592,7 @@ namespace StardewModdingAPI.Framework
// vanilla languages (override custom language if they conflict)
foreach (LocalizedContentManager.LanguageCode code in Enum.GetValues(typeof(LocalizedContentManager.LanguageCode)))
{
- string locale = this.GetLocaleCode(code);
+ string? locale = this.GetLocaleCode(code);
if (locale != null)
map[locale] = code;
}
@@ -602,6 +604,7 @@ namespace StardewModdingAPI.Framework
/// <typeparam name="T">The asset type.</typeparam>
/// <param name="info">The asset info to load or edit.</param>
private IEnumerable<AssetOperationGroup> GetAssetOperationsWithoutCache<T>(IAssetInfo info)
+ where T : notnull
{
IAssetInfo legacyInfo = this.GetLegacyAssetInfo(info);
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
index e494559d..6469fea4 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
@@ -1,7 +1,6 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -70,7 +69,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
return true;
// managed asset
- if (this.Coordinator.TryParseManagedAssetKey(assetName.Name, out string contentManagerID, out IAssetName relativePath))
+ if (this.Coordinator.TryParseManagedAssetKey(assetName.Name, out string? contentManagerID, out IAssetName? relativePath))
return this.Coordinator.DoesManagedAssetExist<T>(contentManagerID, relativePath);
// custom asset from a loader
@@ -78,7 +77,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
IAssetInfo info = new AssetInfo(locale, assetName, typeof(T), this.AssertAndNormalizeAssetName);
AssetLoadOperation[] loaders = this.GetLoaders<object>(info).ToArray();
- if (!this.AssertMaxOneRequiredLoader(info, loaders, out string error))
+ if (!this.AssertMaxOneRequiredLoader(info, loaders, out string? error))
{
this.Monitor.Log(error, LogLevel.Warn);
return false;
@@ -102,7 +101,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
return this.RawLoad<T>(assetName, useCache: true);
// get managed asset
- if (this.Coordinator.TryParseManagedAssetKey(assetName.Name, out string contentManagerID, out IAssetName relativePath))
+ if (this.Coordinator.TryParseManagedAssetKey(assetName.Name, out string? contentManagerID, out IAssetName? relativePath))
{
T managedAsset = this.Coordinator.LoadManagedAsset<T>(contentManagerID, relativePath);
this.TrackAsset(assetName, managedAsset, useCache);
@@ -151,14 +150,15 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <summary>Load the initial asset from the registered loaders.</summary>
/// <param name="info">The basic asset metadata.</param>
/// <returns>Returns the loaded asset metadata, or <c>null</c> if no loader matched.</returns>
- private IAssetData ApplyLoader<T>(IAssetInfo info)
+ private IAssetData? ApplyLoader<T>(IAssetInfo info)
+ where T : notnull
{
// find matching loader
- AssetLoadOperation loader;
+ AssetLoadOperation? loader;
{
AssetLoadOperation[] loaders = this.GetLoaders<T>(info).OrderByDescending(p => p.Priority).ToArray();
- if (!this.AssertMaxOneRequiredLoader(info, loaders, out string error))
+ if (!this.AssertMaxOneRequiredLoader(info, loaders, out string? error))
{
this.Monitor.Log(error, LogLevel.Warn);
return null;
@@ -196,20 +196,21 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="info">The basic asset metadata.</param>
/// <param name="asset">The loaded asset.</param>
private IAssetData ApplyEditors<T>(IAssetInfo info, IAssetData asset)
+ where T : notnull
{
IAssetData GetNewData(object data) => new AssetDataForObject(info, data, this.AssertAndNormalizeAssetName, this.Reflection);
// special case: if the asset was loaded with a more general type like 'object', call editors with the actual type instead.
{
Type actualType = asset.Data.GetType();
- Type actualOpenType = actualType.IsGenericType ? actualType.GetGenericTypeDefinition() : null;
+ Type? actualOpenType = actualType.IsGenericType ? actualType.GetGenericTypeDefinition() : null;
if (typeof(T) != actualType && (actualOpenType == typeof(Dictionary<,>) || actualOpenType == typeof(List<>) || actualType == typeof(Texture2D) || actualType == typeof(Map)))
{
return (IAssetData)this.GetType()
.GetMethod(nameof(this.ApplyEditors), BindingFlags.NonPublic | BindingFlags.Instance)!
.MakeGenericMethod(actualType)
- .Invoke(this, new object[] { info, asset });
+ .Invoke(this, new object[] { info, asset })!;
}
}
@@ -232,6 +233,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
}
// validate edit
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalse -- it's only guaranteed non-null after this method
if (asset.Data == null)
{
mod.LogAsMod($"Mod incorrectly set asset '{info.Name}'{this.GetOnBehalfOfLabel(editor.OnBehalfOf)} to a null value; ignoring override.", LogLevel.Warn);
@@ -252,6 +254,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <typeparam name="T">The asset type.</typeparam>
/// <param name="info">The basic asset metadata.</param>
private IEnumerable<AssetLoadOperation> GetLoaders<T>(IAssetInfo info)
+ where T : notnull
{
return this.Coordinator
.GetAssetOperations<T>(info)
@@ -262,6 +265,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <typeparam name="T">The asset type.</typeparam>
/// <param name="info">The basic asset metadata.</param>
private IEnumerable<AssetEditOperation> GetEditors<T>(IAssetInfo info)
+ where T : notnull
{
return this.Coordinator
.GetAssetOperations<T>(info)
@@ -273,7 +277,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="loaders">The asset loaders to apply.</param>
/// <param name="error">The error message to show to the user, if the method returns false.</param>
/// <returns>Returns true if only one loader will apply, else false.</returns>
- private bool AssertMaxOneRequiredLoader(IAssetInfo info, AssetLoadOperation[] loaders, out string error)
+ private bool AssertMaxOneRequiredLoader(IAssetInfo info, AssetLoadOperation[] loaders, [NotNullWhen(false)] out string? error)
{
AssetLoadOperation[] required = loaders.Where(p => p.Priority == AssetLoadPriority.Exclusive).ToArray();
if (required.Length <= 1)
@@ -299,7 +303,8 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="onBehalfOf">The content pack on whose behalf the action is being performed.</param>
/// <param name="parenthetical">whether to format the label as a parenthetical shown after the mod name like <c> (for the 'X' content pack)</c>, instead of a standalone label like <c>the 'X' content pack</c>.</param>
/// <returns>Returns the on-behalf-of label if applicable, else <c>null</c>.</returns>
- private string GetOnBehalfOfLabel(IModMetadata onBehalfOf, bool parenthetical = true)
+ [return: NotNullIfNotNull("onBehalfOf")]
+ private string? GetOnBehalfOfLabel(IModMetadata? onBehalfOf, bool parenthetical = true)
{
if (onBehalfOf == null)
return null;
@@ -315,7 +320,8 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="data">The loaded asset data.</param>
/// <param name="loader">The loader which loaded the asset.</param>
/// <returns>Returns whether the asset passed validation checks (after any fixes were applied).</returns>
- private bool TryFixAndValidateLoadedAsset<T>(IAssetInfo info, T data, AssetLoadOperation loader)
+ private bool TryFixAndValidateLoadedAsset<T>(IAssetInfo info, [NotNullWhen(true)] T? data, AssetLoadOperation loader)
+ where T : notnull
{
IModMetadata mod = loader.Mod;
@@ -335,7 +341,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
// add missing tilesheet
if (loadedMap.GetTileSheet(vanillaSheet.Id) == null)
{
- mod.Monitor.LogOnce("SMAPI fixed maps loaded by this mod to prevent errors. See the log file for details.", LogLevel.Warn);
+ mod.Monitor!.LogOnce("SMAPI fixed maps loaded by this mod to prevent errors. See the log file for details.", LogLevel.Warn);
this.Monitor.Log($"Fixed broken map replacement: {mod.DisplayName} loaded '{info.Name}' without a required tilesheet (id: {vanillaSheet.Id}, source: {vanillaSheet.ImageSource}).");
loadedMap.AddTileSheet(new TileSheet(vanillaSheet.Id, loadedMap, vanillaSheet.ImageSource, vanillaSheet.SheetSize, vanillaSheet.TileSize));
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
index 46d5d24e..1b0e1016 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Globalization;
using Microsoft.Xna.Framework.Graphics;
@@ -39,7 +37,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <summary>Get whether a texture was loaded by this content manager.</summary>
/// <param name="texture">The texture to check.</param>
- public bool IsResponsibleFor(Texture2D texture)
+ public bool IsResponsibleFor(Texture2D? texture)
{
return
texture?.Tag is string tag
diff --git a/src/SMAPI/Framework/ContentManagers/IContentManager.cs b/src/SMAPI/Framework/ContentManagers/IContentManager.cs
index c8b2ae64..ac67cad5 100644
--- a/src/SMAPI/Framework/ContentManagers/IContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/IContentManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Content;
@@ -33,25 +31,28 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <summary>Get whether an asset exists and can be loaded.</summary>
/// <typeparam name="T">The expected asset type.</typeparam>
/// <param name="assetName">The normalized asset name.</param>
- bool DoesAssetExist<T>(IAssetName assetName);
+ bool DoesAssetExist<T>(IAssetName assetName)
+ where T: notnull;
/// <summary>Load an asset through the content pipeline, using a localized variant of the <paramref name="assetName"/> if available.</summary>
/// <typeparam name="T">The type of asset to load.</typeparam>
/// <param name="assetName">The asset name relative to the loader root directory.</param>
/// <param name="language">The language for which to load the asset.</param>
/// <param name="useCache">Whether to read/write the loaded asset to the asset cache.</param>
- T LoadLocalized<T>(IAssetName assetName, LocalizedContentManager.LanguageCode language, bool useCache);
+ T LoadLocalized<T>(IAssetName assetName, LocalizedContentManager.LanguageCode language, bool useCache)
+ where T : notnull;
/// <summary>Load an asset through the content pipeline, using the exact asset name without checking for localized variants.</summary>
/// <typeparam name="T">The type of asset to load.</typeparam>
/// <param name="assetName">The asset name relative to the loader root directory.</param>
/// <param name="useCache">Whether to read/write the loaded asset to the asset cache.</param>
- T LoadExact<T>(IAssetName assetName, bool useCache);
+ T LoadExact<T>(IAssetName assetName, bool useCache)
+ where T : notnull;
/// <summary>Assert that the given key has a valid format and return a normalized form consistent with the underlying cache.</summary>
/// <param name="assetName">The asset key to check.</param>
/// <exception cref="SContentLoadException">The asset key is empty or contains invalid characters.</exception>
- string AssertAndNormalizeAssetName(string assetName);
+ string AssertAndNormalizeAssetName(string? assetName);
/// <summary>Get the current content locale.</summary>
string GetLocale();
diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs
index 2d33a22e..2cfd5cce 100644
--- a/src/SMAPI/Framework/ContentPack.cs
+++ b/src/SMAPI/Framework/ContentPack.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.IO;
using StardewModdingAPI.Framework.ModHelpers;
@@ -69,12 +67,12 @@ namespace StardewModdingAPI.Framework
}
/// <inheritdoc />
- public TModel ReadJsonFile<TModel>(string path) where TModel : class
+ public TModel? ReadJsonFile<TModel>(string path) where TModel : class
{
path = PathUtilities.NormalizePath(path);
FileInfo file = this.GetFile(path);
- return file.Exists && this.JsonHelper.ReadJsonFileIfExists(file.FullName, out TModel model)
+ return file.Exists && this.JsonHelper.ReadJsonFileIfExists(file.FullName, out TModel? model)
? model
: null;
}
@@ -93,6 +91,7 @@ namespace StardewModdingAPI.Framework
/// <inheritdoc />
[Obsolete]
public T LoadAsset<T>(string key)
+ where T : notnull
{
return this.ModContent.Load<T>(key);
}
@@ -101,7 +100,7 @@ namespace StardewModdingAPI.Framework
[Obsolete]
public string GetActualAssetKey(string key)
{
- return this.ModContent.GetInternalAssetName(key)?.Name;
+ return this.ModContent.GetInternalAssetName(key).Name;
}
diff --git a/src/SMAPI/Framework/CursorPosition.cs b/src/SMAPI/Framework/CursorPosition.cs
index 8f36a554..24084830 100644
--- a/src/SMAPI/Framework/CursorPosition.cs
+++ b/src/SMAPI/Framework/CursorPosition.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using Microsoft.Xna.Framework;
using StardewValley;
@@ -41,7 +39,7 @@ namespace StardewModdingAPI.Framework
}
/// <inheritdoc />
- public bool Equals(ICursorPosition other)
+ public bool Equals(ICursorPosition? other)
{
return other != null && this.AbsolutePixels == other.AbsolutePixels;
}
diff --git a/src/SMAPI/Framework/Events/ManagedEvent.cs b/src/SMAPI/Framework/Events/ManagedEvent.cs
index 8fa31165..4b8a770d 100644
--- a/src/SMAPI/Framework/Events/ManagedEvent.cs
+++ b/src/SMAPI/Framework/Events/ManagedEvent.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Linq;
@@ -23,7 +21,7 @@ namespace StardewModdingAPI.Framework.Events
private readonly List<ManagedEventHandler<TEventArgs>> Handlers = new();
/// <summary>A cached snapshot of <see cref="Handlers"/>, or <c>null</c> to rebuild it next raise.</summary>
- private ManagedEventHandler<TEventArgs>[] CachedHandlers = Array.Empty<ManagedEventHandler<TEventArgs>>();
+ private ManagedEventHandler<TEventArgs>[]? CachedHandlers = Array.Empty<ManagedEventHandler<TEventArgs>>();
/// <summary>The total number of event handlers registered for this events, regardless of whether they're still registered.</summary>
private int RegistrationIndex;
@@ -100,7 +98,7 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>Raise the event and notify all handlers.</summary>
/// <param name="args">The event arguments to pass.</param>
/// <param name="match">A lambda which returns true if the event should be raised for the given mod.</param>
- public void Raise(TEventArgs args, Func<IModMetadata, bool> match = null)
+ public void Raise(TEventArgs args, Func<IModMetadata, bool>? match = null)
{
this.Raise((_, invoke) => invoke(args), match);
}
@@ -108,7 +106,7 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>Raise the event and notify all handlers.</summary>
/// <param name="invoke">Invoke an event handler. This receives the mod which registered the handler, and should invoke the callback with the event arguments to pass it.</param>
/// <param name="match">A lambda which returns true if the event should be raised for the given mod.</param>
- public void Raise(Action<IModMetadata, Action<TEventArgs>> invoke, Func<IModMetadata, bool> match = null)
+ public void Raise(Action<IModMetadata, Action<TEventArgs>> invoke, Func<IModMetadata, bool>? match = null)
{
// skip if no handlers
if (this.Handlers.Count == 0)
diff --git a/src/SMAPI/Framework/Events/ManagedEventHandler.cs b/src/SMAPI/Framework/Events/ManagedEventHandler.cs
index f31bc04d..d32acdb9 100644
--- a/src/SMAPI/Framework/Events/ManagedEventHandler.cs
+++ b/src/SMAPI/Framework/Events/ManagedEventHandler.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using StardewModdingAPI.Events;
@@ -42,7 +40,7 @@ namespace StardewModdingAPI.Framework.Events
}
/// <inheritdoc />
- public int CompareTo(object obj)
+ public int CompareTo(object? obj)
{
if (obj is not ManagedEventHandler<TEventArgs> other)
throw new ArgumentException("Can't compare to an unrelated object type.");
diff --git a/src/SMAPI/Framework/Exceptions/SContentLoadException.cs b/src/SMAPI/Framework/Exceptions/SContentLoadException.cs
index c21a6b0e..be1fe748 100644
--- a/src/SMAPI/Framework/Exceptions/SContentLoadException.cs
+++ b/src/SMAPI/Framework/Exceptions/SContentLoadException.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using Microsoft.Xna.Framework.Content;
@@ -14,7 +12,7 @@ namespace StardewModdingAPI.Framework.Exceptions
/// <summary>Construct an instance.</summary>
/// <param name="message">The error message.</param>
/// <param name="ex">The underlying exception, if any.</param>
- public SContentLoadException(string message, Exception ex = null)
+ public SContentLoadException(string message, Exception? ex = null)
: base(message, ex) { }
}
}
diff --git a/src/SMAPI/Framework/GameVersion.cs b/src/SMAPI/Framework/GameVersion.cs
index aa91d8f3..542c1345 100644
--- a/src/SMAPI/Framework/GameVersion.cs
+++ b/src/SMAPI/Framework/GameVersion.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
@@ -55,7 +53,7 @@ namespace StardewModdingAPI.Framework
private static string GetSemanticVersionString(string gameVersion)
{
// mapped version
- return GameVersion.VersionMap.TryGetValue(gameVersion, out string semanticVersion)
+ return GameVersion.VersionMap.TryGetValue(gameVersion, out string? semanticVersion)
? semanticVersion
: gameVersion;
}
@@ -64,10 +62,10 @@ namespace StardewModdingAPI.Framework
/// <param name="semanticVersion">The semantic version string.</param>
private static string GetGameVersionString(string semanticVersion)
{
- foreach (var mapping in GameVersion.VersionMap)
+ foreach ((string gameVersion, string equivalentSemanticVersion) in GameVersion.VersionMap)
{
- if (mapping.Value.Equals(semanticVersion, StringComparison.OrdinalIgnoreCase))
- return mapping.Key;
+ if (equivalentSemanticVersion.Equals(semanticVersion, StringComparison.OrdinalIgnoreCase))
+ return gameVersion;
}
return semanticVersion;
diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index 800b198a..7cee20b9 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using StardewModdingAPI.Framework.ModHelpers;
@@ -29,7 +27,7 @@ namespace StardewModdingAPI.Framework
string RelativeDirectoryPath { get; }
/// <summary>Metadata about the mod from SMAPI's internal data (if any).</summary>
- ModDataRecordVersionedFields DataRecord { get; }
+ ModDataRecordVersionedFields? DataRecord { get; }
/// <summary>The metadata resolution status.</summary>
ModMetadataStatus Status { get; }
@@ -41,31 +39,31 @@ namespace StardewModdingAPI.Framework
ModWarning Warnings { get; }
/// <summary>The reason the metadata is invalid, if any.</summary>
- string Error { get; }
+ string? Error { get; }
/// <summary>A detailed technical message for <see cref="Error"/>, if any.</summary>
- public string ErrorDetails { get; }
+ string? ErrorDetails { get; }
/// <summary>Whether the mod folder should be ignored. This is <c>true</c> if it was found within a folder whose name starts with a dot.</summary>
bool IsIgnored { get; }
/// <summary>The mod instance (if loaded and <see cref="IModInfo.IsContentPack"/> is false).</summary>
- IMod Mod { get; }
+ IMod? Mod { get; }
/// <summary>The content pack instance (if loaded and <see cref="IModInfo.IsContentPack"/> is true).</summary>
- IContentPack ContentPack { get; }
+ IContentPack? ContentPack { get; }
/// <summary>The translations for this mod (if loaded).</summary>
- TranslationHelper Translations { get; }
+ TranslationHelper? Translations { get; }
/// <summary>Writes messages to the console and log file as this mod.</summary>
- IMonitor Monitor { get; }
+ IMonitor? Monitor { get; }
/// <summary>The mod-provided API (if any).</summary>
- object Api { get; }
+ object? Api { get; }
/// <summary>The update-check metadata for this mod (if any).</summary>
- ModEntryModel UpdateCheckData { get; }
+ ModEntryModel? UpdateCheckData { get; }
/// <summary>The fake content packs created by this mod, if any.</summary>
ISet<WeakReference<ContentPack>> FakeContentPacks { get; }
@@ -84,7 +82,7 @@ namespace StardewModdingAPI.Framework
/// <param name="error">The reason the metadata is invalid, if any.</param>
/// <param name="errorDetails">A detailed technical message, if any.</param>
/// <returns>Return the instance for chaining.</returns>
- IModMetadata SetStatus(ModMetadataStatus status, ModFailReason reason, string error, string errorDetails = null);
+ IModMetadata SetStatus(ModMetadataStatus status, ModFailReason reason, string? error, string? errorDetails = null);
/// <summary>Set a warning flag for the mod.</summary>
/// <param name="warning">The warning to set.</param>
@@ -103,7 +101,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Set the mod-provided API instance.</summary>
/// <param name="api">The mod-provided API.</param>
- IModMetadata SetApi(object api);
+ IModMetadata SetApi(object? api);
/// <summary>Set the update-check metadata for this mod.</summary>
/// <param name="data">The update-check metadata.</param>
@@ -117,7 +115,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Whether the mod has the given ID.</summary>
/// <param name="id">The mod ID to check.</param>
- bool HasID(string id);
+ bool HasID(string? id);
/// <summary>Get the defined update keys.</summary>
/// <param name="validOnly">Only return valid update keys.</param>
diff --git a/src/SMAPI/Framework/Logging/LogFileManager.cs b/src/SMAPI/Framework/Logging/LogFileManager.cs
index 0b6f9ad2..b396091a 100644
--- a/src/SMAPI/Framework/Logging/LogFileManager.cs
+++ b/src/SMAPI/Framework/Logging/LogFileManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.IO;
@@ -32,7 +30,7 @@ namespace StardewModdingAPI.Framework.Logging
this.Path = path;
// create log directory if needed
- string logDir = System.IO.Path.GetDirectoryName(path);
+ string? logDir = System.IO.Path.GetDirectoryName(path);
if (logDir == null)
throw new ArgumentException($"The log path '{path}' is not valid.");
Directory.CreateDirectory(logDir);
diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
index 12ef0439..b610b395 100644
--- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -114,6 +112,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
public T Load<T>(string key, ContentSource source = ContentSource.ModFolder)
+ where T : notnull
{
IAssetName assetName = this.ContentCore.ParseAssetName(key, allowLocales: source == ContentSource.GameContent);
@@ -140,7 +139,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
[Pure]
- public string NormalizeAssetName(string assetName)
+ public string NormalizeAssetName(string? assetName)
{
return this.ModContentManager.AssertAndNormalizeAssetName(assetName);
}
@@ -171,6 +170,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
public bool InvalidateCache<T>()
+ where T : notnull
{
this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible.");
return this.ContentCore.InvalidateCache((_, _, type) => typeof(T).IsAssignableFrom(type)).Any();
@@ -184,7 +184,8 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public IAssetData GetPatchHelper<T>(T data, string assetName = null)
+ public IAssetData GetPatchHelper<T>(T data, string? assetName = null)
+ where T : notnull
{
if (data == null)
throw new ArgumentNullException(nameof(data), "Can't get a patch helper for a null value.");
diff --git a/src/SMAPI/Framework/ModHelpers/DataHelper.cs b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
index 86a34ee8..92b3b398 100644
--- a/src/SMAPI/Framework/ModHelpers/DataHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.IO;
@@ -42,19 +40,21 @@ namespace StardewModdingAPI.Framework.ModHelpers
** JSON file
****/
/// <inheritdoc />
- public TModel ReadJsonFile<TModel>(string path) where TModel : class
+ public TModel? ReadJsonFile<TModel>(string path)
+ where TModel : class
{
if (!PathUtilities.IsSafeRelativePath(path))
throw new InvalidOperationException($"You must call {nameof(IModHelper.Data)}.{nameof(this.ReadJsonFile)} with a relative path.");
path = Path.Combine(this.ModFolderPath, PathUtilities.NormalizePath(path));
- return this.JsonHelper.ReadJsonFileIfExists(path, out TModel data)
+ return this.JsonHelper.ReadJsonFileIfExists(path, out TModel? data)
? data
: null;
}
/// <inheritdoc />
- public void WriteJsonFile<TModel>(string path, TModel data) where TModel : class
+ public void WriteJsonFile<TModel>(string path, TModel? data)
+ where TModel : class
{
if (!PathUtilities.IsSafeRelativePath(path))
throw new InvalidOperationException($"You must call {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteJsonFile)} with a relative path (without directory climbing).");
@@ -71,7 +71,8 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Save file
****/
/// <inheritdoc />
- public TModel ReadSaveData<TModel>(string key) where TModel : class
+ public TModel? ReadSaveData<TModel>(string key)
+ where TModel : class
{
if (Context.LoadStage == LoadStage.None)
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} when a save file isn't loaded.");
@@ -82,14 +83,15 @@ namespace StardewModdingAPI.Framework.ModHelpers
string internalKey = this.GetSaveFileKey(key);
foreach (IDictionary<string, string> dataField in this.GetDataFields(Context.LoadStage))
{
- if (dataField.TryGetValue(internalKey, out string value))
+ if (dataField.TryGetValue(internalKey, out string? value))
return this.JsonHelper.Deserialize<TModel>(value);
}
return null;
}
/// <inheritdoc />
- public void WriteSaveData<TModel>(string key, TModel model) where TModel : class
+ public void WriteSaveData<TModel>(string key, TModel? model)
+ where TModel : class
{
if (Context.LoadStage == LoadStage.None)
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteSaveData)} when a save file isn't loaded.");
@@ -97,7 +99,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteSaveData)} when connected to a remote host. (Save files are stored on the main player's computer.)");
string internalKey = this.GetSaveFileKey(key);
- string data = model != null
+ string? data = model != null
? this.JsonHelper.Serialize(model, Formatting.None)
: null;
@@ -114,16 +116,18 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Global app data
****/
/// <inheritdoc />
- public TModel ReadGlobalData<TModel>(string key) where TModel : class
+ public TModel? ReadGlobalData<TModel>(string key)
+ where TModel : class
{
string path = this.GetGlobalDataPath(key);
- return this.JsonHelper.ReadJsonFileIfExists(path, out TModel data)
+ return this.JsonHelper.ReadJsonFileIfExists(path, out TModel? data)
? data
: null;
}
/// <inheritdoc />
- public void WriteGlobalData<TModel>(string key, TModel data) where TModel : class
+ public void WriteGlobalData<TModel>(string key, TModel? data)
+ where TModel : class
{
string path = this.GetGlobalDataPath(key);
if (data != null)
diff --git a/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs
index 6d0c2f5f..4c1cde02 100644
--- a/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Linq;
using StardewModdingAPI.Framework.Content;
@@ -71,6 +69,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
public T Load<T>(string key)
+ where T : notnull
{
IAssetName assetName = this.ContentCore.ParseAssetName(key, allowLocales: true);
return this.Load<T>(assetName);
@@ -78,6 +77,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
public T Load<T>(IAssetName assetName)
+ where T : notnull
{
try
{
@@ -105,6 +105,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
public bool InvalidateCache<T>()
+ where T : notnull
{
this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible.");
return this.ContentCore.InvalidateCache((_, _, type) => typeof(T).IsAssignableFrom(type)).Any();
@@ -118,7 +119,8 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public IAssetData GetPatchHelper<T>(T data, string assetName = null)
+ public IAssetData GetPatchHelper<T>(T data, string? assetName = null)
+ where T : notnull
{
if (data == null)
throw new ArgumentNullException(nameof(data), "Can't get a patch helper for a null value.");
diff --git a/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs
index b149ed82..fc47f269 100644
--- a/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using Microsoft.Xna.Framework.Content;
using StardewModdingAPI.Framework.Content;
@@ -57,6 +55,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
public T Load<T>(string relativePath)
+ where T : notnull
{
relativePath = this.RelativePathCache.GetAssetName(relativePath);
@@ -80,7 +79,8 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public IAssetData GetPatchHelper<T>(T data, string relativePath = null)
+ public IAssetData GetPatchHelper<T>(T data, string? relativePath = null)
+ where T : notnull
{
if (data == null)
throw new ArgumentNullException(nameof(data), "Can't get a patch helper for a null value.");
diff --git a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
index e277e6fa..84899610 100644
--- a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using StardewModdingAPI.Framework.Reflection;
@@ -47,7 +45,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public IModInfo Get(string uniqueID)
+ public IModInfo? Get(string uniqueID)
{
return this.Registry.Get(uniqueID);
}
@@ -59,7 +57,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public object GetApi(string uniqueID)
+ public object? GetApi(string uniqueID)
{
// validate ready
if (!this.Registry.AreAllModsInitialized)
@@ -69,17 +67,18 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
// get raw API
- IModMetadata mod = this.Registry.Get(uniqueID);
+ IModMetadata? mod = this.Registry.Get(uniqueID);
if (mod?.Api != null && this.AccessedModApis.Add(mod.Manifest.UniqueID))
this.Monitor.Log($"Accessed mod-provided API for {mod.DisplayName}.");
return mod?.Api;
}
/// <inheritdoc />
- public TInterface GetApi<TInterface>(string uniqueID) where TInterface : class
+ public TInterface? GetApi<TInterface>(string uniqueID)
+ where TInterface : class
{
// get raw API
- object api = this.GetApi(uniqueID);
+ object? api = this.GetApi(uniqueID);
if (api == null)
return null;
diff --git a/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs b/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
index 96b074e2..a419327e 100644
--- a/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using StardewModdingAPI.Framework.Networking;
using StardewValley;
@@ -41,9 +39,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public IMultiplayerPeer GetConnectedPlayer(long id)
+ public IMultiplayerPeer? GetConnectedPlayer(long id)
{
- return this.Multiplayer.Peers.TryGetValue(id, out MultiplayerPeer peer)
+ return this.Multiplayer.Peers.TryGetValue(id, out MultiplayerPeer? peer)
? peer
: null;
}
@@ -55,7 +53,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public void SendMessage<TMessage>(TMessage message, string messageType, string[] modIDs = null, long[] playerIDs = null)
+ public void SendMessage<TMessage>(TMessage message, string messageType, string[]? modIDs = null, long[]? playerIDs = null)
{
this.Multiplayer.BroadcastModMessage(
message: message,
diff --git a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
index 24cbd01c..af018c87 100644
--- a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Reflection;
using StardewModdingAPI.Framework.Reflection;
@@ -39,7 +37,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetField<TValue>(obj, name, required)
- );
+ )!;
}
/// <inheritdoc />
@@ -47,7 +45,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetField<TValue>(type, name, required)
- );
+ )!;
}
/// <inheritdoc />
@@ -55,7 +53,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetProperty<TValue>(obj, name, required)
- );
+ )!;
}
/// <inheritdoc />
@@ -63,7 +61,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetProperty<TValue>(type, name, required)
- );
+ )!;
}
/// <inheritdoc />
@@ -71,7 +69,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetMethod(obj, name, required)
- );
+ )!;
}
/// <inheritdoc />
@@ -79,7 +77,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetMethod(type, name, required)
- );
+ )!;
}
@@ -90,7 +88,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <typeparam name="T">The field value type.</typeparam>
/// <param name="field">The field being accessed.</param>
/// <returns>Returns the same field instance for convenience.</returns>
- private IReflectedField<T> AssertAccessAllowed<T>(IReflectedField<T> field)
+ private IReflectedField<T>? AssertAccessAllowed<T>(IReflectedField<T>? field)
{
this.AssertAccessAllowed(field?.FieldInfo);
return field;
@@ -100,7 +98,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <typeparam name="T">The property value type.</typeparam>
/// <param name="property">The property being accessed.</param>
/// <returns>Returns the same property instance for convenience.</returns>
- private IReflectedProperty<T> AssertAccessAllowed<T>(IReflectedProperty<T> property)
+ private IReflectedProperty<T>? AssertAccessAllowed<T>(IReflectedProperty<T>? property)
{
this.AssertAccessAllowed(property?.PropertyInfo.GetMethod?.GetBaseDefinition());
this.AssertAccessAllowed(property?.PropertyInfo.SetMethod?.GetBaseDefinition());
@@ -110,7 +108,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <summary>Assert that mods can use the reflection helper to access the given member.</summary>
/// <param name="method">The method being accessed.</param>
/// <returns>Returns the same method instance for convenience.</returns>
- private IReflectedMethod AssertAccessAllowed(IReflectedMethod method)
+ private IReflectedMethod? AssertAccessAllowed(IReflectedMethod? method)
{
this.AssertAccessAllowed(method?.MethodInfo.GetBaseDefinition());
return method;
@@ -118,18 +116,18 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <summary>Assert that mods can use the reflection helper to access the given member.</summary>
/// <param name="member">The member being accessed.</param>
- private void AssertAccessAllowed(MemberInfo member)
+ private void AssertAccessAllowed(MemberInfo? member)
{
if (member == null)
return;
// get type which defines the member
- Type declaringType = member.DeclaringType;
+ Type? declaringType = member.DeclaringType;
if (declaringType == null)
throw new InvalidOperationException($"Can't validate access to {member.MemberType} {member.Name} because it has no declaring type."); // should never happen
// validate access
- string rootNamespace = typeof(Program).Namespace;
+ string? rootNamespace = typeof(Program).Namespace;
if (declaringType.Namespace == rootNamespace || declaringType.Namespace?.StartsWith(rootNamespace + ".") == true)
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. (Detected access to {declaringType.FullName}.{member.Name}.)");
}
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs b/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
index 1d4ddf72..b3378ad1 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using Mono.Cecil;
@@ -38,6 +36,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>Resolve an assembly reference.</summary>
/// <param name="name">The assembly name.</param>
+ /// <exception cref="AssemblyResolutionException">The assembly can't be resolved.</exception>
public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
return this.ResolveName(name.Name) ?? base.Resolve(name);
@@ -46,6 +45,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>Resolve an assembly reference.</summary>
/// <param name="name">The assembly name.</param>
/// <param name="parameters">The assembly reader parameters.</param>
+ /// <exception cref="AssemblyResolutionException">The assembly can't be resolved.</exception>
public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
return this.ResolveName(name.Name) ?? base.Resolve(name, parameters);
@@ -57,9 +57,9 @@ namespace StardewModdingAPI.Framework.ModLoading
*********/
/// <summary>Resolve a known assembly definition based on its short or full name.</summary>
/// <param name="name">The assembly's short or full name.</param>
- private AssemblyDefinition ResolveName(string name)
+ private AssemblyDefinition? ResolveName(string name)
{
- return this.Lookup.TryGetValue(name, out AssemblyDefinition match)
+ return this.Lookup.TryGetValue(name, out AssemblyDefinition? match)
? match
: null;
}
diff --git a/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
index 7c94beb7..f5d449c5 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
@@ -57,7 +55,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
{
if (this.MethodNames.Any())
{
- MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ MethodReference? methodRef = RewriteHelper.AsMethodReference(instruction);
if (methodRef != null && methodRef.DeclaringType.FullName == this.FullTypeName && this.MethodNames.Contains(methodRef.Name))
{
string eventName = methodRef.Name.Split(new[] { '_' }, 2)[1];
diff --git a/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
index 96b4098a..7fe4abec 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
@@ -51,7 +49,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
{
if (this.FieldNames.Any())
{
- FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
+ FieldReference? fieldRef = RewriteHelper.AsFieldReference(instruction);
if (fieldRef != null && fieldRef.DeclaringType.FullName == this.FullTypeName && this.FieldNames.Contains(fieldRef.Name))
{
this.FieldNames.Remove(fieldRef.Name);
diff --git a/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
index 7d3c1fd7..e8fdc8c7 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@@ -54,7 +52,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <param name="instruction">The IL instruction.</param>
protected bool IsMatch(Instruction instruction)
{
- MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ MethodReference? methodRef = RewriteHelper.AsMethodReference(instruction);
return
methodRef != null
&& methodRef.DeclaringType.FullName == this.FullTypeName
diff --git a/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
index b2f2e193..2af76f55 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@@ -54,7 +52,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <param name="instruction">The IL instruction.</param>
protected bool IsMatch(Instruction instruction)
{
- MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ MethodReference? methodRef = RewriteHelper.AsMethodReference(instruction);
return
methodRef != null
&& methodRef.DeclaringType.FullName == this.FullTypeName
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
index 81f90498..f34542c3 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
@@ -1,6 +1,5 @@
-#nullable disable
-
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
@@ -34,11 +33,11 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
// field reference
- FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
+ FieldReference? fieldRef = RewriteHelper.AsFieldReference(instruction);
if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType))
{
// get target field
- FieldDefinition targetField = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name);
+ FieldDefinition? targetField = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name);
if (targetField == null)
return false;
@@ -51,16 +50,16 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
}
// method reference
- MethodReference methodReference = RewriteHelper.AsMethodReference(instruction);
+ MethodReference? methodReference = RewriteHelper.AsMethodReference(instruction);
if (methodReference != null && !this.IsUnsupported(methodReference) && this.ShouldValidate(methodReference.DeclaringType))
{
// get potential targets
- MethodDefinition[] candidateMethods = methodReference.DeclaringType.Resolve()?.Methods.Where(found => found.Name == methodReference.Name).ToArray();
+ MethodDefinition[]? candidateMethods = methodReference.DeclaringType.Resolve()?.Methods.Where(found => found.Name == methodReference.Name).ToArray();
if (candidateMethods == null || !candidateMethods.Any())
return false;
// compare return types
- MethodDefinition methodDef = methodReference.Resolve();
+ MethodDefinition? methodDef = methodReference.Resolve();
if (methodDef == null)
return false; // validated by ReferenceToMissingMemberFinder
@@ -80,7 +79,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
*********/
/// <summary>Whether references to the given type should be validated.</summary>
/// <param name="type">The type reference.</param>
- private bool ShouldValidate(TypeReference type)
+ private bool ShouldValidate([NotNullWhen(true)] TypeReference? type)
{
return type != null && this.ValidateReferencesToAssemblies.Contains(type.Scope.Name);
}
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
index 001d1986..fae7fb12 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
@@ -1,6 +1,5 @@
-#nullable disable
-
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@@ -33,10 +32,10 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
// field reference
- FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
+ FieldReference? fieldRef = RewriteHelper.AsFieldReference(instruction);
if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType))
{
- FieldDefinition target = fieldRef.Resolve();
+ FieldDefinition? target = fieldRef.Resolve();
if (target == null || target.HasConstant)
{
this.MarkFlag(InstructionHandleResult.NotCompatible, $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (no such field)");
@@ -45,10 +44,10 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
}
// method reference
- MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ MethodReference? methodRef = RewriteHelper.AsMethodReference(instruction);
if (methodRef != null && this.ShouldValidate(methodRef.DeclaringType) && !this.IsUnsupported(methodRef))
{
- MethodDefinition target = methodRef.Resolve();
+ MethodDefinition? target = methodRef.Resolve();
if (target == null)
{
string phrase;
@@ -73,7 +72,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
*********/
/// <summary>Whether references to the given type should be validated.</summary>
/// <param name="type">The type reference.</param>
- private bool ShouldValidate(TypeReference type)
+ private bool ShouldValidate([NotNullWhen(true)] TypeReference? type)
{
return type != null && this.ValidateReferencesToAssemblies.Contains(type.Scope.Name);
}
diff --git a/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
index 4c589ed8..17acbf9a 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using Mono.Cecil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@@ -19,7 +17,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
private readonly InstructionHandleResult Result;
/// <summary>Get whether a matched type should be ignored.</summary>
- private readonly Func<TypeReference, bool> ShouldIgnore;
+ private readonly Func<TypeReference, bool>? ShouldIgnore;
/*********
@@ -29,7 +27,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <param name="assemblyName">The full assembly name to which to find references.</param>
/// <param name="result">The result to return for matching instructions.</param>
/// <param name="shouldIgnore">Get whether a matched type should be ignored.</param>
- public TypeAssemblyFinder(string assemblyName, InstructionHandleResult result, Func<TypeReference, bool> shouldIgnore = null)
+ public TypeAssemblyFinder(string assemblyName, InstructionHandleResult result, Func<TypeReference, bool>? shouldIgnore = null)
: base(defaultPhrase: $"{assemblyName} assembly")
{
this.AssemblyName = assemblyName;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
index 04a5b970..77762f41 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using Mono.Cecil;
@@ -20,7 +18,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
private readonly InstructionHandleResult Result;
/// <summary>Get whether a matched type should be ignored.</summary>
- private readonly Func<TypeReference, bool> ShouldIgnore;
+ private readonly Func<TypeReference, bool>? ShouldIgnore;
/*********
@@ -30,7 +28,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <param name="fullTypeNames">The full type names to match.</param>
/// <param name="result">The result to return for matching instructions.</param>
/// <param name="shouldIgnore">Get whether a matched type should be ignored.</param>
- public TypeFinder(string[] fullTypeNames, InstructionHandleResult result, Func<TypeReference, bool> shouldIgnore = null)
+ public TypeFinder(string[] fullTypeNames, InstructionHandleResult result, Func<TypeReference, bool>? shouldIgnore = null)
: base(defaultPhrase: $"{string.Join(", ", fullTypeNames)} type{(fullTypeNames.Length != 1 ? "s" : "")}") // default phrase should never be used
{
this.FullTypeNames = new HashSet<string>(fullTypeNames);
@@ -42,7 +40,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
/// <param name="fullTypeName">The full type name to match.</param>
/// <param name="result">The result to return for matching instructions.</param>
/// <param name="shouldIgnore">Get whether a matched type should be ignored.</param>
- public TypeFinder(string fullTypeName, InstructionHandleResult result, Func<TypeReference, bool> shouldIgnore = null)
+ public TypeFinder(string fullTypeName, InstructionHandleResult result, Func<TypeReference, bool>? shouldIgnore = null)
: this(new[] { fullTypeName }, result, shouldIgnore) { }
/// <inheritdoc />
diff --git a/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs b/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs
index bea786cd..865bf076 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using Mono.Cecil;
@@ -59,7 +57,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
/// <param name="flag">The result flag to set.</param>
/// <param name="resultMessage">The result message to add.</param>
/// <returns>Returns true for convenience.</returns>
- protected bool MarkFlag(InstructionHandleResult flag, string resultMessage = null)
+ protected bool MarkFlag(InstructionHandleResult flag, string? resultMessage = null)
{
this.Flags.Add(flag);
if (resultMessage != null)
diff --git a/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs b/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
index 09ff78f7..55369602 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
@@ -1,7 +1,6 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
@@ -10,6 +9,7 @@ using Mono.Collections.Generic;
namespace StardewModdingAPI.Framework.ModLoading.Framework
{
/// <summary>Handles recursively rewriting loaded assembly code.</summary>
+ [SuppressMessage("ReSharper", "AccessToModifiedClosure", Justification = "Rewrite callbacks are invoked immediately.")]
internal class RecursiveRewriter
{
/*********
@@ -77,7 +77,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
{
changed |= this.RewriteModuleImpl(this.Module);
- foreach (var type in types)
+ foreach (TypeDefinition type in types)
changed |= this.RewriteTypeDefinition(type);
}
catch (Exception ex)
@@ -129,9 +129,10 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
ILProcessor cil = method.Body.GetILProcessor();
Collection<Instruction> instructions = cil.Body.Instructions;
bool addedInstructions = false;
+ // ReSharper disable once ForCanBeConvertedToForeach -- deliberate to allow changing the collection
for (int i = 0; i < instructions.Count; i++)
{
- var instruction = instructions[i];
+ Instruction instruction = instructions[i];
if (instruction.OpCode.Code == Code.Nop)
continue;
@@ -174,7 +175,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
bool rewritten = false;
// field reference
- FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
+ FieldReference? fieldRef = RewriteHelper.AsFieldReference(instruction);
if (fieldRef != null)
{
rewritten |= this.RewriteTypeReference(fieldRef.DeclaringType, newType => fieldRef.DeclaringType = newType);
@@ -182,7 +183,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
}
// method reference
- MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ MethodReference? methodRef = RewriteHelper.AsMethodReference(instruction);
if (methodRef != null)
this.RewriteMethodReference(methodRef);
@@ -212,7 +213,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
});
rewritten |= this.RewriteTypeReference(methodRef.ReturnType, newType => methodRef.ReturnType = newType);
- foreach (var parameter in methodRef.Parameters)
+ foreach (ParameterDefinition parameter in methodRef.Parameters)
rewritten |= this.RewriteTypeReference(parameter.ParameterType, newType => parameter.ParameterType = newType);
if (methodRef is GenericInstanceMethod genericRef)
@@ -264,7 +265,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
bool curChanged = false;
// attribute type
- TypeReference newAttrType = null;
+ TypeReference? newAttrType = null;
rewritten |= this.RewriteTypeReference(attribute.AttributeType, newType =>
{
newAttrType = newType;
@@ -289,9 +290,9 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
if (curChanged)
{
// get constructor
- MethodDefinition constructor = (newAttrType ?? attribute.AttributeType)
+ MethodDefinition? constructor = (newAttrType ?? attribute.AttributeType)
.Resolve()
- .Methods
+ ?.Methods
.Where(method => method.IsConstructor)
.FirstOrDefault(ctor => RewriteHelper.HasMatchingSignature(ctor, attribute.Constructor));
if (constructor == null)
@@ -301,9 +302,9 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
var newAttr = new CustomAttribute(this.Module.ImportReference(constructor));
for (int i = 0; i < argTypes.Length; i++)
newAttr.ConstructorArguments.Add(new CustomAttributeArgument(argTypes[i], attribute.ConstructorArguments[i].Value));
- foreach (var prop in attribute.Properties)
+ foreach (CustomAttributeNamedArgument prop in attribute.Properties)
newAttr.Properties.Add(new CustomAttributeNamedArgument(prop.Name, prop.Argument));
- foreach (var field in attribute.Fields)
+ foreach (CustomAttributeNamedArgument field in attribute.Fields)
newAttr.Fields.Add(new CustomAttributeNamedArgument(field.Name, field.Argument));
// swap attribute
diff --git a/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs b/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
index 8f47fbdd..15f71251 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Linq;
using System.Reflection;
@@ -23,7 +21,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
*********/
/// <summary>Get the field reference from an instruction if it matches.</summary>
/// <param name="instruction">The IL instruction.</param>
- public static FieldReference AsFieldReference(Instruction instruction)
+ public static FieldReference? AsFieldReference(Instruction instruction)
{
return instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld
? (FieldReference)instruction.Operand
@@ -32,7 +30,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
/// <summary>Get the method reference from an instruction if it matches.</summary>
/// <param name="instruction">The IL instruction.</param>
- public static MethodReference AsMethodReference(Instruction instruction)
+ public static MethodReference? AsMethodReference(Instruction instruction)
{
return instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt || instruction.OpCode == OpCodes.Newobj
? (MethodReference)instruction.Operand
@@ -42,7 +40,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
/// <summary>Get the CIL instruction to load a value onto the stack.</summary>
/// <param name="rawValue">The constant value to inject.</param>
/// <returns>Returns the instruction, or <c>null</c> if the value type isn't supported.</returns>
- public static Instruction GetLoadValueInstruction(object rawValue)
+ public static Instruction? GetLoadValueInstruction(object? rawValue)
{
return rawValue switch
{
@@ -151,7 +149,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework
/// <param name="typeA">The type ID to compare.</param>
/// <param name="typeB">The other type ID to compare.</param>
/// <returns>true if the type IDs look like the same type, false if not.</returns>
- public static bool LooksLikeSameType(TypeReference typeA, TypeReference typeB)
+ public static bool LooksLikeSameType(TypeReference? typeA, TypeReference? typeB)
{
return RewriteHelper.TypeDefinitionComparer.Equals(typeA, typeB);
}
diff --git a/src/SMAPI/Framework/ModLoading/InvalidModStateException.cs b/src/SMAPI/Framework/ModLoading/InvalidModStateException.cs
index 9dca9bc4..b53a9886 100644
--- a/src/SMAPI/Framework/ModLoading/InvalidModStateException.cs
+++ b/src/SMAPI/Framework/ModLoading/InvalidModStateException.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
namespace StardewModdingAPI.Framework.ModLoading
@@ -10,7 +8,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>Construct an instance.</summary>
/// <param name="message">The error message.</param>
/// <param name="ex">The underlying exception, if any.</param>
- public InvalidModStateException(string message, Exception ex = null)
+ public InvalidModStateException(string message, Exception? ex = null)
: base(message, ex) { }
}
}
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index 0e698bfd..fe54634b 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -1,7 +1,6 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using StardewModdingAPI.Framework.ModHelpers;
@@ -44,7 +43,7 @@ namespace StardewModdingAPI.Framework.ModLoading
public IManifest Manifest { get; }
/// <inheritdoc />
- public ModDataRecordVersionedFields DataRecord { get; }
+ public ModDataRecordVersionedFields? DataRecord { get; }
/// <inheritdoc />
public ModMetadataStatus Status { get; private set; }
@@ -56,33 +55,35 @@ namespace StardewModdingAPI.Framework.ModLoading
public ModWarning Warnings => this.ActualWarnings & ~(this.DataRecord?.DataRecord.SuppressWarnings ?? ModWarning.None);
/// <inheritdoc />
- public string Error { get; private set; }
+ public string? Error { get; private set; }
/// <inheritdoc />
- public string ErrorDetails { get; private set; }
+ public string? ErrorDetails { get; private set; }
/// <inheritdoc />
public bool IsIgnored { get; }
/// <inheritdoc />
- public IMod Mod { get; private set; }
+ public IMod? Mod { get; private set; }
/// <inheritdoc />
- public IContentPack ContentPack { get; private set; }
+ public IContentPack? ContentPack { get; private set; }
/// <inheritdoc />
- public TranslationHelper Translations { get; private set; }
+ public TranslationHelper? Translations { get; private set; }
/// <inheritdoc />
- public IMonitor Monitor { get; private set; }
+ public IMonitor? Monitor { get; private set; }
/// <inheritdoc />
- public object Api { get; private set; }
+ public object? Api { get; private set; }
/// <inheritdoc />
- public ModEntryModel UpdateCheckData { get; private set; }
+ public ModEntryModel? UpdateCheckData { get; private set; }
/// <inheritdoc />
+ [MemberNotNullWhen(true, nameof(ModMetadata.ContentPack))]
+ [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "The manifest may be null for broken mods while loading.")]
public bool IsContentPack => this.Manifest?.ContentPackFor != null;
/// <summary>The fake content packs created by this mod, if any.</summary>
@@ -99,13 +100,13 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <param name="manifest">The mod manifest.</param>
/// <param name="dataRecord">Metadata about the mod from SMAPI's internal data (if any).</param>
/// <param name="isIgnored">Whether the mod folder should be ignored. This should be <c>true</c> if it was found within a folder whose name starts with a dot.</param>
- public ModMetadata(string displayName, string directoryPath, string rootPath, IManifest manifest, ModDataRecordVersionedFields dataRecord, bool isIgnored)
+ public ModMetadata(string displayName, string directoryPath, string rootPath, IManifest? manifest, ModDataRecordVersionedFields? dataRecord, bool isIgnored)
{
this.DisplayName = displayName;
this.DirectoryPath = directoryPath;
this.RootPath = rootPath;
this.RelativeDirectoryPath = PathUtilities.GetRelativePath(this.RootPath, this.DirectoryPath);
- this.Manifest = manifest;
+ this.Manifest = manifest!; // manifest may be null in low-level SMAPI code, but won't be null once it's received by mods via IModInfo
this.DataRecord = dataRecord;
this.IsIgnored = isIgnored;
@@ -121,7 +122,7 @@ namespace StardewModdingAPI.Framework.ModLoading
}
/// <inheritdoc />
- public IModMetadata SetStatus(ModMetadataStatus status, ModFailReason reason, string error, string errorDetails = null)
+ public IModMetadata SetStatus(ModMetadataStatus status, ModFailReason reason, string? error, string? errorDetails = null)
{
this.Status = status;
this.FailReason = reason;
@@ -162,7 +163,7 @@ namespace StardewModdingAPI.Framework.ModLoading
}
/// <inheritdoc />
- public IModMetadata SetApi(object api)
+ public IModMetadata SetApi(object? api)
{
this.Api = api;
return this;
@@ -176,6 +177,7 @@ namespace StardewModdingAPI.Framework.ModLoading
}
/// <inheritdoc />
+ [MemberNotNullWhen(true, nameof(IModInfo.Manifest))]
public bool HasManifest()
{
return this.Manifest != null;
@@ -190,7 +192,7 @@ namespace StardewModdingAPI.Framework.ModLoading
}
/// <inheritdoc />
- public bool HasID(string id)
+ public bool HasID(string? id)
{
return
this.HasID()
@@ -245,7 +247,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <inheritdoc />
public string GetRelativePathWithRoot()
{
- string rootFolderName = Path.GetFileName(this.RootPath) ?? "";
+ string rootFolderName = Path.GetFileName(this.RootPath);
return Path.Combine(rootFolderName, this.RelativeDirectoryPath);
}
@@ -254,7 +256,7 @@ namespace StardewModdingAPI.Framework.ModLoading
{
foreach (var reference in this.FakeContentPacks.ToArray())
{
- if (!reference.TryGetTarget(out ContentPack pack))
+ if (!reference.TryGetTarget(out ContentPack? pack))
{
this.FakeContentPacks.Remove(reference);
continue;
diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs
index c05005b8..afe38bfd 100644
--- a/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs
+++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -19,7 +17,7 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
/*********
** Public methods
*********/
- public static ConstructorInfo DeclaredConstructor(Type type, Type[] parameters = null)
+ public static ConstructorInfo DeclaredConstructor(Type type, Type[]? parameters = null)
{
// Harmony 1.x matched both static and instance constructors
return
@@ -27,7 +25,7 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
?? AccessTools.DeclaredConstructor(type, parameters, searchForStatic: true);
}
- public static ConstructorInfo Constructor(Type type, Type[] parameters = null)
+ public static ConstructorInfo Constructor(Type type, Type[]? parameters = null)
{
// Harmony 1.x matched both static and instance constructors
return
diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs
index fea8c100..9c8ba2b0 100644
--- a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs
+++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -30,7 +28,8 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
return new Harmony(id);
}
- public DynamicMethod Patch(MethodBase original, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null)
+ [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse", Justification = "If the user passes a null original method, we let it fail in the underlying Harmony instance instead of handling it here.")]
+ public DynamicMethod Patch(MethodBase original, HarmonyMethod? prefix = null, HarmonyMethod? postfix = null, HarmonyMethod? transpiler = null)
{
// In Harmony 1.x you could target a virtual method that's not implemented by the
// target type, but in Harmony 2.0 you need to target the concrete implementation.
@@ -60,7 +59,7 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
/// <param name="prefix">The prefix method, if any.</param>
/// <param name="postfix">The postfix method, if any.</param>
/// <param name="transpiler">The transpiler method, if any.</param>
- private string GetPatchTypesLabel(HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null)
+ private string GetPatchTypesLabel(HarmonyMethod? prefix = null, HarmonyMethod? postfix = null, HarmonyMethod? transpiler = null)
{
var patchTypes = new List<string>();
@@ -76,7 +75,7 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
/// <summary>Get a human-readable label for the method being patched.</summary>
/// <param name="method">The method being patched.</param>
- private string GetMethodLabel(MethodBase method)
+ private string GetMethodLabel(MethodBase? method)
{
return method != null
? $"method {method.DeclaringType?.FullName}.{method.Name}"
diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs
index 93124591..2b1ca54b 100644
--- a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs
+++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
@@ -23,7 +21,7 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
this.ImportMethodImpl(method);
}
- public HarmonyMethodFacade(Type type, string name, Type[] parameters = null)
+ public HarmonyMethodFacade(Type type, string name, Type[]? parameters = null)
{
this.ImportMethodImpl(AccessTools.Method(type, name, parameters));
}
@@ -40,7 +38,7 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
// internal code still handles null fine. For backwards compatibility, this bypasses
// the new restriction when the mod hasn't been updated for Harmony 2.0 yet.
- MethodInfo importMethod = typeof(HarmonyMethod).GetMethod("ImportMethod", BindingFlags.Instance | BindingFlags.NonPublic);
+ MethodInfo? importMethod = typeof(HarmonyMethod).GetMethod("ImportMethod", BindingFlags.Instance | BindingFlags.NonPublic);
if (importMethod == null)
throw new InvalidOperationException("Can't find 'HarmonyMethod.ImportMethod' method");
importMethod.Invoke(this, new object[] { methodInfo });
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/HarmonyRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/HarmonyRewriter.cs
index 92397c58..aea490c8 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/HarmonyRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/HarmonyRewriter.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using HarmonyLib;
using Mono.Cecil;
@@ -59,7 +57,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
if (this.ShouldRewrite)
{
// rewrite Harmony 1.x methods to Harmony 2.0
- MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ MethodReference? methodRef = RewriteHelper.AsMethodReference(instruction);
if (this.TryRewriteMethodsToFacade(module, methodRef))
{
this.OnChanged();
@@ -67,7 +65,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
}
// rewrite renamed fields
- FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
+ FieldReference? fieldRef = RewriteHelper.AsFieldReference(instruction);
if (fieldRef != null)
{
if (fieldRef.DeclaringType.FullName == "HarmonyLib.HarmonyMethod" && fieldRef.Name == "prioritiy")
@@ -95,13 +93,13 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <summary>Rewrite methods to use Harmony facades if needed.</summary>
/// <param name="module">The assembly module containing the method reference.</param>
/// <param name="methodRef">The method reference to map.</param>
- private bool TryRewriteMethodsToFacade(ModuleDefinition module, MethodReference methodRef)
+ private bool TryRewriteMethodsToFacade(ModuleDefinition module, MethodReference? methodRef)
{
if (!this.ReplacedTypes)
return false; // not Harmony (or already using Harmony 2.0)
// get facade type
- Type toType = methodRef?.DeclaringType.FullName switch
+ Type? toType = methodRef?.DeclaringType.FullName switch
{
"HarmonyLib.Harmony" => typeof(HarmonyInstanceFacade),
"HarmonyLib.AccessTools" => typeof(AccessToolsFacade),
@@ -112,9 +110,9 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
return false;
// map if there's a matching method
- if (RewriteHelper.HasMatchingSignature(toType, methodRef))
+ if (RewriteHelper.HasMatchingSignature(toType, methodRef!))
{
- methodRef.DeclaringType = module.ImportReference(toType);
+ methodRef!.DeclaringType = module.ImportReference(toType);
return true;
}
@@ -139,7 +137,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
{
string fullName = type.FullName.Replace("Harmony.", "HarmonyLib.");
string targetName = typeof(Harmony).AssemblyQualifiedName!.Replace(typeof(Harmony).FullName!, fullName);
- return Type.GetType(targetName, throwOnError: true);
+ return Type.GetType(targetName, throwOnError: true)!;
}
}
}
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs
index fc06e779..9c6a3980 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs
@@ -1,6 +1,5 @@
-#nullable disable
-
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
@@ -33,17 +32,17 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
// get field ref
- FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
+ FieldReference? fieldRef = RewriteHelper.AsFieldReference(instruction);
if (fieldRef == null || !this.ShouldValidate(fieldRef.DeclaringType))
return false;
// skip if not broken
- FieldDefinition fieldDefinition = fieldRef.Resolve();
+ FieldDefinition? fieldDefinition = fieldRef.Resolve();
if (fieldDefinition?.HasConstant == false)
return false;
// rewrite if possible
- TypeDefinition declaringType = fieldRef.DeclaringType.Resolve();
+ TypeDefinition? declaringType = fieldRef.DeclaringType.Resolve();
bool isRead = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld;
return
this.TryRewriteToProperty(module, instruction, fieldRef, declaringType, isRead)
@@ -56,7 +55,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
*********/
/// <summary>Whether references to the given type should be validated.</summary>
/// <param name="type">The type reference.</param>
- private bool ShouldValidate(TypeReference type)
+ private bool ShouldValidate([NotNullWhen(true)] TypeReference? type)
{
return type != null && this.RewriteReferencesToAssemblies.Contains(type.Scope.Name);
}
@@ -70,8 +69,8 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
private bool TryRewriteToProperty(ModuleDefinition module, Instruction instruction, FieldReference fieldRef, TypeDefinition declaringType, bool isRead)
{
// get equivalent property
- PropertyDefinition property = declaringType?.Properties.FirstOrDefault(p => p.Name == fieldRef.Name);
- MethodDefinition method = isRead ? property?.GetMethod : property?.SetMethod;
+ PropertyDefinition? property = declaringType?.Properties.FirstOrDefault(p => p.Name == fieldRef.Name);
+ MethodDefinition? method = isRead ? property?.GetMethod : property?.SetMethod;
if (method == null)
return false;
@@ -86,14 +85,14 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <summary>Try rewriting the field into a matching const field.</summary>
/// <param name="instruction">The CIL instruction to rewrite.</param>
/// <param name="field">The field definition.</param>
- private bool TryRewriteToConstField(Instruction instruction, FieldDefinition field)
+ private bool TryRewriteToConstField(Instruction instruction, FieldDefinition? field)
{
// must have been a static field read, and the new field must be const
if (instruction.OpCode != OpCodes.Ldsfld || field?.HasConstant != true)
return false;
// get opcode for value type
- Instruction loadInstruction = RewriteHelper.GetLoadValueInstruction(field.Constant);
+ Instruction? loadInstruction = RewriteHelper.GetLoadValueInstruction(field.Constant);
if (loadInstruction == null)
return false;
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs
index 4860072c..601ecbbc 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs
@@ -1,6 +1,5 @@
-#nullable disable
-
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
@@ -33,7 +32,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
// get method ref
- MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ MethodReference? methodRef = RewriteHelper.AsMethodReference(instruction);
if (methodRef == null || !this.ShouldValidate(methodRef.DeclaringType))
return false;
@@ -42,13 +41,13 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
return false;
// get type
- var type = methodRef.DeclaringType.Resolve();
+ TypeDefinition? type = methodRef.DeclaringType.Resolve();
if (type == null)
return false;
// get method definition
- MethodDefinition method = null;
- foreach (var match in type.Methods.Where(p => p.Name == methodRef.Name))
+ MethodDefinition? method = null;
+ foreach (MethodDefinition match in type.Methods.Where(p => p.Name == methodRef.Name))
{
// reference matches initial parameters of definition
if (methodRef.Parameters.Count >= match.Parameters.Count || !this.InitialParametersMatch(methodRef, match))
@@ -72,7 +71,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
return false; // SMAPI needs to load the value onto the stack before the method call, but the optional parameter type wasn't recognized
// rewrite method reference
- foreach (Instruction loadInstruction in loadInstructions)
+ foreach (Instruction? loadInstruction in loadInstructions)
cil.InsertBefore(instruction, loadInstruction);
instruction.Operand = module.ImportReference(method);
@@ -86,7 +85,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
*********/
/// <summary>Whether references to the given type should be validated.</summary>
/// <param name="type">The type reference.</param>
- private bool ShouldValidate(TypeReference type)
+ private bool ShouldValidate([NotNullWhen(true)] TypeReference? type)
{
return type != null && this.RewriteReferencesToAssemblies.Contains(type.Scope.Name);
}
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs
index 00daf337..2e2f6316 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs
@@ -1,6 +1,5 @@
-#nullable disable
-
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
@@ -28,7 +27,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <param name="fromType">The type whose methods to remap.</param>
/// <param name="toType">The type with methods to map to.</param>
/// <param name="nounPhrase">A brief noun phrase indicating what the instruction finder matches (or <c>null</c> to generate one).</param>
- public MethodParentRewriter(string fromType, Type toType, string nounPhrase = null)
+ public MethodParentRewriter(string fromType, Type toType, string? nounPhrase = null)
: base(nounPhrase ?? $"{fromType.Split('.').Last()} methods")
{
this.FromType = fromType;
@@ -39,14 +38,14 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <param name="fromType">The type whose methods to remap.</param>
/// <param name="toType">The type with methods to map to.</param>
/// <param name="nounPhrase">A brief noun phrase indicating what the instruction finder matches (or <c>null</c> to generate one).</param>
- public MethodParentRewriter(Type fromType, Type toType, string nounPhrase = null)
- : this(fromType.FullName, toType, nounPhrase) { }
+ public MethodParentRewriter(Type fromType, Type toType, string? nounPhrase = null)
+ : this(fromType.FullName!, toType, nounPhrase) { }
/// <inheritdoc />
public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction)
{
// get method ref
- MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ MethodReference? methodRef = RewriteHelper.AsMethodReference(instruction);
if (!this.IsMatch(methodRef))
return false;
@@ -61,7 +60,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
*********/
/// <summary>Get whether a CIL instruction matches.</summary>
/// <param name="methodRef">The method reference.</param>
- private bool IsMatch(MethodReference methodRef)
+ private bool IsMatch([NotNullWhen(true)] MethodReference? methodRef)
{
return
methodRef != null
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs
index bdc4c4f3..a81cb5be 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using Mono.Cecil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@@ -19,7 +17,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
private readonly Type ToType;
/// <summary>Get whether a matched type should be ignored.</summary>
- private readonly Func<TypeReference, bool> ShouldIgnore;
+ private readonly Func<TypeReference, bool>? ShouldIgnore;
/*********
@@ -29,7 +27,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// <param name="fromTypeFullName">The full type name to which to find references.</param>
/// <param name="toType">The new type to reference.</param>
/// <param name="shouldIgnore">Get whether a matched type should be ignored.</param>
- public TypeReferenceRewriter(string fromTypeFullName, Type toType, Func<TypeReference, bool> shouldIgnore = null)
+ public TypeReferenceRewriter(string fromTypeFullName, Type toType, Func<TypeReference, bool>? shouldIgnore = null)
: base($"{fromTypeFullName} type")
{
this.FromTypeName = fromTypeFullName;
diff --git a/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs b/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs
index 4af7c1e7..0d3aff9f 100644
--- a/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs
+++ b/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.IO;
@@ -38,7 +36,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Symbols
/// <param name="fileName">The assembly file name.</param>
public ISymbolReader GetSymbolReader(ModuleDefinition module, string fileName)
{
- return this.SymbolsByAssemblyPath.TryGetValue(module.Name, out Stream symbolData)
+ return this.SymbolsByAssemblyPath.TryGetValue(module.Name, out Stream? symbolData)
? new SymbolReader(module, symbolData)
: this.BaseProvider.GetSymbolReader(module, fileName);
}
@@ -48,7 +46,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Symbols
/// <param name="symbolStream">The loaded symbol file stream.</param>
public ISymbolReader GetSymbolReader(ModuleDefinition module, Stream symbolStream)
{
- return this.SymbolsByAssemblyPath.TryGetValue(module.Name, out Stream symbolData)
+ return this.SymbolsByAssemblyPath.TryGetValue(module.Name, out Stream? symbolData)
? new SymbolReader(module, symbolData)
: this.BaseProvider.GetSymbolReader(module, symbolStream);
}
diff --git a/src/SMAPI/Framework/ModLoading/TypeReferenceComparer.cs b/src/SMAPI/Framework/ModLoading/TypeReferenceComparer.cs
index 248c29fc..d81d763e 100644
--- a/src/SMAPI/Framework/ModLoading/TypeReferenceComparer.cs
+++ b/src/SMAPI/Framework/ModLoading/TypeReferenceComparer.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Linq;
@@ -18,7 +16,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <c>TKey</c> in the above example). If all components are equal after substitution, and the
/// tokens can all be mapped to the same generic type, the types are considered equal.
/// </remarks>
- internal class TypeReferenceComparer : IEqualityComparer<TypeReference>
+ internal class TypeReferenceComparer : IEqualityComparer<TypeReference?>
{
/*********
** Public methods
@@ -26,7 +24,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>Get whether the specified objects are equal.</summary>
/// <param name="a">The first object to compare.</param>
/// <param name="b">The second object to compare.</param>
- public bool Equals(TypeReference a, TypeReference b)
+ public bool Equals(TypeReference? a, TypeReference? b)
{
if (a == null || b == null)
return a == b;
@@ -54,7 +52,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <param name="typeB">The second type to compare.</param>
private bool HeuristicallyEquals(TypeReference typeA, TypeReference typeB)
{
- bool HeuristicallyEquals(string typeNameA, string typeNameB, IDictionary<string, string> tokenMap)
+ bool HeuristicallyEqualsImpl(string typeNameA, string typeNameB, IDictionary<string, string> tokenMap)
{
// analyze type names
bool hasTokensA = typeNameA.Contains("!");
@@ -82,14 +80,14 @@ namespace StardewModdingAPI.Framework.ModLoading
for (int i = 0; i < symbolsA.Length; i++)
{
- if (!HeuristicallyEquals(symbolsA[i], symbolsB[i], tokenMap))
+ if (!HeuristicallyEqualsImpl(symbolsA[i], symbolsB[i], tokenMap))
return false;
}
return true;
}
- return HeuristicallyEquals(typeA.FullName, typeB.FullName, new Dictionary<string, string>());
+ return HeuristicallyEqualsImpl(typeA.FullName, typeB.FullName, new Dictionary<string, string>());
}
/// <summary>Map a generic type placeholder (like <c>!0</c>) to its actual type.</summary>
@@ -99,7 +97,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <returns>Returns the previously-mapped type if applicable, else the <paramref name="type"/>.</returns>
private string MapPlaceholder(string placeholder, string type, IDictionary<string, string> map)
{
- if (map.TryGetValue(placeholder, out string result))
+ if (map.TryGetValue(placeholder, out string? result))
return result;
map[placeholder] = type;
diff --git a/src/SMAPI/Framework/ModRegistry.cs b/src/SMAPI/Framework/ModRegistry.cs
index cae38637..de16a780 100644
--- a/src/SMAPI/Framework/ModRegistry.cs
+++ b/src/SMAPI/Framework/ModRegistry.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -42,7 +40,7 @@ namespace StardewModdingAPI.Framework
/// <param name="modAssembly">The mod assembly.</param>
public void TrackAssemblies(IModMetadata metadata, Assembly modAssembly)
{
- this.ModNamesByAssembly[modAssembly.FullName] = metadata;
+ this.ModNamesByAssembly[modAssembly.FullName!] = metadata;
}
/// <summary>Get metadata for all loaded mods.</summary>
@@ -62,7 +60,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Get metadata for a loaded mod.</summary>
/// <param name="uniqueID">The mod's unique ID.</param>
/// <returns>Returns the matching mod's metadata, or <c>null</c> if not found.</returns>
- public IModMetadata Get(string uniqueID)
+ public IModMetadata? Get(string uniqueID)
{
// normalize search ID
if (string.IsNullOrWhiteSpace(uniqueID))
@@ -76,14 +74,14 @@ namespace StardewModdingAPI.Framework
/// <summary>Get the mod metadata from one of its assemblies.</summary>
/// <param name="type">The type to check.</param>
/// <returns>Returns the mod name, or <c>null</c> if the type isn't part of a known mod.</returns>
- public IModMetadata GetFrom(Type type)
+ public IModMetadata? GetFrom(Type? type)
{
// null
if (type == null)
return null;
// known type
- string assemblyName = type.Assembly.FullName;
+ string assemblyName = type.Assembly.FullName!;
if (this.ModNamesByAssembly.ContainsKey(assemblyName))
return this.ModNamesByAssembly[assemblyName];
@@ -93,7 +91,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Get the friendly name for the closest assembly registered as a source of deprecation warnings.</summary>
/// <returns>Returns the source name, or <c>null</c> if no registered assemblies were found.</returns>
- public IModMetadata GetFromStack()
+ public IModMetadata? GetFromStack()
{
// get stack frames
StackTrace stack = new();
@@ -102,8 +100,8 @@ namespace StardewModdingAPI.Framework
// search stack for a source assembly
foreach (StackFrame frame in frames)
{
- MethodBase method = frame.GetMethod();
- IModMetadata mod = this.GetFrom(method.ReflectedType);
+ MethodBase? method = frame.GetMethod();
+ IModMetadata? mod = this.GetFrom(method?.ReflectedType);
if (mod != null)
return mod;
}
diff --git a/src/SMAPI/Framework/Reflection/ReflectedField.cs b/src/SMAPI/Framework/Reflection/ReflectedField.cs
index 921876b9..197a246e 100644
--- a/src/SMAPI/Framework/Reflection/ReflectedField.cs
+++ b/src/SMAPI/Framework/Reflection/ReflectedField.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Reflection;
@@ -15,8 +13,8 @@ namespace StardewModdingAPI.Framework.Reflection
/// <summary>The type that has the field.</summary>
private readonly Type ParentType;
- /// <summary>The object that has the instance field (if applicable).</summary>
- private readonly object Parent;
+ /// <summary>The object that has the instance field, or <c>null</c> for a static field.</summary>
+ private readonly object? Parent;
/// <summary>The display name shown in error messages.</summary>
private string DisplayName => $"{this.ParentType.FullName}::{this.FieldInfo.Name}";
@@ -34,12 +32,12 @@ namespace StardewModdingAPI.Framework.Reflection
*********/
/// <summary>Construct an instance.</summary>
/// <param name="parentType">The type that has the field.</param>
- /// <param name="obj">The object that has the instance field (if applicable).</param>
+ /// <param name="obj">The object that has the instance field, or <c>null</c> for a static field.</param>
/// <param name="field">The reflection metadata.</param>
/// <param name="isStatic">Whether the field is static.</param>
/// <exception cref="ArgumentNullException">The <paramref name="parentType"/> or <paramref name="field"/> is null.</exception>
/// <exception cref="ArgumentException">The <paramref name="obj"/> is null for a non-static field, or not null for a static field.</exception>
- public ReflectedField(Type parentType, object obj, FieldInfo field, bool isStatic)
+ public ReflectedField(Type parentType, object? obj, FieldInfo field, bool isStatic)
{
// validate
if (parentType == null)
@@ -58,11 +56,11 @@ namespace StardewModdingAPI.Framework.Reflection
}
/// <inheritdoc />
- public TValue GetValue()
+ public TValue? GetValue()
{
try
{
- return (TValue)this.FieldInfo.GetValue(this.Parent);
+ return (TValue?)this.FieldInfo.GetValue(this.Parent);
}
catch (InvalidCastException)
{
@@ -75,7 +73,7 @@ namespace StardewModdingAPI.Framework.Reflection
}
/// <inheritdoc />
- public void SetValue(TValue value)
+ public void SetValue(TValue? value)
{
try
{
diff --git a/src/SMAPI/Framework/Reflection/ReflectedMethod.cs b/src/SMAPI/Framework/Reflection/ReflectedMethod.cs
index 50f89b40..c245e843 100644
--- a/src/SMAPI/Framework/Reflection/ReflectedMethod.cs
+++ b/src/SMAPI/Framework/Reflection/ReflectedMethod.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Reflection;
@@ -14,8 +12,8 @@ namespace StardewModdingAPI.Framework.Reflection
/// <summary>The type that has the method.</summary>
private readonly Type ParentType;
- /// <summary>The object that has the instance method (if applicable).</summary>
- private readonly object Parent;
+ /// <summary>The object that has the instance method, or <c>null</c> for a static method.</summary>
+ private readonly object? Parent;
/// <summary>The display name shown in error messages.</summary>
private string DisplayName => $"{this.ParentType.FullName}::{this.MethodInfo.Name}";
@@ -33,12 +31,12 @@ namespace StardewModdingAPI.Framework.Reflection
*********/
/// <summary>Construct an instance.</summary>
/// <param name="parentType">The type that has the method.</param>
- /// <param name="obj">The object that has the instance method(if applicable).</param>
+ /// <param name="obj">The object that has the instance method, or <c>null</c> for a static method.</param>
/// <param name="method">The reflection metadata.</param>
/// <param name="isStatic">Whether the method is static.</param>
/// <exception cref="ArgumentNullException">The <paramref name="parentType"/> or <paramref name="method"/> is null.</exception>
/// <exception cref="ArgumentException">The <paramref name="obj"/> is null for a non-static method, or not null for a static method.</exception>
- public ReflectedMethod(Type parentType, object obj, MethodInfo method, bool isStatic)
+ public ReflectedMethod(Type parentType, object? obj, MethodInfo method, bool isStatic)
{
// validate
if (parentType == null)
@@ -57,10 +55,10 @@ namespace StardewModdingAPI.Framework.Reflection
}
/// <inheritdoc />
- public TValue Invoke<TValue>(params object[] arguments)
+ public TValue? Invoke<TValue>(params object?[] arguments)
{
// invoke method
- object result;
+ object? result;
try
{
result = this.MethodInfo.Invoke(this.Parent, arguments);
@@ -77,7 +75,7 @@ namespace StardewModdingAPI.Framework.Reflection
// cast return value
try
{
- return (TValue)result;
+ return (TValue?)result;
}
catch (InvalidCastException)
{
@@ -86,7 +84,7 @@ namespace StardewModdingAPI.Framework.Reflection
}
/// <inheritdoc />
- public void Invoke(params object[] arguments)
+ public void Invoke(params object?[] arguments)
{
// invoke method
try
diff --git a/src/SMAPI/Framework/Reflection/ReflectedProperty.cs b/src/SMAPI/Framework/Reflection/ReflectedProperty.cs
index a6d8c75c..638953a3 100644
--- a/src/SMAPI/Framework/Reflection/ReflectedProperty.cs
+++ b/src/SMAPI/Framework/Reflection/ReflectedProperty.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Reflection;
@@ -16,10 +14,10 @@ namespace StardewModdingAPI.Framework.Reflection
private readonly string DisplayName;
/// <summary>The underlying property getter.</summary>
- private readonly Func<TValue> GetMethod;
+ private readonly Func<TValue?>? GetMethod;
/// <summary>The underlying property setter.</summary>
- private readonly Action<TValue> SetMethod;
+ private readonly Action<TValue?>? SetMethod;
/*********
@@ -34,12 +32,12 @@ namespace StardewModdingAPI.Framework.Reflection
*********/
/// <summary>Construct an instance.</summary>
/// <param name="parentType">The type that has the property.</param>
- /// <param name="obj">The object that has the instance property (if applicable).</param>
+ /// <param name="obj">The object that has the instance property, or <c>null</c> for a static property.</param>
/// <param name="property">The reflection metadata.</param>
/// <param name="isStatic">Whether the property is static.</param>
/// <exception cref="ArgumentNullException">The <paramref name="parentType"/> or <paramref name="property"/> is null.</exception>
/// <exception cref="ArgumentException">The <paramref name="obj"/> is null for a non-static property, or not null for a static property.</exception>
- public ReflectedProperty(Type parentType, object obj, PropertyInfo property, bool isStatic)
+ public ReflectedProperty(Type parentType, object? obj, PropertyInfo property, bool isStatic)
{
// validate input
if (parentType == null)
@@ -58,13 +56,13 @@ namespace StardewModdingAPI.Framework.Reflection
this.PropertyInfo = property;
if (this.PropertyInfo.GetMethod != null)
- this.GetMethod = (Func<TValue>)Delegate.CreateDelegate(typeof(Func<TValue>), obj, this.PropertyInfo.GetMethod);
+ this.GetMethod = (Func<TValue?>)Delegate.CreateDelegate(typeof(Func<TValue?>), obj, this.PropertyInfo.GetMethod);
if (this.PropertyInfo.SetMethod != null)
- this.SetMethod = (Action<TValue>)Delegate.CreateDelegate(typeof(Action<TValue>), obj, this.PropertyInfo.SetMethod);
+ this.SetMethod = (Action<TValue?>)Delegate.CreateDelegate(typeof(Action<TValue?>), obj, this.PropertyInfo.SetMethod);
}
/// <inheritdoc />
- public TValue GetValue()
+ public TValue? GetValue()
{
if (this.GetMethod == null)
throw new InvalidOperationException($"The {this.DisplayName} property has no get method.");
@@ -84,7 +82,7 @@ namespace StardewModdingAPI.Framework.Reflection
}
/// <inheritdoc />
- public void SetValue(TValue value)
+ public void SetValue(TValue? value)
{
if (this.SetMethod == null)
throw new InvalidOperationException($"The {this.DisplayName} property has no set method.");
diff --git a/src/SMAPI/Framework/Rendering/SDisplayDevice.cs b/src/SMAPI/Framework/Rendering/SDisplayDevice.cs
index 8718bcb1..37996b0f 100644
--- a/src/SMAPI/Framework/Rendering/SDisplayDevice.cs
+++ b/src/SMAPI/Framework/Rendering/SDisplayDevice.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
@@ -27,7 +25,7 @@ namespace StardewModdingAPI.Framework.Rendering
/// <param name="tile">The tile to draw.</param>
/// <param name="location">The tile position to draw.</param>
/// <param name="layerDepth">The layer depth at which to draw.</param>
- public override void DrawTile(Tile tile, Location location, float layerDepth)
+ public override void DrawTile(Tile? tile, Location location, float layerDepth)
{
// identical to XnaDisplayDevice
if (tile == null)
@@ -58,7 +56,7 @@ namespace StardewModdingAPI.Framework.Rendering
/// <param name="tile">The tile being drawn.</param>
private SpriteEffects GetSpriteEffects(Tile tile)
{
- return tile.Properties.TryGetValue("@Flip", out PropertyValue propertyValue) && int.TryParse(propertyValue, out int value)
+ return tile.Properties.TryGetValue("@Flip", out PropertyValue? propertyValue) && int.TryParse(propertyValue, out int value)
? (SpriteEffects)value
: SpriteEffects.None;
}
@@ -67,7 +65,7 @@ namespace StardewModdingAPI.Framework.Rendering
/// <param name="tile">The tile being drawn.</param>
private float GetRotation(Tile tile)
{
- if (!tile.Properties.TryGetValue("@Rotation", out PropertyValue propertyValue) || !int.TryParse(propertyValue, out int value))
+ if (!tile.Properties.TryGetValue("@Rotation", out PropertyValue? propertyValue) || !int.TryParse(propertyValue, out int value))
return 0;
value %= 360;
diff --git a/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs b/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs
index 21edaedd..94b13378 100644
--- a/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs
+++ b/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -91,7 +89,7 @@ namespace StardewModdingAPI.Framework.Rendering
/// <param name="tile">The tile to draw.</param>
/// <param name="location">The tile position to draw.</param>
/// <param name="layerDepth">The layer depth at which to draw.</param>
- public virtual void DrawTile(Tile tile, Location location, float layerDepth)
+ public virtual void DrawTile(Tile? tile, Location location, float layerDepth)
{
if (tile == null)
return;
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index 7ca89eec..4fa7fe7b 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
@@ -48,10 +46,10 @@ namespace StardewModdingAPI.Framework
private readonly Action<string> ExitGameImmediately;
/// <summary>The initial override for <see cref="Input"/>. This value is null after initialization.</summary>
- private SInputState InitialInput;
+ private SInputState? InitialInput;
/// <summary>The initial override for <see cref="Multiplayer"/>. This value is null after initialization.</summary>
- private SMultiplayer InitialMultiplayer;
+ private SMultiplayer? InitialMultiplayer;
/// <summary>Raised when the instance is updating its state (roughly 60 times per second).</summary>
private readonly Action<SGame, GameTime, Action> OnUpdating;
@@ -70,7 +68,7 @@ namespace StardewModdingAPI.Framework
public Task NewDayTask => Game1._newDayTask;
/// <summary>Monitors the entire game state for changes.</summary>
- public WatcherCore Watchers { get; private set; }
+ public WatcherCore Watchers { get; private set; } = null!; // initialized on first update tick
/// <summary>A snapshot of the current <see cref="Watchers"/> state.</summary>
public WatcherSnapshot WatcherSnapshot { get; } = new();
@@ -94,7 +92,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Construct a content manager to read game content files.</summary>
/// <remarks>This must be static because the game accesses it before the <see cref="SGame"/> constructor is called.</remarks>
[NonInstancedStatic]
- public static Func<IServiceProvider, string, LocalizedContentManager> CreateContentManagerImpl;
+ public static Func<IServiceProvider, string, LocalizedContentManager>? CreateContentManagerImpl;
/*********
@@ -138,11 +136,10 @@ namespace StardewModdingAPI.Framework
/// <remarks>This is intended for use by <see cref="Keybind"/> and shouldn't be used directly in most cases.</remarks>
internal static SButtonState GetInputState(SButton button)
{
- SInputState input = Game1.input as SInputState;
- if (input == null)
+ if (Game1.input is not SInputState inputHandler)
throw new InvalidOperationException("SMAPI's input state is not in a ready state yet.");
- return input.GetState(button);
+ return inputHandler.GetState(button);
}
/// <inheritdoc />
@@ -172,13 +169,11 @@ namespace StardewModdingAPI.Framework
{
base.Initialize();
- // The game resets public static fields after the class is constructed (see
- // GameRunner.SetInstanceDefaults), so SMAPI needs to re-override them here.
+ // The game resets public static fields after the class is constructed (see GameRunner.SetInstanceDefaults), so SMAPI needs to re-override them here.
Game1.input = this.InitialInput;
Game1.multiplayer = this.InitialMultiplayer;
- // The Initial* fields should no longer be used after this point, since mods may
- // further override them after initialization.
+ // The Initial* fields should no longer be used after this point, since mods may further override them after initialization.
this.InitialInput = null;
this.InitialMultiplayer = null;
}
@@ -251,6 +246,7 @@ namespace StardewModdingAPI.Framework
Context.IsInDrawLoop = false;
}
+#nullable disable
/// <summary>Replicate the game's draw logic with some changes for SMAPI.</summary>
/// <param name="gameTime">A snapshot of the game timing state.</param>
/// <param name="target_screen">The render target, if any.</param>
@@ -258,6 +254,7 @@ namespace StardewModdingAPI.Framework
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator", Justification = "copied from game code as-is")]
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "copied from game code as-is")]
[SuppressMessage("ReSharper", "LocalVariableHidesMember", Justification = "copied from game code as-is")]
+ [SuppressMessage("ReSharper", "MergeIntoPattern", Justification = "copied from game code as-is")]
[SuppressMessage("ReSharper", "PossibleLossOfFraction", Justification = "copied from game code as-is")]
[SuppressMessage("ReSharper", "RedundantArgumentDefaultValue", Justification = "copied from game code as-is")]
[SuppressMessage("ReSharper", "RedundantBaseQualifier", Justification = "copied from game code as-is")]
@@ -265,8 +262,9 @@ namespace StardewModdingAPI.Framework
[SuppressMessage("ReSharper", "RedundantExplicitNullableCreation", Justification = "copied from game code as-is")]
[SuppressMessage("ReSharper", "RedundantTypeArgumentsOfMethod", Justification = "copied from game code as-is")]
[SuppressMessage("ReSharper", "IdentifierTypo", Justification = "copied from game code as-is")]
- [SuppressMessage("SMAPI.CommonErrors", "AvoidNetField", Justification = "copied from game code as-is")]
+ [SuppressMessage("ReSharper", "MergeIntoPattern", Justification = "copied from game code as-is")]
[SuppressMessage("SMAPI.CommonErrors", "AvoidImplicitNetFieldCast", Justification = "copied from game code as-is")]
+ [SuppressMessage("SMAPI.CommonErrors", "AvoidNetField", Justification = "copied from game code as-is")]
private void DrawImpl(GameTime gameTime, RenderTarget2D target_screen)
{
var events = this.Events;
@@ -952,5 +950,6 @@ namespace StardewModdingAPI.Framework
this.drawOverlays(Game1.spriteBatch);
Game1.PopUIMode();
}
+#nullable enable
}
}
diff --git a/src/SMAPI/Framework/SModHooks.cs b/src/SMAPI/Framework/SModHooks.cs
index 7941e102..a7736c8b 100644
--- a/src/SMAPI/Framework/SModHooks.cs
+++ b/src/SMAPI/Framework/SModHooks.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Threading.Tasks;
using StardewValley;
@@ -35,7 +33,7 @@ namespace StardewModdingAPI.Framework
/// <param name="action">The vanilla <see cref="Game1.newDayAfterFade"/> logic.</param>
public override void OnGame1_NewDayAfterFade(Action action)
{
- this.BeforeNewDayAfterFade?.Invoke();
+ this.BeforeNewDayAfterFade();
action();
}
diff --git a/src/SMAPI/Framework/SnapshotDiff.cs b/src/SMAPI/Framework/SnapshotDiff.cs
index eb2aebe1..d659d2b4 100644
--- a/src/SMAPI/Framework/SnapshotDiff.cs
+++ b/src/SMAPI/Framework/SnapshotDiff.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using StardewModdingAPI.Framework.StateTracking;
namespace StardewModdingAPI.Framework
@@ -15,10 +13,10 @@ namespace StardewModdingAPI.Framework
public bool IsChanged { get; private set; }
/// <summary>The previous value.</summary>
- public T Old { get; private set; }
+ public T? Old { get; private set; }
/// <summary>The current value.</summary>
- public T New { get; private set; }
+ public T? New { get; private set; }
/*********
diff --git a/src/SMAPI/Framework/SnapshotItemListDiff.cs b/src/SMAPI/Framework/SnapshotItemListDiff.cs
index 97942783..76060db2 100644
--- a/src/SMAPI/Framework/SnapshotItemListDiff.cs
+++ b/src/SMAPI/Framework/SnapshotItemListDiff.cs
@@ -1,6 +1,5 @@
-#nullable disable
-
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewModdingAPI.Events;
using StardewValley;
@@ -48,7 +47,7 @@ namespace StardewModdingAPI.Framework
/// <param name="stackSizes">The items with their previous stack sizes.</param>
/// <param name="changes">The inventory changes, or <c>null</c> if nothing changed.</param>
/// <returns>Returns whether anything changed.</returns>
- public static bool TryGetChanges(ISet<Item> added, ISet<Item> removed, IDictionary<Item, int> stackSizes, out SnapshotItemListDiff changes)
+ public static bool TryGetChanges(ISet<Item> added, ISet<Item> removed, IDictionary<Item, int> stackSizes, [NotNullWhen(true)] out SnapshotItemListDiff? changes)
{
KeyValuePair<Item, int>[] sizesChanged = stackSizes.Where(p => p.Key.Stack != p.Value).ToArray();
if (sizesChanged.Any() || added.Any() || removed.Any())
diff --git a/src/SMAPI/Framework/SnapshotListDiff.cs b/src/SMAPI/Framework/SnapshotListDiff.cs
index 1d585c15..90066af1 100644
--- a/src/SMAPI/Framework/SnapshotListDiff.cs
+++ b/src/SMAPI/Framework/SnapshotListDiff.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using StardewModdingAPI.Framework.StateTracking;
@@ -39,7 +37,7 @@ namespace StardewModdingAPI.Framework
/// <param name="isChanged">Whether the value changed since the last update.</param>
/// <param name="removed">The removed values.</param>
/// <param name="added">The added values.</param>
- public void Update(bool isChanged, IEnumerable<T> removed, IEnumerable<T> added)
+ public void Update(bool isChanged, IEnumerable<T>? removed, IEnumerable<T>? added)
{
this.IsChanged = isChanged;
diff --git a/src/SMAPI/Framework/StateTracking/ChestTracker.cs b/src/SMAPI/Framework/StateTracking/ChestTracker.cs
index 28335200..c33a7498 100644
--- a/src/SMAPI/Framework/StateTracking/ChestTracker.cs
+++ b/src/SMAPI/Framework/StateTracking/ChestTracker.cs
@@ -1,7 +1,6 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewModdingAPI.Framework.StateTracking.Comparers;
using StardewModdingAPI.Framework.StateTracking.FieldWatchers;
@@ -86,7 +85,7 @@ namespace StardewModdingAPI.Framework.StateTracking
/// <summary>Get the inventory changes since the last update, if anything changed.</summary>
/// <param name="changes">The inventory changes, or <c>null</c> if nothing changed.</param>
/// <returns>Returns whether anything changed.</returns>
- public bool TryGetInventoryChanges(out SnapshotItemListDiff changes)
+ public bool TryGetInventoryChanges([NotNullWhen(true)] out SnapshotItemListDiff? changes)
{
return SnapshotItemListDiff.TryGetChanges(added: this.Added, removed: this.Removed, stackSizes: this.StackSizes, out changes);
}
diff --git a/src/SMAPI/Framework/StateTracking/Comparers/EquatableComparer.cs b/src/SMAPI/Framework/StateTracking/Comparers/EquatableComparer.cs
index 987e1820..9d8559b4 100644
--- a/src/SMAPI/Framework/StateTracking/Comparers/EquatableComparer.cs
+++ b/src/SMAPI/Framework/StateTracking/Comparers/EquatableComparer.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@@ -17,7 +15,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Comparers
/// <returns>true if the specified objects are equal; otherwise, false.</returns>
/// <param name="x">The first object to compare.</param>
/// <param name="y">The second object to compare.</param>
- public bool Equals(T x, T y)
+ public bool Equals(T? x, T? y)
{
if (x == null)
return y == null;
diff --git a/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs b/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs
index f6b04583..41b17e10 100644
--- a/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs
+++ b/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@@ -16,7 +14,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Comparers
/// <returns>true if the specified objects are equal; otherwise, false.</returns>
/// <param name="x">The first object to compare.</param>
/// <param name="y">The second object to compare.</param>
- public bool Equals(T x, T y)
+ public bool Equals(T? x, T? y)
{
if (x == null)
return y == null;
diff --git a/src/SMAPI/Framework/StateTracking/Comparers/ObjectReferenceComparer.cs b/src/SMAPI/Framework/StateTracking/Comparers/ObjectReferenceComparer.cs
index 8d3a7eb9..e6ece854 100644
--- a/src/SMAPI/Framework/StateTracking/Comparers/ObjectReferenceComparer.cs
+++ b/src/SMAPI/Framework/StateTracking/Comparers/ObjectReferenceComparer.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@@ -16,7 +14,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Comparers
/// <returns>true if the specified objects are equal; otherwise, false.</returns>
/// <param name="x">The first object to compare.</param>
/// <param name="y">The second object to compare.</param>
- public bool Equals(T x, T y)
+ public bool Equals(T? x, T? y)
{
return object.ReferenceEquals(x, y);
}
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs
index 0d7f2ad2..f55e4cea 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using Netcode;
@@ -12,6 +10,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
/// <typeparam name="TSerialDict">The serializable dictionary type that can store the keys and values.</typeparam>
/// <typeparam name="TSelf">The net field instance type.</typeparam>
internal class NetDictionaryWatcher<TKey, TValue, TField, TSerialDict, TSelf> : BaseDisposableWatcher, IDictionaryWatcher<TKey, TValue>
+ where TKey : notnull
where TField : class, INetObject<INetSerializable>, new()
where TSerialDict : IDictionary<TKey, TValue>, new()
where TSelf : NetDictionary<TKey, TValue, TField, TSerialDict, TSelf>
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs
index 82e5387e..97aedca8 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
@@ -81,7 +79,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
/// <summary>A callback invoked when an entry is added or removed from the collection.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
- private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
@@ -90,8 +88,8 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
}
else
{
- TValue[] added = e.NewItems?.Cast<TValue>().ToArray();
- TValue[] removed = e.OldItems?.Cast<TValue>().ToArray();
+ TValue[]? added = e.NewItems?.Cast<TValue>().ToArray();
+ TValue[]? removed = e.OldItems?.Cast<TValue>().ToArray();
if (removed != null)
{
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs
index 0b99914c..c4a4d0b9 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -20,7 +18,8 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
/// <summary>Get a watcher which compares values using their <see cref="object.Equals(object)"/> method. This method should only be used when <see cref="ForEquatable{T}"/> won't work, since this doesn't validate whether they're comparable.</summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="getValue">Get the current value.</param>
- public static IValueWatcher<T> ForGenericEquality<T>(Func<T> getValue) where T : struct
+ public static IValueWatcher<T> ForGenericEquality<T>(Func<T> getValue)
+ where T : struct
{
return new ComparableWatcher<T>(getValue, new GenericEqualsComparer<T>());
}
@@ -28,7 +27,8 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
/// <summary>Get a watcher for an <see cref="IEquatable{T}"/> value.</summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="getValue">Get the current value.</param>
- public static IValueWatcher<T> ForEquatable<T>(Func<T> getValue) where T : IEquatable<T>
+ public static IValueWatcher<T> ForEquatable<T>(Func<T> getValue)
+ where T : IEquatable<T>
{
return new ComparableWatcher<T>(getValue, new EquatableComparer<T>());
}
@@ -79,7 +79,8 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
/// <summary>Get a watcher for a net collection.</summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="collection">The net collection.</param>
- public static ICollectionWatcher<T> ForNetCollection<T>(NetCollection<T> collection) where T : class, INetObject<INetSerializable>
+ public static ICollectionWatcher<T> ForNetCollection<T>(NetCollection<T> collection)
+ where T : class, INetObject<INetSerializable>
{
return new NetCollectionWatcher<T>(collection);
}
@@ -87,7 +88,8 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
/// <summary>Get a watcher for a net list.</summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="collection">The net list.</param>
- public static ICollectionWatcher<T> ForNetList<T>(NetList<T, NetRef<T>> collection) where T : class, INetObject<INetSerializable>
+ public static ICollectionWatcher<T> ForNetList<T>(NetList<T, NetRef<T>> collection)
+ where T : class, INetObject<INetSerializable>
{
return new NetListWatcher<T>(collection);
}
@@ -100,6 +102,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
/// <typeparam name="TSelf">The net field instance type.</typeparam>
/// <param name="field">The net field.</param>
public static NetDictionaryWatcher<TKey, TValue, TField, TSerialDict, TSelf> ForNetDictionary<TKey, TValue, TField, TSerialDict, TSelf>(NetDictionary<TKey, TValue, TField, TSerialDict, TSelf> field)
+ where TKey : notnull
where TField : class, INetObject<INetSerializable>, new()
where TSerialDict : IDictionary<TKey, TValue>, new()
where TSelf : NetDictionary<TKey, TValue, TField, TSerialDict, TSelf>
diff --git a/src/SMAPI/Framework/StateTracking/LocationTracker.cs b/src/SMAPI/Framework/StateTracking/LocationTracker.cs
index 9c2ff7f0..ff72a19b 100644
--- a/src/SMAPI/Framework/StateTracking/LocationTracker.cs
+++ b/src/SMAPI/Framework/StateTracking/LocationTracker.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Linq;
@@ -132,20 +130,20 @@ namespace StardewModdingAPI.Framework.StateTracking
private void UpdateChestWatcherList(IEnumerable<KeyValuePair<Vector2, SObject>> added, IEnumerable<KeyValuePair<Vector2, SObject>> removed)
{
// remove unused watchers
- foreach (KeyValuePair<Vector2, SObject> pair in removed)
+ foreach ((Vector2 tile, SObject? obj) in removed)
{
- if (pair.Value is Chest && this.ChestWatchers.TryGetValue(pair.Key, out ChestTracker watcher))
+ if (obj is Chest && this.ChestWatchers.TryGetValue(tile, out ChestTracker? watcher))
{
watcher.Dispose();
- this.ChestWatchers.Remove(pair.Key);
+ this.ChestWatchers.Remove(tile);
}
}
// add new watchers
- foreach (KeyValuePair<Vector2, SObject> pair in added)
+ foreach ((Vector2 tile, SObject? obj) in added)
{
- if (pair.Value is Chest chest && !this.ChestWatchers.ContainsKey(pair.Key))
- this.ChestWatchers.Add(pair.Key, new ChestTracker(chest));
+ if (obj is Chest chest && !this.ChestWatchers.ContainsKey(tile))
+ this.ChestWatchers.Add(tile, new ChestTracker(chest));
}
}
}
diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs
index 3d13f92b..0d0469d7 100644
--- a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs
+++ b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using StardewValley;
@@ -70,7 +68,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots
this.ChestItems.Clear();
foreach (ChestTracker tracker in watcher.ChestWatchers.Values)
{
- if (tracker.TryGetInventoryChanges(out SnapshotItemListDiff changes))
+ if (tracker.TryGetInventoryChanges(out SnapshotItemListDiff? changes))
this.ChestItems[tracker.Chest] = changes;
}
}
diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/WatcherSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/WatcherSnapshot.cs
index 1d43ef26..27a891de 100644
--- a/src/SMAPI/Framework/StateTracking/Snapshots/WatcherSnapshot.cs
+++ b/src/SMAPI/Framework/StateTracking/Snapshots/WatcherSnapshot.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using Microsoft.Xna.Framework;
using StardewValley;
using StardewValley.Menus;
@@ -16,7 +14,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots
public SnapshotDiff<Point> WindowSize { get; } = new();
/// <summary>Tracks changes to the current player.</summary>
- public PlayerSnapshot CurrentPlayer { get; private set; }
+ public PlayerSnapshot? CurrentPlayer { get; private set; }
/// <summary>Tracks changes to the time of day (in 24-hour military format).</summary>
public SnapshotDiff<int> Time { get; } = new();
@@ -56,7 +54,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots
// update snapshots
this.WindowSize.Update(watchers.WindowSizeWatcher);
this.Locale.Update(watchers.LocaleWatcher);
- this.CurrentPlayer?.Update(watchers.CurrentPlayerTracker);
+ this.CurrentPlayer?.Update(watchers.CurrentPlayerTracker!);
this.Time.Update(watchers.TimeWatcher);
this.SaveID.Update(watchers.SaveIdWatcher);
this.Locations.Update(watchers.LocationsWatcher);
diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs
index 88aac0df..59f94942 100644
--- a/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs
+++ b/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using System.Linq;
using StardewModdingAPI.Framework.StateTracking.Comparers;
@@ -44,7 +42,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots
// update locations
foreach (LocationTracker locationWatcher in watcher.Locations)
{
- if (!this.LocationsDict.TryGetValue(locationWatcher.Location, out LocationSnapshot snapshot))
+ if (!this.LocationsDict.TryGetValue(locationWatcher.Location, out LocationSnapshot? snapshot))
this.LocationsDict[locationWatcher.Location] = snapshot = new LocationSnapshot(locationWatcher.Location);
snapshot.Update(locationWatcher);
diff --git a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs
index ab02d7d5..817a6011 100644
--- a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs
+++ b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
@@ -27,10 +25,10 @@ namespace StardewModdingAPI.Framework.StateTracking
private readonly ICollectionWatcher<GameLocation> VolcanoLocationListWatcher;
/// <summary>A lookup of the tracked locations.</summary>
- private IDictionary<GameLocation, LocationTracker> LocationDict { get; } = new Dictionary<GameLocation, LocationTracker>(new ObjectReferenceComparer<GameLocation>());
+ private Dictionary<GameLocation, LocationTracker> LocationDict { get; } = new(new ObjectReferenceComparer<GameLocation>());
/// <summary>A lookup of registered buildings and their indoor location.</summary>
- private readonly IDictionary<Building, GameLocation> BuildingIndoors = new Dictionary<Building, GameLocation>(new ObjectReferenceComparer<Building>());
+ private readonly Dictionary<Building, GameLocation?> BuildingIndoors = new(new ObjectReferenceComparer<Building>());
/*********
@@ -101,10 +99,9 @@ namespace StardewModdingAPI.Framework.StateTracking
}
// detect building interiors changed (e.g. construction completed)
- foreach (KeyValuePair<Building, GameLocation> pair in this.BuildingIndoors.Where(p => !object.Equals(p.Key.indoors.Value, p.Value)))
+ foreach ((Building building, GameLocation? oldIndoors) in this.BuildingIndoors.Where(p => !object.Equals(p.Key.indoors.Value, p.Value)))
{
- GameLocation oldIndoors = pair.Value;
- GameLocation newIndoors = pair.Key.indoors.Value;
+ GameLocation? newIndoors = building.indoors.Value;
if (oldIndoors != null)
this.Added.Add(oldIndoors);
@@ -189,19 +186,19 @@ namespace StardewModdingAPI.Framework.StateTracking
****/
/// <summary>Add the given building.</summary>
/// <param name="building">The building to add.</param>
- public void Add(Building building)
+ public void Add(Building? building)
{
if (building == null)
return;
- GameLocation indoors = building.indoors.Value;
+ GameLocation? indoors = building.indoors.Value;
this.BuildingIndoors[building] = indoors;
this.Add(indoors);
}
/// <summary>Add the given location.</summary>
/// <param name="location">The location to add.</param>
- public void Add(GameLocation location)
+ public void Add(GameLocation? location)
{
if (location == null)
return;
@@ -220,7 +217,7 @@ namespace StardewModdingAPI.Framework.StateTracking
/// <summary>Remove the given building.</summary>
/// <param name="building">The building to remove.</param>
- public void Remove(Building building)
+ public void Remove(Building? building)
{
if (building == null)
return;
@@ -231,12 +228,12 @@ namespace StardewModdingAPI.Framework.StateTracking
/// <summary>Remove the given location.</summary>
/// <param name="location">The location to remove.</param>
- public void Remove(GameLocation location)
+ public void Remove(GameLocation? location)
{
if (location == null)
return;
- if (this.LocationDict.TryGetValue(location, out LocationTracker watcher))
+ if (this.LocationDict.TryGetValue(location, out LocationTracker? watcher))
{
// track change
this.Removed.Add(location);
diff --git a/src/SMAPI/Framework/Translator.cs b/src/SMAPI/Framework/Translator.cs
index 144b043c..794e3cde 100644
--- a/src/SMAPI/Framework/Translator.cs
+++ b/src/SMAPI/Framework/Translator.cs
@@ -1,7 +1,6 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;
@@ -23,7 +22,7 @@ namespace StardewModdingAPI.Framework
/*********
** Accessors
*********/
- /// <summary>The current locale.</summary>
+ /// <summary>The current locale code like <c>fr-FR</c>, or an empty string for English.</summary>
public string Locale { get; private set; }
/// <summary>The game's current language code.</summary>
@@ -39,9 +38,10 @@ namespace StardewModdingAPI.Framework
this.SetLocale(string.Empty, LocalizedContentManager.LanguageCode.en);
}
- /// <summary>Set the current locale and precache translations.</summary>
+ /// <summary>Set the current locale and pre-cache translations.</summary>
/// <param name="locale">The current locale.</param>
/// <param name="localeEnum">The game's current language code.</param>
+ [MemberNotNull(nameof(Translator.ForLocale), nameof(Translator.Locale))]
public void SetLocale(string locale, LocalizedContentManager.LanguageCode localeEnum)
{
this.Locale = locale.ToLower().Trim();
@@ -50,7 +50,7 @@ namespace StardewModdingAPI.Framework
this.ForLocale = new Dictionary<string, Translation>(StringComparer.OrdinalIgnoreCase);
foreach (string key in this.GetAllKeysRaw())
{
- string text = this.GetRaw(key, locale, withFallback: true);
+ string? text = this.GetRaw(key, locale, withFallback: true);
this.ForLocale.Add(key, new Translation(this.Locale, key, text));
}
}
@@ -65,7 +65,7 @@ namespace StardewModdingAPI.Framework
/// <param name="key">The translation key.</param>
public Translation Get(string key)
{
- this.ForLocale.TryGetValue(key, out Translation translation);
+ this.ForLocale.TryGetValue(key, out Translation? translation);
return translation ?? new Translation(this.Locale, key, null);
}
@@ -87,7 +87,7 @@ namespace StardewModdingAPI.Framework
foreach (var localeSet in this.All)
{
string locale = localeSet.Key;
- string text = this.GetRaw(key, locale, withFallback);
+ string? text = this.GetRaw(key, locale, withFallback);
if (text != null)
translations[locale] = new Translation(locale, key, text);
@@ -128,13 +128,13 @@ namespace StardewModdingAPI.Framework
/// <param name="key">The translation key.</param>
/// <param name="locale">The locale to get.</param>
/// <param name="withFallback">Whether to add duplicate translations for locale fallback. For example, if a translation is defined in <c>default.json</c> but not <c>fr.json</c>, setting this to true will add a <c>fr</c> entry which duplicates the default text.</param>
- private string GetRaw(string key, string locale, bool withFallback)
+ private string? GetRaw(string key, string locale, bool withFallback)
{
foreach (string next in this.GetRelevantLocales(locale))
{
- string translation = null;
+ string? translation = null;
bool hasTranslation =
- this.All.TryGetValue(next, out IDictionary<string, string> translations)
+ this.All.TryGetValue(next, out IDictionary<string, string>? translations)
&& translations.TryGetValue(key, out translation);
if (hasTranslation)
diff --git a/src/SMAPI/Framework/Utilities/TickCacheDictionary.cs b/src/SMAPI/Framework/Utilities/TickCacheDictionary.cs
index 94ce0069..20d206e2 100644
--- a/src/SMAPI/Framework/Utilities/TickCacheDictionary.cs
+++ b/src/SMAPI/Framework/Utilities/TickCacheDictionary.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
@@ -9,6 +7,7 @@ namespace StardewModdingAPI.Framework.Utilities
/// <typeparam name="TKey">The dictionary key type.</typeparam>
/// <typeparam name="TValue">The dictionary value type.</typeparam>
internal class TickCacheDictionary<TKey, TValue>
+ where TKey : notnull
{
/*********
** Fields
@@ -36,7 +35,7 @@ namespace StardewModdingAPI.Framework.Utilities
}
// fetch value
- if (!this.Cache.TryGetValue(cacheKey, out TValue cached))
+ if (!this.Cache.TryGetValue(cacheKey, out TValue? cached))
this.Cache[cacheKey] = cached = get();
return cached;
}
diff --git a/src/SMAPI/Framework/WatcherCore.cs b/src/SMAPI/Framework/WatcherCore.cs
index bd8d3367..5e20ac7b 100644
--- a/src/SMAPI/Framework/WatcherCore.cs
+++ b/src/SMAPI/Framework/WatcherCore.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
@@ -29,7 +27,7 @@ namespace StardewModdingAPI.Framework
public readonly IValueWatcher<Point> WindowSizeWatcher;
/// <summary>Tracks changes to the current player.</summary>
- public PlayerTracker CurrentPlayerTracker;
+ public PlayerTracker? CurrentPlayerTracker;
/// <summary>Tracks changes to the time of day (in 24-hour military format).</summary>
public readonly IValueWatcher<int> TimeWatcher;