enable nullable annotations in bundled mods (#837)
This commit is contained in:
parent
aa7b0caf46
commit
2765e3f9b3
|
@ -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
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands
|
||||
{
|
||||
/// <summary>A console command to register.</summary>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using StardewValley;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData;
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewValley;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewValley;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewValley;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewValley;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewValley;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewValley;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using StardewValley;
|
||||
using StardewValley.Locations;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using StardewValley;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using StardewValley;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewModdingAPI.Utilities;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using StardewValley;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewModdingAPI.Utilities;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using StardewValley;
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)!
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!;
|
||||
|
||||
|
||||
/*********
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using HarmonyLib;
|
||||
using StardewModdingAPI.Internal.Patching;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue