enable nullable annotations in bundled mods (#837)

This commit is contained in:
Jesse Plamondon-Willard 2022-04-13 22:06:07 -04:00
parent aa7b0caf46
commit 2765e3f9b3
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
45 changed files with 92 additions and 173 deletions

View File

@ -1,8 +1,7 @@
#nullable disable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands
@ -54,7 +53,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands
/// <param name="value">The parsed value.</param>
/// <param name="required">Whether to show an error if the argument is missing.</param>
/// <param name="oneOf">Require that the argument match one of the given values (case-insensitive).</param>
public bool TryGet(int index, string name, out string value, bool required = true, string[] oneOf = null)
public bool TryGet(int index, string name, [NotNullWhen(true)] out string? value, bool required = true, string[]? oneOf = null)
{
value = null;
@ -88,7 +87,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands
value = 0;
// get argument
if (!this.TryGet(index, name, out string raw, required))
if (!this.TryGet(index, name, out string? raw, required))
return false;
// parse

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;

View File

@ -1,5 +1,3 @@
#nullable disable
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands
{
/// <summary>A console command to register.</summary>

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@ -25,7 +23,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// get fix ID
if (!args.TryGet(0, "fix_id", out string rawFixId, required: false))
if (!args.TryGet(0, "fix_id", out string? rawFixId, required: false))
{
monitor.Log("Invalid usage. Type 'help apply_save_fix' for details.", LogLevel.Error);
return;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Linq;
using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData;
@ -42,7 +40,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
}
// read arguments
if (!args.TryGet(0, "item type", out string type, oneOf: this.ValidTypes))
if (!args.TryGet(0, "item type", out string? type, oneOf: this.ValidTypes))
return;
if (!args.TryGetInt(2, "count", out int count, min: 1, required: false))
count = 1;
@ -50,7 +48,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
quality = Object.lowQuality;
// find matching item
SearchableItem match = Enum.TryParse(type, true, out ItemType itemType)
SearchableItem? match = Enum.TryParse(type, true, out ItemType itemType)
? this.FindItemByID(monitor, args, itemType)
: this.FindItemByName(monitor, args);
if (match == null)
@ -78,14 +76,14 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="args">The command arguments.</param>
/// <param name="type">The item type.</param>
private SearchableItem FindItemByID(IMonitor monitor, ArgumentParser args, ItemType type)
private SearchableItem? FindItemByID(IMonitor monitor, ArgumentParser args, ItemType type)
{
// read arguments
if (!args.TryGetInt(1, "item ID", out int id, min: 0))
return null;
// find matching item
SearchableItem item = this.Items.GetAll().FirstOrDefault(p => p.Type == type && p.ID == id);
SearchableItem? item = this.Items.GetAll().FirstOrDefault(p => p.Type == type && p.ID == id);
if (item == null)
monitor.Log($"There's no {type} item with ID {id}.", LogLevel.Error);
return item;
@ -94,10 +92,10 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
/// <summary>Get a matching item by its name.</summary>
/// <param name="monitor">Writes messages to the console and log file.</param>
/// <param name="args">The command arguments.</param>
private SearchableItem FindItemByName(IMonitor monitor, ArgumentParser args)
private SearchableItem? FindItemByName(IMonitor monitor, ArgumentParser args)
{
// read arguments
if (!args.TryGet(1, "item name", out string name))
if (!args.TryGet(1, "item name", out string? name))
return null;
// find matching items

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData;

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@ -63,15 +61,14 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
private IEnumerable<SearchableItem> GetItems(string[] searchWords)
{
// normalize search term
searchWords = searchWords?.Where(word => !string.IsNullOrWhiteSpace(word)).ToArray();
if (searchWords?.Any() == false)
searchWords = null;
searchWords = searchWords.Where(word => !string.IsNullOrWhiteSpace(word)).ToArray();
bool getAll = !searchWords.Any();
// find matches
return (
from item in this.Items.GetAll()
let term = $"{item.ID}|{item.Type}|{item.Name}|{item.DisplayName}"
where searchWords == null || searchWords.All(word => term.IndexOf(word, StringComparison.CurrentCultureIgnoreCase) != -1)
where getAll || searchWords.All(word => term.IndexOf(word, StringComparison.CurrentCultureIgnoreCase) != -1)
select item
);
}

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using Microsoft.Xna.Framework;
using StardewValley;
@ -24,9 +22,9 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// parse arguments
if (!args.TryGet(0, "target", out string target, oneOf: new[] { "hair", "eyes", "pants" }))
if (!args.TryGet(0, "target", out string? target, oneOf: new[] { "hair", "eyes", "pants" }))
return;
if (!args.TryGet(1, "color", out string rawColor))
if (!args.TryGet(1, "color", out string? rawColor))
return;
// parse color

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections;
using System.Collections.Generic;
@ -37,7 +35,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
}
// parse arguments
if (!args.TryGet(0, "farm type", out string farmType))
if (!args.TryGet(0, "farm type", out string? farmType))
return;
bool isVanillaId = int.TryParse(farmType, out int vanillaId) && vanillaId is (>= 0 and < Farm.layout_max);
@ -112,7 +110,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
return;
}
if (!this.GetCustomFarmTypes().TryGetValue(id, out ModFarmType customFarmType))
if (!this.GetCustomFarmTypes().TryGetValue(id, out ModFarmType? customFarmType))
{
monitor.Log($"Invalid farm type '{id}'. Enter `help set_farm_type` for more info.", LogLevel.Error);
return;
@ -125,7 +123,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
/// <summary>Change the farm type.</summary>
/// <param name="type">The farm type ID.</param>
/// <param name="customFarmData">The custom farm type data, if applicable.</param>
private void SetFarmType(int type, ModFarmType customFarmData)
private void SetFarmType(int type, ModFarmType? customFarmData)
{
// set flags
Game1.whichFarm = type;
@ -138,7 +136,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
farm.updateWarps();
// clear spouse area cache to avoid errors
FieldInfo cacheField = farm.GetType().GetField("_baseSpouseAreaTiles", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
FieldInfo? cacheField = farm.GetType().GetField("_baseSpouseAreaTiles", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (cacheField == null)
throw new InvalidOperationException("Failed to access '_baseSpouseAreaTiles' field to clear spouse area cache.");
if (cacheField.GetValue(farm) is not IDictionary cache)
@ -166,7 +164,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
/// <param name="type">The farm type.</param>
private string GetVanillaName(int type)
{
string translationKey = type switch
string? translationKey = type switch
{
Farm.default_layout => "Character_FarmStandard",
Farm.riverlands_layout => "Character_FarmFishing",
@ -199,7 +197,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
****/
/// <summary>Get the display name for a custom farm type.</summary>
/// <param name="farmType">The custom farm type.</param>
private string GetCustomName(ModFarmType farmType)
private string? GetCustomName(ModFarmType? farmType)
{
if (string.IsNullOrWhiteSpace(farmType?.TooltipStringPath))
return farmType?.ID;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using StardewValley;
@ -23,9 +21,9 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// parse arguments
if (!args.TryGet(0, "target", out string target, oneOf: new[] { "player", "farm" }))
if (!args.TryGet(0, "target", out string? target, oneOf: new[] { "player", "farm" }))
return;
args.TryGet(1, "name", out string name, required: false);
args.TryGet(1, "name", out string? name, required: false);
// handle
switch (target)

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using StardewValley;
@ -23,7 +21,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
public override void Handle(IMonitor monitor, string command, ArgumentParser args)
{
// parse arguments
if (!args.TryGet(0, "target", out string target, oneOf: new[] { "hair", "shirt", "acc", "skin", "shoe", "swim", "gender" }))
if (!args.TryGet(0, "target", out string? target, oneOf: new[] { "hair", "shirt", "acc", "skin", "shoe", "swim", "gender" }))
return;
if (!args.TryGetInt(1, "style ID", out int styleID))
return;

View File

@ -1,8 +1,7 @@
#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Xna.Framework;
using StardewValley;
using StardewValley.Locations;
using StardewValley.Objects;
@ -53,13 +52,13 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
}
// parse arguments
if (!args.TryGet(0, "location", out string locationName, required: true))
if (!args.TryGet(0, "location", out string? locationName, required: true))
return;
if (!args.TryGet(1, "object type", out string type, required: true, oneOf: this.ValidTypes))
if (!args.TryGet(1, "object type", out string? type, required: true, oneOf: this.ValidTypes))
return;
// get target location
GameLocation location = Game1.locations.FirstOrDefault(p => p.Name != null && p.Name.Equals(locationName, StringComparison.OrdinalIgnoreCase));
GameLocation? location = Game1.locations.FirstOrDefault(p => p.Name != null && p.Name.Equals(locationName, StringComparison.OrdinalIgnoreCase));
if (location == null && locationName == "current")
location = Game1.currentLocation;
if (location == null)
@ -168,11 +167,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
int removed = 0;
foreach (var pair in location.Objects.Pairs.ToArray())
foreach ((Vector2 tile, SObject? obj) in location.Objects.Pairs.ToArray())
{
if (shouldRemove(pair.Value))
if (shouldRemove(obj))
{
location.Objects.Remove(pair.Key);
location.Objects.Remove(tile);
removed++;
}
}
@ -188,11 +187,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
int removed = 0;
foreach (var pair in location.terrainFeatures.Pairs.ToArray())
foreach ((Vector2 tile, TerrainFeature? feature) in location.terrainFeatures.Pairs.ToArray())
{
if (shouldRemove(pair.Value))
if (shouldRemove(feature))
{
location.terrainFeatures.Remove(pair.Key);
location.terrainFeatures.Remove(tile);
removed++;
}
}
@ -228,7 +227,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
int removed = 0;
foreach (var clump in location.resourceClumps.Where(shouldRemove).ToArray())
foreach (ResourceClump clump in location.resourceClumps.Where(shouldRemove).ToArray())
{
location.resourceClumps.Remove(clump);
removed++;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using StardewValley;
using StardewValley.Locations;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Linq;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewModdingAPI.Utilities;

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewModdingAPI.Utilities;
@ -39,7 +37,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
}
// parse arguments
if (!args.TryGet(0, "season", out string season, oneOf: this.ValidSeasons))
if (!args.TryGet(0, "season", out string? season, oneOf: this.ValidSeasons))
return;
// handle

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Xna.Framework;

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewModdingAPI.Utilities;

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using StardewValley;

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@ -33,7 +31,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
/// <param name="itemTypes">The item types to fetch (or null for any type).</param>
/// <param name="includeVariants">Whether to include flavored variants like "Sunflower Honey".</param>
[SuppressMessage("ReSharper", "AccessToModifiedClosure", Justification = "TryCreate invokes the lambda immediately.")]
public IEnumerable<SearchableItem> GetAll(ItemType[] itemTypes = null, bool includeVariants = true)
public IEnumerable<SearchableItem> GetAll(ItemType[]? itemTypes = null, bool includeVariants = true)
{
//
//
@ -45,9 +43,9 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
//
//
IEnumerable<SearchableItem> GetAllRaw()
IEnumerable<SearchableItem?> GetAllRaw()
{
HashSet<ItemType> types = itemTypes?.Any() == true ? new HashSet<ItemType>(itemTypes) : null;
HashSet<ItemType>? types = itemTypes?.Any() == true ? new HashSet<ItemType>(itemTypes) : null;
bool ShouldGet(ItemType type) => types == null || types.Contains(type);
// get tools
@ -134,7 +132,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
{
foreach (int id in Game1.objectInformation.Keys)
{
string[] fields = Game1.objectInformation[id]?.Split('/');
string[]? fields = Game1.objectInformation[id]?.Split('/');
// ring
if (id != 801 && fields?.Length >= 4 && fields[3] == "Ring") // 801 = wedding ring, which isn't an equippable ring
@ -148,7 +146,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
{
if (ShouldGet(ItemType.Object))
{
foreach (SearchableItem journalScrap in this.GetSecretNotes(isJournalScrap: true))
foreach (SearchableItem? journalScrap in this.GetSecretNotes(isJournalScrap: true))
yield return journalScrap;
}
}
@ -158,7 +156,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
{
if (ShouldGet(ItemType.Object))
{
foreach (SearchableItem secretNote in this.GetSecretNotes(isJournalScrap: false))
foreach (SearchableItem? secretNote in this.GetSecretNotes(isJournalScrap: false))
yield return secretNote;
}
}
@ -167,7 +165,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
else if (ShouldGet(ItemType.Object))
{
// spawn main item
SObject item = null;
SObject? item = null;
yield return this.TryCreate(ItemType.Object, id, p =>
{
return item = (p.ID == 812 // roe
@ -181,7 +179,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
// flavored items
if (includeVariants)
{
foreach (SearchableItem variant in this.GetFlavoredObjectVariants(item))
foreach (SearchableItem? variant in this.GetFlavoredObjectVariants(item))
yield return variant;
}
}
@ -189,7 +187,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
}
}
return GetAllRaw().Where(p => p != null);
return (
from item in GetAllRaw()
where item != null
select item
);
}
@ -199,7 +201,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
/// <summary>Get the individual secret note or journal scrap items.</summary>
/// <param name="isJournalScrap">Whether to get journal scraps.</param>
/// <remarks>Derived from <see cref="GameLocation.tryToCreateUnseenSecretNote"/>.</remarks>
private IEnumerable<SearchableItem> GetSecretNotes(bool isJournalScrap)
private IEnumerable<SearchableItem?> GetSecretNotes(bool isJournalScrap)
{
// get base item ID
int baseId = isJournalScrap ? 842 : 79;
@ -235,7 +237,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
/// <summary>Get flavored variants of a base item (like Blueberry Wine for Blueberry), if any.</summary>
/// <param name="item">A sample of the base item.</param>
private IEnumerable<SearchableItem> GetFlavoredObjectVariants(SObject item)
private IEnumerable<SearchableItem?> GetFlavoredObjectVariants(SObject item)
{
int id = item.ParentSheetIndex;
@ -305,9 +307,12 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
foreach (var pair in Game1.objectInformation)
{
// get input
SObject input = this.TryCreate(ItemType.Object, pair.Key, p => new SObject(p.ID, 1))?.Item as SObject;
var inputTags = input?.GetContextTags();
if (inputTags?.Any() != true)
SObject? input = this.TryCreate(ItemType.Object, pair.Key, p => new SObject(p.ID, 1))?.Item as SObject;
if (input == null)
continue;
HashSet<string> inputTags = input.GetContextTags();
if (!inputTags.Any())
continue;
// check if roe-producing fish
@ -315,7 +320,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
continue;
// yield roe
SObject roe = null;
SObject? roe = null;
Color color = this.GetRoeColor(input);
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 7 + id, _ =>
{
@ -372,6 +377,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
/// <typeparam name="TValue">The asset value type.</typeparam>
/// <param name="assetName">The data asset name.</param>
private Dictionary<TKey, TValue> TryLoad<TKey, TValue>(string assetName)
where TKey : notnull
{
try
{
@ -388,7 +394,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
/// <param name="type">The item type.</param>
/// <param name="id">The unique ID (if different from the item's parent sheet index).</param>
/// <param name="createItem">Create an item instance.</param>
private SearchableItem TryCreate(ItemType type, int id, Func<SearchableItem, Item> createItem)
private SearchableItem? TryCreate(ItemType type, int id, Func<SearchableItem, Item> createItem)
{
try
{

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@ -15,13 +13,13 @@ namespace StardewModdingAPI.Mods.ConsoleCommands
** Fields
*********/
/// <summary>The commands to handle.</summary>
private IConsoleCommand[] Commands;
private IConsoleCommand[] Commands = null!;
/// <summary>The commands which may need to handle update ticks.</summary>
private IConsoleCommand[] UpdateHandlers;
private IConsoleCommand[] UpdateHandlers = null!;
/// <summary>The commands which may need to handle input.</summary>
private IConsoleCommand[] InputHandlers;
private IConsoleCommand[] InputHandlers = null!;
/*********
@ -52,7 +50,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands
/// <summary>The method invoked when a button is pressed.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
private void OnButtonPressed(object? sender, ButtonPressedEventArgs e)
{
foreach (IConsoleCommand command in this.InputHandlers)
command.OnButtonPressed(this.Monitor, e.Button);
@ -61,7 +59,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands
/// <summary>The method invoked when the game updates its state.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnUpdateTicked(object sender, EventArgs e)
private void OnUpdateTicked(object? sender, EventArgs e)
{
foreach (IConsoleCommand command in this.UpdateHandlers)
command.OnUpdated(this.Monitor);
@ -83,7 +81,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands
return (
from type in this.GetType().Assembly.GetTypes()
where !type.IsAbstract && typeof(IConsoleCommand).IsAssignableFrom(type)
select (IConsoleCommand)Activator.CreateInstance(type)
select (IConsoleCommand)Activator.CreateInstance(type)!
);
}
}

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Reflection;
using StardewModdingAPI.Events;
@ -59,7 +57,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler
/// <summary>The method invoked when a save is loaded.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnSaveLoaded(object sender, SaveLoadedEventArgs e)
private void OnSaveLoaded(object? sender, SaveLoadedEventArgs e)
{
// show in-game warning for removed save content
if (this.IsSaveContentRemoved)
@ -82,7 +80,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler
MethodInfo getMonitorForGame = coreType.GetMethod("GetMonitorForGame")
?? throw new InvalidOperationException("Can't access the SMAPI's 'GetMonitorForGame' method. This mod may not work correctly.");
return (IMonitor)getMonitorForGame.Invoke(core, Array.Empty<object>()) ?? this.Monitor;
return (IMonitor?)getMonitorForGame.Invoke(core, Array.Empty<object>()) ?? this.Monitor;
}
}
}

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
@ -19,10 +17,10 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
** Fields
*********/
/// <summary>Writes messages to the console and log file on behalf of the game.</summary>
private static IMonitor MonitorForGame;
private static IMonitor MonitorForGame = null!;
/// <summary>Simplifies access to private code.</summary>
private static IReflectionHelper Reflection;
private static IReflectionHelper Reflection = null!;
/*********
@ -56,12 +54,12 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <param name="speaker">The NPC for which the dialogue is being parsed.</param>
/// <param name="__exception">The exception thrown by the wrapped method, if any.</param>
/// <returns>Returns the exception to throw, if any.</returns>
private static Exception Finalize_Constructor(Dialogue __instance, string masterDialogue, NPC speaker, Exception __exception)
private static Exception? Finalize_Constructor(Dialogue __instance, string masterDialogue, NPC? speaker, Exception? __exception)
{
if (__exception != null)
{
// log message
string name = !string.IsNullOrWhiteSpace(speaker?.Name) ? speaker.Name : null;
string? name = !string.IsNullOrWhiteSpace(speaker?.Name) ? speaker.Name : null;
DialoguePatcher.MonitorForGame.Log($"Failed parsing dialogue string{(name != null ? $" for {name}" : "")}:\n{masterDialogue}\n{__exception.GetLogSummary()}", LogLevel.Error);
// set default dialogue

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
@ -18,7 +16,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
** Fields
*********/
/// <summary>Writes messages to the console and log file on behalf of the game.</summary>
private static IMonitor MonitorForGame;
private static IMonitor MonitorForGame = null!;
/*********

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
@ -19,8 +17,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
** Fields
*********/
/// <summary>Writes messages to the console and log file on behalf of the game.</summary>
private static IMonitor MonitorForGame;
private static IMonitor MonitorForGame = null!;
/*********
** Public methods
@ -54,7 +51,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <param name="precondition">The precondition to be parsed.</param>
/// <param name="__exception">The exception thrown by the wrapped method, if any.</param>
/// <returns>Returns the exception to throw, if any.</returns>
private static Exception Finalize_CheckEventPrecondition(ref int __result, string precondition, Exception __exception)
private static Exception? Finalize_CheckEventPrecondition(ref int __result, string precondition, Exception? __exception)
{
if (__exception != null)
{
@ -70,7 +67,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <param name="map">The map whose tilesheets to update.</param>
/// <param name="__exception">The exception thrown by the wrapped method, if any.</param>
/// <returns>Returns the exception to throw, if any.</returns>
private static Exception Finalize_UpdateSeasonalTileSheets(GameLocation __instance, Map map, Exception __exception)
private static Exception? Finalize_UpdateSeasonalTileSheets(GameLocation __instance, Map map, Exception? __exception)
{
if (__exception != null)
GameLocationPatcher.MonitorForGame.Log($"Failed updating seasonal tilesheets for location '{__instance.NameOrUniqueName}': \n{__exception}", LogLevel.Error);

View File

@ -1,5 +1,3 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
using StardewModdingAPI.Internal.Patching;

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@ -20,7 +18,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
** Fields
*********/
/// <summary>Writes messages to the console and log file on behalf of the game.</summary>
private static IMonitor MonitorForGame;
private static IMonitor MonitorForGame = null!;
/*********
@ -56,7 +54,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <param name="__result">The return value of the original method.</param>
/// <param name="__exception">The exception thrown by the wrapped method, if any.</param>
/// <returns>Returns the exception to throw, if any.</returns>
private static Exception Finalize_CurrentDialogue(NPC __instance, ref Stack<Dialogue> __result, Exception __exception)
private static Exception? Finalize_CurrentDialogue(NPC __instance, ref Stack<Dialogue> __result, Exception? __exception)
{
if (__exception == null)
return null;
@ -73,7 +71,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <param name="__result">The patched method's return value.</param>
/// <param name="__exception">The exception thrown by the wrapped method, if any.</param>
/// <returns>Returns the exception to throw, if any.</returns>
private static Exception Finalize_ParseMasterSchedule(string rawData, NPC __instance, ref Dictionary<int, SchedulePathDescription> __result, Exception __exception)
private static Exception? Finalize_ParseMasterSchedule(string rawData, NPC __instance, ref Dictionary<int, SchedulePathDescription> __result, Exception? __exception)
{
if (__exception != null)
{

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@ -59,7 +57,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <param name="__result">The patched method's return value.</param>
/// <param name="__exception">The exception thrown by the wrapped method, if any.</param>
/// <returns>Returns the exception to throw, if any.</returns>
private static Exception Finalize_Object_loadDisplayName(ref string __result, Exception __exception)
private static Exception? Finalize_Object_loadDisplayName(ref string __result, Exception? __exception)
{
if (__exception is KeyNotFoundException)
{

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@ -24,10 +22,10 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
** Fields
*********/
/// <summary>Writes messages to the console and log file.</summary>
private static IMonitor Monitor;
private static IMonitor Monitor = null!;
/// <summary>A callback invoked when custom content is removed from the save data to avoid a crash.</summary>
private static Action OnContentRemoved;
private static Action OnContentRemoved = null!;
/*********
@ -76,7 +74,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <summary>The method to call after <see cref="SaveGame.LoadFarmType"/> throws an exception.</summary>
/// <param name="__exception">The exception thrown by the wrapped method, if any.</param>
/// <returns>Returns the exception to throw, if any.</returns>
private static Exception Finalize_LoadFarmType(Exception __exception)
private static Exception? Finalize_LoadFarmType(Exception? __exception)
{
// missing custom farm type
if (__exception?.Message.Contains("not a valid farm type") == true && !int.TryParse(SaveGame.loaded.whichFarm, out _))
@ -110,7 +108,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <summary>Remove content which no longer exists in the game data.</summary>
/// <param name="location">The current game location.</param>
/// <param name="npcs">The NPC data.</param>
private static bool RemoveBrokenContent(GameLocation location, IDictionary<string, string> npcs)
private static bool RemoveBrokenContent(GameLocation? location, IDictionary<string, string> npcs)
{
bool removedAny = false;
if (location == null)

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
@ -30,9 +28,9 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/*********
** Private methods
*********/
/// <summary>The method to call after <see cref="SpriteBatch.CheckValid"/>.</summary>
/// <summary>The method to call after <see cref="SpriteBatch.CheckValid(Texture2D)"/>.</summary>
/// <param name="texture">The texture to validate.</param>
private static void After_CheckValid(Texture2D texture)
private static void After_CheckValid(Texture2D? texture)
{
if (texture?.IsDisposed == true)
throw new ObjectDisposedException("Cannot draw this texture because it's disposed.");

View File

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
@ -35,7 +33,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <param name="delimiter">The delimiter by which to split the text description.</param>
/// <param name="__exception">The exception thrown by the wrapped method, if any.</param>
/// <returns>Returns the exception to throw, if any.</returns>
private static Exception Finalize_GetItemFromStandardTextDescription(string description, char delimiter, ref Exception __exception)
private static Exception? Finalize_GetItemFromStandardTextDescription(string description, char delimiter, ref Exception? __exception)
{
return __exception != null
? new FormatException($"Failed to parse item text description \"{description}\" with delimiter \"{delimiter}\".", __exception)

View File

@ -1,7 +1,6 @@
#nullable disable
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Compression;
using System.Linq;
@ -82,7 +81,7 @@ namespace StardewModdingAPI.Mods.SaveBackup
}
// compress backup if possible
if (!this.TryCompress(fallbackDir.FullName, targetFile, out Exception compressError))
if (!this.TryCompress(fallbackDir.FullName, targetFile, out Exception? compressError))
{
this.Monitor.Log(Constants.TargetPlatform != GamePlatform.Android
? $"Backed up to {fallbackDir.FullName}." // expected to fail on Android
@ -142,7 +141,7 @@ namespace StardewModdingAPI.Mods.SaveBackup
/// <param name="destination">The destination file to create.</param>
/// <param name="error">The error which occurred trying to compress, if applicable. This is <see cref="NotSupportedException"/> if compression isn't supported on this platform.</param>
/// <returns>Returns whether compression succeeded.</returns>
private bool TryCompress(string sourcePath, FileInfo destination, out Exception error)
private bool TryCompress(string sourcePath, FileInfo destination, [NotNullWhen(false)] out Exception? error)
{
try
{
@ -210,7 +209,7 @@ namespace StardewModdingAPI.Mods.SaveBackup
/// <param name="filter">A filter which matches the files or directories to copy, or <c>null</c> to copy everything.</param>
/// <remarks>Derived from the SMAPI installer code.</remarks>
/// <returns>Returns whether any files were copied.</returns>
private bool RecursiveCopy(FileSystemInfo source, DirectoryInfo targetFolder, Func<FileSystemInfo, bool> filter, bool copyRoot = true)
private bool RecursiveCopy(FileSystemInfo source, DirectoryInfo targetFolder, Func<FileSystemInfo, bool>? filter, bool copyRoot = true)
{
if (!source.Exists || filter?.Invoke(source) == false)
return false;
@ -244,7 +243,7 @@ namespace StardewModdingAPI.Mods.SaveBackup
private bool MatchSaveFolders(DirectoryInfo savesFolder, FileSystemInfo entry)
{
// only need to filter top-level entries
string parentPath = (entry as FileInfo)?.DirectoryName ?? (entry as DirectoryInfo)?.Parent?.FullName;
string? parentPath = (entry as FileInfo)?.DirectoryName ?? (entry as DirectoryInfo)?.Parent?.FullName;
if (parentPath != savesFolder.FullName)
return true;