summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/SMAPI.Installer/Framework/InstallerContext.cs4
-rw-r--r--src/SMAPI.Installer/Framework/InstallerPaths.cs2
-rw-r--r--src/SMAPI.Installer/InteractiveInstaller.cs24
-rw-r--r--src/SMAPI.Installer/Program.cs8
-rw-r--r--src/SMAPI.Internal.Patching/BasePatcher.cs2
-rw-r--r--src/SMAPI.Internal.Patching/HarmonyPatcher.cs4
-rw-r--r--src/SMAPI.Internal.Patching/IPatcher.cs2
-rw-r--r--src/SMAPI.Internal.Patching/PatchHelper.cs4
-rw-r--r--src/SMAPI.Internal/ConsoleWriting/ColorSchemeConfig.cs2
-rw-r--r--src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs2
-rw-r--r--src/SMAPI.Internal/ConsoleWriting/IConsoleWriter.cs2
-rw-r--r--src/SMAPI.Internal/ExceptionHelper.cs13
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticResult.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticVerifier.Helper.cs26
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticVerifier.cs34
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetCollection.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetFieldBase.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetInt.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetList.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetObjectList.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Farmer.cs2
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Item.cs18
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Object.cs4
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs6
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs4
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/AnalyzerReleases.Shipped.md7
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs4
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs9
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs6
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/SMAPI.ModBuildConfig.Analyzer.csproj4
-rw-r--r--src/SMAPI.ModBuildConfig/DeployModTask.cs6
-rw-r--r--src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs10
-rw-r--r--src/SMAPI.ModBuildConfig/Framework/UserErrorException.cs2
-rw-r--r--src/SMAPI.ModBuildConfig/SMAPI.ModBuildConfig.csproj1
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ConsoleCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/IConsoleCommand.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/DebugCommand.cs6
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/RegenerateBundles.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowDataFilesCommand.cs6
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowGameFilesCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs8
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs8
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs6
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetColorCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetImmunityCommand.cs6
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxHealthCommand.cs6
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxStaminaCommand.cs6
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetNameCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStyleCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs23
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/DownMineLevelCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/HurryAllCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetDayCommand.cs6
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetMineLevelCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetYearCommand.cs6
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/ItemData/SearchableItem.cs12
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs8
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/ModEntry.cs4
-rw-r--r--src/SMAPI.Mods.ErrorHandler/ModEntry.cs2
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs2
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs2
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatcher.cs2
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/IClickableMenuPatcher.cs2
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/NpcPatcher.cs2
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/ObjectPatcher.cs2
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/SaveGamePatcher.cs6
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchPatcher.cs2
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/UtilityPatcher.cs2
-rw-r--r--src/SMAPI.Mods.SaveBackup/ModEntry.cs20
-rw-r--r--src/SMAPI.Tests.ModApiConsumer/ApiConsumer.cs6
-rw-r--r--src/SMAPI.Tests.ModApiConsumer/Interfaces/ISimpleApi.cs2
-rw-r--r--src/SMAPI.Tests.ModApiProvider/Framework/BaseApi.cs2
-rw-r--r--src/SMAPI.Tests.ModApiProvider/Framework/SimpleApi.cs2
-rw-r--r--src/SMAPI.Tests.ModApiProvider/ProviderMod.cs2
-rw-r--r--src/SMAPI.Tests/Core/AssetNameTests.cs7
-rw-r--r--src/SMAPI.Tests/Core/InterfaceProxyTests.cs2
-rw-r--r--src/SMAPI.Tests/Core/ModResolverTests.cs55
-rw-r--r--src/SMAPI.Tests/Core/TranslationTests.cs8
-rw-r--r--src/SMAPI.Tests/Sample.cs4
-rw-r--r--src/SMAPI.Tests/Utilities/KeybindListTests.cs2
-rw-r--r--src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs24
-rw-r--r--src/SMAPI.Tests/Utilities/SDateTests.cs6
-rw-r--r--src/SMAPI.Tests/Utilities/SemanticVersionTests.cs66
-rw-r--r--src/SMAPI.Tests/WikiClient/ChangeDescriptorTests.cs3
-rw-r--r--src/SMAPI.Toolkit.CoreInterfaces/IManifest.cs6
-rw-r--r--src/SMAPI.Toolkit.CoreInterfaces/IManifestContentPackFor.cs2
-rw-r--r--src/SMAPI.Toolkit.CoreInterfaces/IManifestDependency.cs2
-rw-r--r--src/SMAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs36
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs6
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/ChangeDescriptor.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs6
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiDataOverrideEntry.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs34
-rw-r--r--src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs8
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/MetadataModel.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataField.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataModel.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs4
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs2
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDatabase.cs4
-rw-r--r--src/SMAPI.Toolkit/Framework/ModScanning/ModFolder.cs12
-rw-r--r--src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs41
-rw-r--r--src/SMAPI.Toolkit/Framework/SemanticVersionReader.cs11
-rw-r--r--src/SMAPI.Toolkit/Framework/UpdateData/UpdateKey.cs32
-rw-r--r--src/SMAPI.Toolkit/ModToolkit.cs12
-rw-r--r--src/SMAPI.Toolkit/SemanticVersion.cs80
-rw-r--r--src/SMAPI.Toolkit/SemanticVersionComparer.cs4
-rw-r--r--src/SMAPI.Toolkit/Serialization/Converters/ManifestContentPackForConverter.cs4
-rw-r--r--src/SMAPI.Toolkit/Serialization/Converters/ManifestDependencyArrayConverter.cs8
-rw-r--r--src/SMAPI.Toolkit/Serialization/Converters/SemanticVersionConverter.cs12
-rw-r--r--src/SMAPI.Toolkit/Serialization/Converters/SimpleReadOnlyConverter.cs20
-rw-r--r--src/SMAPI.Toolkit/Serialization/InternalExtensions.cs6
-rw-r--r--src/SMAPI.Toolkit/Serialization/JsonHelper.cs24
-rw-r--r--src/SMAPI.Toolkit/Serialization/Models/Manifest.cs100
-rw-r--r--src/SMAPI.Toolkit/Serialization/Models/ManifestContentPackFor.cs33
-rw-r--r--src/SMAPI.Toolkit/Serialization/Models/ManifestDependency.cs46
-rw-r--r--src/SMAPI.Toolkit/Serialization/SParseException.cs2
-rw-r--r--src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs2
-rw-r--r--src/SMAPI.Toolkit/Utilities/PathUtilities.cs33
-rw-r--r--src/SMAPI.Web/BackgroundService.cs2
-rw-r--r--src/SMAPI.Web/Controllers/IndexController.cs4
-rw-r--r--src/SMAPI.Web/Controllers/JsonValidatorController.cs15
-rw-r--r--src/SMAPI.Web/Controllers/LogParserController.cs2
-rw-r--r--src/SMAPI.Web/Controllers/ModsApiController.cs4
-rw-r--r--src/SMAPI.Web/Controllers/ModsController.cs2
-rw-r--r--src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs2
-rw-r--r--src/SMAPI.Web/Framework/Caching/Cached.cs2
-rw-r--r--src/SMAPI.Web/Framework/Caching/Mods/IModCacheRepository.cs3
-rw-r--r--src/SMAPI.Web/Framework/Caching/Mods/ModCacheMemoryRepository.cs3
-rw-r--r--src/SMAPI.Web/Framework/Caching/Wiki/IWikiCacheRepository.cs2
-rw-r--r--src/SMAPI.Web/Framework/Caching/Wiki/WikiCacheMemoryRepository.cs2
-rw-r--r--src/SMAPI.Web/Framework/Caching/Wiki/WikiMetadata.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs6
-rw-r--r--src/SMAPI.Web/Framework/Clients/CurseForge/CurseForgeClient.cs4
-rw-r--r--src/SMAPI.Web/Framework/Clients/CurseForge/ResponseModels/ModFileModel.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/CurseForge/ResponseModels/ModModel.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/GenericModDownload.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/GenericModPage.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitAsset.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitLicense.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitRepo.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/IModSiteClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/FileDataModel.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModDataModel.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModListModel.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModModel.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs6
-rw-r--r--src/SMAPI.Web/Framework/Clients/Nexus/ResponseModels/NexusMod.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/Pastebin/IPastebinClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs2
-rw-r--r--src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs2
-rw-r--r--src/SMAPI.Web/Framework/Compression/GzipHelper.cs10
-rw-r--r--src/SMAPI.Web/Framework/Compression/IGzipHelper.cs2
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs2
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ModOverrideConfig.cs2
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs2
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs2
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/SmapiInfoConfig.cs2
-rw-r--r--src/SMAPI.Web/Framework/Extensions.cs6
-rw-r--r--src/SMAPI.Web/Framework/IModDownload.cs2
-rw-r--r--src/SMAPI.Web/Framework/IModPage.cs2
-rw-r--r--src/SMAPI.Web/Framework/InternalControllerFeatureProvider.cs2
-rw-r--r--src/SMAPI.Web/Framework/JobDashboardAuthorizationFilter.cs4
-rw-r--r--src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs4
-rw-r--r--src/SMAPI.Web/Framework/LogParsing/LogParseException.cs5
-rw-r--r--src/SMAPI.Web/Framework/LogParsing/LogParser.cs36
-rw-r--r--src/SMAPI.Web/Framework/LogParsing/Models/LogMessage.cs2
-rw-r--r--src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs2
-rw-r--r--src/SMAPI.Web/Framework/LogParsing/Models/ParsedLog.cs2
-rw-r--r--src/SMAPI.Web/Framework/ModInfoModel.cs2
-rw-r--r--src/SMAPI.Web/Framework/ModSiteManager.cs2
-rw-r--r--src/SMAPI.Web/Framework/RedirectRules/RedirectHostsToUrlsRule.cs4
-rw-r--r--src/SMAPI.Web/Framework/RedirectRules/RedirectMatchRule.cs2
-rw-r--r--src/SMAPI.Web/Framework/RedirectRules/RedirectPathsToUrlsRule.cs2
-rw-r--r--src/SMAPI.Web/Framework/RedirectRules/RedirectToHttpsRule.cs4
-rw-r--r--src/SMAPI.Web/Framework/Storage/IStorageProvider.cs2
-rw-r--r--src/SMAPI.Web/Framework/Storage/StorageProvider.cs6
-rw-r--r--src/SMAPI.Web/Framework/Storage/StoredFileInfo.cs2
-rw-r--r--src/SMAPI.Web/Framework/Storage/UploadResult.cs2
-rw-r--r--src/SMAPI.Web/Framework/VersionConstraint.cs2
-rw-r--r--src/SMAPI.Web/Program.cs2
-rw-r--r--src/SMAPI.Web/Startup.cs4
-rw-r--r--src/SMAPI.Web/ViewModels/IndexModel.cs2
-rw-r--r--src/SMAPI.Web/ViewModels/IndexVersionModel.cs2
-rw-r--r--src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorErrorModel.cs2
-rw-r--r--src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorModel.cs2
-rw-r--r--src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorRequestModel.cs2
-rw-r--r--src/SMAPI.Web/ViewModels/LogParserModel.cs4
-rw-r--r--src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs2
-rw-r--r--src/SMAPI.Web/ViewModels/ModLinkModel.cs2
-rw-r--r--src/SMAPI.Web/ViewModels/ModListModel.cs2
-rw-r--r--src/SMAPI.Web/ViewModels/ModModel.cs2
-rw-r--r--src/SMAPI.Web/Views/Index/Index.cshtml4
-rw-r--r--src/SMAPI.Web/Views/Index/Privacy.cshtml4
-rw-r--r--src/SMAPI.Web/Views/JsonValidator/Index.cshtml4
-rw-r--r--src/SMAPI.Web/Views/LogParser/Index.cshtml10
-rw-r--r--src/SMAPI.Web/Views/Mods/Index.cshtml4
-rw-r--r--src/SMAPI.Web/Views/Shared/_Layout.cshtml4
-rw-r--r--src/SMAPI.Web/Views/_ViewStart.cshtml6
-rw-r--r--src/SMAPI.Web/wwwroot/Content/css/file-upload.css2
-rw-r--r--src/SMAPI.sln.DotSettings1
-rw-r--r--src/SMAPI/Constants.cs20
-rw-r--r--src/SMAPI/Context.cs14
-rw-r--r--src/SMAPI/Events/AssetReadyEventArgs.cs2
-rw-r--r--src/SMAPI/Events/AssetRequestedEventArgs.cs4
-rw-r--r--src/SMAPI/Events/AssetsInvalidatedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/BuildingListChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/ButtonPressedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/ButtonReleasedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/ButtonsChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/ChestInventoryChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/CursorMovedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/DebrisListChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/FurnitureListChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/IContentEvents.cs2
-rw-r--r--src/SMAPI/Events/IDisplayEvents.cs2
-rw-r--r--src/SMAPI/Events/IGameLoopEvents.cs2
-rw-r--r--src/SMAPI/Events/IInputEvents.cs2
-rw-r--r--src/SMAPI/Events/IModEvents.cs2
-rw-r--r--src/SMAPI/Events/IMultiplayerEvents.cs2
-rw-r--r--src/SMAPI/Events/IPlayerEvents.cs2
-rw-r--r--src/SMAPI/Events/ISpecialisedEvents.cs2
-rw-r--r--src/SMAPI/Events/IWorldEvents.cs2
-rw-r--r--src/SMAPI/Events/InventoryChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/ItemStackSizeChange.cs2
-rw-r--r--src/SMAPI/Events/LargeTerrainFeatureListChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/LevelChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/LocaleChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/LocationListChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/MenuChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/ModMessageReceivedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/NpcListChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/ObjectListChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/PeerConnectedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/PeerContextReceivedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/PeerDisconnectedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/RenderedActiveMenuEventArgs.cs2
-rw-r--r--src/SMAPI/Events/RenderedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/RenderedHudEventArgs.cs2
-rw-r--r--src/SMAPI/Events/RenderedWorldEventArgs.cs2
-rw-r--r--src/SMAPI/Events/RenderingActiveMenuEventArgs.cs2
-rw-r--r--src/SMAPI/Events/RenderingEventArgs.cs2
-rw-r--r--src/SMAPI/Events/RenderingHudEventArgs.cs2
-rw-r--r--src/SMAPI/Events/RenderingWorldEventArgs.cs2
-rw-r--r--src/SMAPI/Events/TerrainFeatureListChangedEventArgs.cs2
-rw-r--r--src/SMAPI/Events/WarpedEventArgs.cs2
-rw-r--r--src/SMAPI/Framework/Command.cs2
-rw-r--r--src/SMAPI/Framework/CommandManager.cs6
-rw-r--r--src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs4
-rw-r--r--src/SMAPI/Framework/Commands/HelpCommand.cs2
-rw-r--r--src/SMAPI/Framework/Commands/IInternalCommand.cs2
-rw-r--r--src/SMAPI/Framework/Commands/ReloadI18nCommand.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetData.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForDictionary.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForImage.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForMap.cs8
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForObject.cs6
-rw-r--r--src/SMAPI/Framework/Content/AssetEditOperation.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetInfo.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetInterceptorChange.cs4
-rw-r--r--src/SMAPI/Framework/Content/AssetLoadOperation.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetName.cs4
-rw-r--r--src/SMAPI/Framework/Content/AssetOperationGroup.cs2
-rw-r--r--src/SMAPI/Framework/Content/ContentCache.cs2
-rw-r--r--src/SMAPI/Framework/Content/TilesheetReference.cs3
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs69
-rw-r--r--src/SMAPI/Framework/ContentManagers/BaseContentManager.cs10
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManager.cs4
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs2
-rw-r--r--src/SMAPI/Framework/ContentManagers/IContentManager.cs3
-rw-r--r--src/SMAPI/Framework/ContentManagers/ModContentManager.cs211
-rw-r--r--src/SMAPI/Framework/ContentPack.cs40
-rw-r--r--src/SMAPI/Framework/CursorPosition.cs2
-rw-r--r--src/SMAPI/Framework/DeprecationManager.cs4
-rw-r--r--src/SMAPI/Framework/DeprecationWarning.cs2
-rw-r--r--src/SMAPI/Framework/Events/EventManager.cs2
-rw-r--r--src/SMAPI/Framework/Events/IManagedEvent.cs2
-rw-r--r--src/SMAPI/Framework/Events/ManagedEvent.cs4
-rw-r--r--src/SMAPI/Framework/Events/ManagedEventHandler.cs4
-rw-r--r--src/SMAPI/Framework/Events/ModContentEvents.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModDisplayEvents.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModEvents.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModEventsBase.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModGameLoopEvents.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModInputEvents.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModMultiplayerEvents.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModPlayerEvents.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModSpecialisedEvents.cs2
-rw-r--r--src/SMAPI/Framework/Events/ModWorldEvents.cs2
-rw-r--r--src/SMAPI/Framework/Exceptions/SAssemblyLoadFailedException.cs2
-rw-r--r--src/SMAPI/Framework/Exceptions/SContentLoadException.cs4
-rw-r--r--src/SMAPI/Framework/GameVersion.cs2
-rw-r--r--src/SMAPI/Framework/IModMetadata.cs2
-rw-r--r--src/SMAPI/Framework/Input/GamePadStateBuilder.cs2
-rw-r--r--src/SMAPI/Framework/Input/IInputStateBuilder.cs2
-rw-r--r--src/SMAPI/Framework/Input/KeyboardStateBuilder.cs4
-rw-r--r--src/SMAPI/Framework/Input/MouseStateBuilder.cs2
-rw-r--r--src/SMAPI/Framework/Input/SInputState.cs16
-rw-r--r--src/SMAPI/Framework/InternalExtensions.cs3
-rw-r--r--src/SMAPI/Framework/Logging/InterceptingTextWriter.cs2
-rw-r--r--src/SMAPI/Framework/Logging/LogFileManager.cs2
-rw-r--r--src/SMAPI/Framework/Logging/LogManager.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/BaseHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/CommandHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentHelper.cs16
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/DataHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/GameContentHelper.cs10
-rw-r--r--src/SMAPI/Framework/ModHelpers/InputHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModContentHelper.cs22
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModHelper.cs21
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs4
-rw-r--r--src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/TranslationHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModLinked.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyLoadStatus.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyLoader.cs22
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyParseResult.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/IInstructionHandler.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/IncompatibleInstructionException.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/InvalidModStateException.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/ModMetadata.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/ModResolver.cs13
-rw-r--r--src/SMAPI/Framework/ModLoading/PlatformAssemblyMap.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/ArchitectureAssemblyRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/HarmonyRewriter.cs4
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs4
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Symbols/SymbolReader.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs4
-rw-r--r--src/SMAPI/Framework/ModLoading/Symbols/SymbolWriterProvider.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/TypeReferenceComparer.cs2
-rw-r--r--src/SMAPI/Framework/ModRegistry.cs8
-rw-r--r--src/SMAPI/Framework/Models/SConfig.cs7
-rw-r--r--src/SMAPI/Framework/Monitor.cs4
-rw-r--r--src/SMAPI/Framework/Networking/ModMessageModel.cs2
-rw-r--r--src/SMAPI/Framework/Networking/MultiplayerPeer.cs2
-rw-r--r--src/SMAPI/Framework/Networking/MultiplayerPeerMod.cs2
-rw-r--r--src/SMAPI/Framework/Networking/RemoteContextModModel.cs2
-rw-r--r--src/SMAPI/Framework/Networking/RemoteContextModel.cs2
-rw-r--r--src/SMAPI/Framework/Networking/SGalaxyNetClient.cs2
-rw-r--r--src/SMAPI/Framework/Networking/SGalaxyNetServer.cs8
-rw-r--r--src/SMAPI/Framework/Networking/SLidgrenClient.cs2
-rw-r--r--src/SMAPI/Framework/Networking/SLidgrenServer.cs6
-rw-r--r--src/SMAPI/Framework/Reflection/CacheEntry.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/ReflectedField.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/ReflectedMethod.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/ReflectedProperty.cs2
-rw-r--r--src/SMAPI/Framework/Reflection/Reflector.cs6
-rw-r--r--src/SMAPI/Framework/Rendering/SDisplayDevice.cs2
-rw-r--r--src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs2
-rw-r--r--src/SMAPI/Framework/RequestExitDelegate.cs2
-rw-r--r--src/SMAPI/Framework/SChatBox.cs2
-rw-r--r--src/SMAPI/Framework/SCore.cs81
-rw-r--r--src/SMAPI/Framework/SGame.cs10
-rw-r--r--src/SMAPI/Framework/SGameRunner.cs4
-rw-r--r--src/SMAPI/Framework/SModHooks.cs2
-rw-r--r--src/SMAPI/Framework/SMultiplayer.cs58
-rw-r--r--src/SMAPI/Framework/Serialization/KeybindConverter.cs6
-rw-r--r--src/SMAPI/Framework/Singleton.cs4
-rw-r--r--src/SMAPI/Framework/SnapshotDiff.cs2
-rw-r--r--src/SMAPI/Framework/SnapshotItemListDiff.cs2
-rw-r--r--src/SMAPI/Framework/SnapshotListDiff.cs6
-rw-r--r--src/SMAPI/Framework/StateTracking/ChestTracker.cs6
-rw-r--r--src/SMAPI/Framework/StateTracking/Comparers/EquatableComparer.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/Comparers/ObjectReferenceComparer.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/BaseDisposableWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs6
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/ImmutableCollectionWatcher.cs4
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs6
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/NetListWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs8
-rw-r--r--src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/ICollectionWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/IDictionaryWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/IValueWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/IWatcher.cs2
-rw-r--r--src/SMAPI/Framework/StateTracking/LocationTracker.cs4
-rw-r--r--src/SMAPI/Framework/StateTracking/PlayerTracker.cs4
-rw-r--r--src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs16
-rw-r--r--src/SMAPI/Framework/StateTracking/Snapshots/PlayerSnapshot.cs8
-rw-r--r--src/SMAPI/Framework/StateTracking/Snapshots/WatcherSnapshot.cs18
-rw-r--r--src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs6
-rw-r--r--src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs2
-rw-r--r--src/SMAPI/Framework/TemporaryHacks/MiniMonoModHotfix.cs8
-rw-r--r--src/SMAPI/Framework/Translator.cs2
-rw-r--r--src/SMAPI/Framework/Utilities/ContextHash.cs2
-rw-r--r--src/SMAPI/Framework/Utilities/Countdown.cs2
-rw-r--r--src/SMAPI/Framework/Utilities/TickCacheDictionary.cs9
-rw-r--r--src/SMAPI/Framework/WatcherCore.cs4
-rw-r--r--src/SMAPI/GamePlatform.cs2
-rw-r--r--src/SMAPI/IAssetData.cs2
-rw-r--r--src/SMAPI/IAssetDataForDictionary.cs2
-rw-r--r--src/SMAPI/IAssetDataForImage.cs2
-rw-r--r--src/SMAPI/IAssetDataForMap.cs2
-rw-r--r--src/SMAPI/IAssetEditor.cs2
-rw-r--r--src/SMAPI/IAssetInfo.cs2
-rw-r--r--src/SMAPI/IAssetLoader.cs2
-rw-r--r--src/SMAPI/IAssetName.cs2
-rw-r--r--src/SMAPI/ICommandHelper.cs2
-rw-r--r--src/SMAPI/IContentHelper.cs2
-rw-r--r--src/SMAPI/IContentPack.cs2
-rw-r--r--src/SMAPI/IContentPackHelper.cs2
-rw-r--r--src/SMAPI/ICursorPosition.cs2
-rw-r--r--src/SMAPI/IDataHelper.cs2
-rw-r--r--src/SMAPI/IGameContentHelper.cs2
-rw-r--r--src/SMAPI/IInputHelper.cs2
-rw-r--r--src/SMAPI/IMod.cs2
-rw-r--r--src/SMAPI/IModContentHelper.cs2
-rw-r--r--src/SMAPI/IModHelper.cs2
-rw-r--r--src/SMAPI/IModInfo.cs2
-rw-r--r--src/SMAPI/IModLinked.cs2
-rw-r--r--src/SMAPI/IModRegistry.cs2
-rw-r--r--src/SMAPI/IMonitor.cs2
-rw-r--r--src/SMAPI/IMultiplayerHelper.cs2
-rw-r--r--src/SMAPI/IMultiplayerPeer.cs2
-rw-r--r--src/SMAPI/IMultiplayerPeerMod.cs2
-rw-r--r--src/SMAPI/IReflectedField.cs4
-rw-r--r--src/SMAPI/IReflectedMethod.cs4
-rw-r--r--src/SMAPI/IReflectedProperty.cs2
-rw-r--r--src/SMAPI/IReflectionHelper.cs2
-rw-r--r--src/SMAPI/ITranslationHelper.cs2
-rw-r--r--src/SMAPI/Metadata/CoreAssetPropagator.cs8
-rw-r--r--src/SMAPI/Metadata/InstructionMetadata.cs2
-rw-r--r--src/SMAPI/Mod.cs2
-rw-r--r--src/SMAPI/Patches/Game1Patcher.cs2
-rw-r--r--src/SMAPI/Patches/TitleMenuPatcher.cs2
-rw-r--r--src/SMAPI/Program.cs6
-rw-r--r--src/SMAPI/SButton.cs2
-rw-r--r--src/SMAPI/SButtonState.cs2
-rw-r--r--src/SMAPI/SMAPI.config.json5
-rw-r--r--src/SMAPI/SemanticVersion.cs29
-rw-r--r--src/SMAPI/Translation.cs4
-rw-r--r--src/SMAPI/Utilities/CaseInsensitivePathCache.cs126
-rw-r--r--src/SMAPI/Utilities/Keybind.cs6
-rw-r--r--src/SMAPI/Utilities/KeybindList.cs4
-rw-r--r--src/SMAPI/Utilities/PathUtilities.cs14
-rw-r--r--src/SMAPI/Utilities/PerScreen.cs4
-rw-r--r--src/SMAPI/Utilities/SDate.cs2
487 files changed, 2171 insertions, 860 deletions
diff --git a/src/SMAPI.Installer/Framework/InstallerContext.cs b/src/SMAPI.Installer/Framework/InstallerContext.cs
index bb973230..23d5b17c 100644
--- a/src/SMAPI.Installer/Framework/InstallerContext.cs
+++ b/src/SMAPI.Installer/Framework/InstallerContext.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.IO;
using StardewModdingAPI.Toolkit;
using StardewModdingAPI.Toolkit.Framework.GameScanning;
@@ -12,7 +14,7 @@ namespace StardewModdingAPI.Installer.Framework
** Fields
*********/
/// <summary>The underlying toolkit game scanner.</summary>
- private readonly GameScanner GameScanner = new GameScanner();
+ private readonly GameScanner GameScanner = new();
/*********
diff --git a/src/SMAPI.Installer/Framework/InstallerPaths.cs b/src/SMAPI.Installer/Framework/InstallerPaths.cs
index 0976eceb..fd9d1be6 100644
--- a/src/SMAPI.Installer/Framework/InstallerPaths.cs
+++ b/src/SMAPI.Installer/Framework/InstallerPaths.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.IO;
using StardewModdingAPI.Toolkit.Framework;
diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs
index b3bba883..b07c0461 100644
--- a/src/SMAPI.Installer/InteractiveInstaller.cs
+++ b/src/SMAPI.Installer/InteractiveInstaller.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -126,7 +128,7 @@ namespace StardewModdingApi.Installer
/****
** Get basic info & set window title
****/
- ModToolkit toolkit = new ModToolkit();
+ ModToolkit toolkit = new();
var context = new InstallerContext();
Console.Title = $"SMAPI {context.GetInstallerVersion()} installer on {context.Platform} {context.PlatformName}";
Console.WriteLine();
@@ -246,7 +248,7 @@ namespace StardewModdingApi.Installer
}
// get folders
- DirectoryInfo bundleDir = new DirectoryInfo(this.BundlePath);
+ DirectoryInfo bundleDir = new(this.BundlePath);
paths = new InstallerPaths(bundleDir, installDir);
}
@@ -354,8 +356,8 @@ namespace StardewModdingApi.Installer
// move global save data folder (changed in 3.2)
{
string dataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley");
- DirectoryInfo oldDir = new DirectoryInfo(Path.Combine(dataPath, "Saves", ".smapi"));
- DirectoryInfo newDir = new DirectoryInfo(Path.Combine(dataPath, ".smapi"));
+ DirectoryInfo oldDir = new(Path.Combine(dataPath, "Saves", ".smapi"));
+ DirectoryInfo newDir = new(Path.Combine(dataPath, ".smapi"));
if (oldDir.Exists)
{
@@ -428,7 +430,7 @@ namespace StardewModdingApi.Installer
}
// add or replace bundled mods
- DirectoryInfo bundledModsDir = new DirectoryInfo(Path.Combine(paths.BundlePath, "Mods"));
+ DirectoryInfo bundledModsDir = new(Path.Combine(paths.BundlePath, "Mods"));
if (bundledModsDir.Exists && bundledModsDir.EnumerateDirectories().Any())
{
this.PrintDebug("Adding bundled mods...");
@@ -450,7 +452,7 @@ namespace StardewModdingApi.Installer
// find target folder
ModFolder targetMod = targetMods.FirstOrDefault(p => p.Manifest?.UniqueID?.Equals(sourceMod.Manifest.UniqueID, StringComparison.OrdinalIgnoreCase) == true);
- DirectoryInfo defaultTargetFolder = new DirectoryInfo(Path.Combine(paths.ModsPath, sourceMod.Directory.Name));
+ DirectoryInfo defaultTargetFolder = new(Path.Combine(paths.ModsPath, sourceMod.Directory.Name));
DirectoryInfo targetFolder = targetMod?.Directory ?? defaultTargetFolder;
this.PrintDebug(targetFolder.FullName == defaultTargetFolder.FullName
? $" adding {sourceMod.Manifest.Name}..."
@@ -562,7 +564,7 @@ namespace StardewModdingApi.Installer
{
try
{
- FileUtilities.ForceDelete(Directory.Exists(path) ? new DirectoryInfo(path) : (FileSystemInfo)new FileInfo(path));
+ FileUtilities.ForceDelete(Directory.Exists(path) ? new DirectoryInfo(path) : new FileInfo(path));
break;
}
catch (Exception ex)
@@ -593,7 +595,7 @@ namespace StardewModdingApi.Installer
break;
case DirectoryInfo sourceDir:
- DirectoryInfo targetSubfolder = new DirectoryInfo(Path.Combine(targetFolder.FullName, sourceDir.Name));
+ DirectoryInfo targetSubfolder = new(Path.Combine(targetFolder.FullName, sourceDir.Name));
foreach (var entry in sourceDir.EnumerateFileSystemInfos())
this.RecursiveCopy(entry, targetSubfolder, filter);
break;
@@ -717,7 +719,7 @@ namespace StardewModdingApi.Installer
// get directory
if (File.Exists(path))
path = Path.GetDirectoryName(path);
- DirectoryInfo directory = new DirectoryInfo(path);
+ DirectoryInfo directory = new(path);
// validate path
if (!directory.Exists)
@@ -795,7 +797,7 @@ namespace StardewModdingApi.Installer
// get path
string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley");
- DirectoryInfo modDir = new DirectoryInfo(Path.Combine(appDataPath, "Mods"));
+ DirectoryInfo modDir = new(Path.Combine(appDataPath, "Mods"));
// check if migration needed
if (!modDir.Exists)
@@ -808,7 +810,7 @@ namespace StardewModdingApi.Installer
{
// get type
bool isDir = entry is DirectoryInfo;
- if (!isDir && !(entry is FileInfo))
+ if (!isDir && entry is not FileInfo)
continue; // should never happen
// delete packaged mods (newer version bundled into SMAPI)
diff --git a/src/SMAPI.Installer/Program.cs b/src/SMAPI.Installer/Program.cs
index 45cfea75..5139513f 100644
--- a/src/SMAPI.Installer/Program.cs
+++ b/src/SMAPI.Installer/Program.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -31,7 +33,7 @@ namespace StardewModdingApi.Installer
public static void Main(string[] args)
{
// find install bundle
- FileInfo zipFile = new FileInfo(Path.Combine(Program.InstallerPath, "install.dat"));
+ FileInfo zipFile = new(Path.Combine(Program.InstallerPath, "install.dat"));
if (!zipFile.Exists)
{
Console.WriteLine($"Oops! Some of the installer files are missing; try re-downloading the installer. (Missing file: {zipFile.FullName})");
@@ -40,7 +42,7 @@ namespace StardewModdingApi.Installer
}
// unzip bundle into temp folder
- DirectoryInfo bundleDir = new DirectoryInfo(Program.ExtractedBundlePath);
+ DirectoryInfo bundleDir = new(Program.ExtractedBundlePath);
Console.WriteLine("Extracting install files...");
ZipFile.ExtractToDirectory(zipFile.FullName, bundleDir.FullName);
@@ -70,7 +72,7 @@ namespace StardewModdingApi.Installer
{
try
{
- AssemblyName name = new AssemblyName(e.Name);
+ AssemblyName name = new(e.Name);
foreach (FileInfo dll in new DirectoryInfo(Program.InternalFilesPath).EnumerateFiles("*.dll"))
{
if (name.Name.Equals(AssemblyName.GetAssemblyName(dll.FullName).Name, StringComparison.OrdinalIgnoreCase))
diff --git a/src/SMAPI.Internal.Patching/BasePatcher.cs b/src/SMAPI.Internal.Patching/BasePatcher.cs
index 87155d7f..6d019b52 100644
--- a/src/SMAPI.Internal.Patching/BasePatcher.cs
+++ b/src/SMAPI.Internal.Patching/BasePatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Reflection;
using HarmonyLib;
diff --git a/src/SMAPI.Internal.Patching/HarmonyPatcher.cs b/src/SMAPI.Internal.Patching/HarmonyPatcher.cs
index c07e3b41..fc239fd2 100644
--- a/src/SMAPI.Internal.Patching/HarmonyPatcher.cs
+++ b/src/SMAPI.Internal.Patching/HarmonyPatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using HarmonyLib;
@@ -15,7 +17,7 @@ namespace StardewModdingAPI.Internal.Patching
/// <param name="patchers">The patchers to apply.</param>
public static Harmony Apply(string id, IMonitor monitor, params IPatcher[] patchers)
{
- Harmony harmony = new Harmony(id);
+ Harmony harmony = new(id);
foreach (IPatcher patcher in patchers)
{
diff --git a/src/SMAPI.Internal.Patching/IPatcher.cs b/src/SMAPI.Internal.Patching/IPatcher.cs
index a732d64f..5b373117 100644
--- a/src/SMAPI.Internal.Patching/IPatcher.cs
+++ b/src/SMAPI.Internal.Patching/IPatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using HarmonyLib;
namespace StardewModdingAPI.Internal.Patching
diff --git a/src/SMAPI.Internal.Patching/PatchHelper.cs b/src/SMAPI.Internal.Patching/PatchHelper.cs
index fc79ddf2..52b15fd1 100644
--- a/src/SMAPI.Internal.Patching/PatchHelper.cs
+++ b/src/SMAPI.Internal.Patching/PatchHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Linq;
using System.Reflection;
@@ -43,7 +45,7 @@ namespace StardewModdingAPI.Internal.Patching
/// <param name="generics">The method generic types, or <c>null</c> if it's not generic.</param>
public static string GetMethodString(Type type, string name, Type[] parameters = null, Type[] generics = null)
{
- StringBuilder str = new StringBuilder();
+ StringBuilder str = new();
// type
str.Append(type.FullName);
diff --git a/src/SMAPI.Internal/ConsoleWriting/ColorSchemeConfig.cs b/src/SMAPI.Internal/ConsoleWriting/ColorSchemeConfig.cs
index 001840bf..b22aa231 100644
--- a/src/SMAPI.Internal/ConsoleWriting/ColorSchemeConfig.cs
+++ b/src/SMAPI.Internal/ConsoleWriting/ColorSchemeConfig.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
diff --git a/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs b/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs
index bfe155e0..19a31c7b 100644
--- a/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs
+++ b/src/SMAPI.Internal/ConsoleWriting/ColorfulConsoleWriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using StardewModdingAPI.Toolkit.Utilities;
diff --git a/src/SMAPI.Internal/ConsoleWriting/IConsoleWriter.cs b/src/SMAPI.Internal/ConsoleWriting/IConsoleWriter.cs
index fbcf161c..84e17207 100644
--- a/src/SMAPI.Internal/ConsoleWriting/IConsoleWriter.cs
+++ b/src/SMAPI.Internal/ConsoleWriting/IConsoleWriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Internal.ConsoleWriting
{
/// <summary>Writes text to the console.</summary>
diff --git a/src/SMAPI.Internal/ExceptionHelper.cs b/src/SMAPI.Internal/ExceptionHelper.cs
index 03d48911..a856cf71 100644
--- a/src/SMAPI.Internal/ExceptionHelper.cs
+++ b/src/SMAPI.Internal/ExceptionHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Reflection;
using System.Text.RegularExpressions;
@@ -25,7 +27,7 @@ namespace StardewModdingAPI.Internal
case ReflectionTypeLoadException ex:
string summary = ex.ToString();
- foreach (Exception childEx in ex.LoaderExceptions ?? Array.Empty<Exception>())
+ foreach (Exception childEx in ex.LoaderExceptions)
summary += $"\n\n{childEx?.GetLogSummary()}";
message = summary;
break;
@@ -43,15 +45,6 @@ namespace StardewModdingAPI.Internal
}
}
- /// <summary>Get the lowest exception in an exception stack.</summary>
- /// <param name="exception">The exception from which to search.</param>
- public static Exception GetInnermostException(this Exception exception)
- {
- while (exception.InnerException != null)
- exception = exception.InnerException;
- return exception;
- }
-
/// <summary>Simplify common patterns in exception log messages that don't convey useful info.</summary>
/// <param name="message">The log message to simplify.</param>
public static string SimplifyExtensionMessage(string message)
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticResult.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticResult.cs
index 896c2cb8..8c24eda9 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticResult.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticResult.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// <generated />
using Microsoft.CodeAnalysis;
using System;
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticVerifier.Helper.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticVerifier.Helper.cs
index 0247288e..68a892a9 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticVerifier.Helper.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticVerifier.Helper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// <generated />
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
@@ -51,17 +53,17 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyzer analyzer, Document[] documents)
{
var projects = new HashSet<Project>();
- foreach (var document in documents)
+ foreach (Document document in documents)
{
projects.Add(document.Project);
}
var diagnostics = new List<Diagnostic>();
- foreach (var project in projects)
+ foreach (Project project in projects)
{
- var compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer));
+ CompilationWithAnalyzers compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer));
var diags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result;
- foreach (var diag in diags)
+ foreach (Diagnostic diag in diags)
{
if (diag.Location == Location.None || diag.Location.IsInMetadata)
{
@@ -71,8 +73,8 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
{
for (int i = 0; i < documents.Length; i++)
{
- var document = documents[i];
- var tree = document.GetSyntaxTreeAsync().Result;
+ Document document = documents[i];
+ SyntaxTree tree = document.GetSyntaxTreeAsync().Result;
if (tree == diag.Location.SourceTree)
{
diagnostics.Add(diag);
@@ -113,7 +115,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
throw new ArgumentException("Unsupported Language");
}
- var project = CreateProject(sources, language);
+ Project project = CreateProject(sources, language);
var documents = project.Documents.ToArray();
if (sources.Length != documents.Length)
@@ -146,9 +148,9 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
string fileNamePrefix = DefaultFilePathPrefix;
string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt;
- var projectId = ProjectId.CreateNewId(debugName: TestProjectName);
+ ProjectId projectId = ProjectId.CreateNewId(debugName: TestProjectName);
- var solution = new AdhocWorkspace()
+ Solution solution = new AdhocWorkspace()
.CurrentSolution
.AddProject(projectId, TestProjectName, TestProjectName, language)
.AddMetadataReference(projectId, DiagnosticVerifier.SelfReference)
@@ -158,10 +160,10 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
.AddMetadataReference(projectId, CodeAnalysisReference);
int count = 0;
- foreach (var source in sources)
+ foreach (string source in sources)
{
- var newFileName = fileNamePrefix + count + "." + fileExt;
- var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName);
+ string newFileName = fileNamePrefix + count + "." + fileExt;
+ DocumentId documentId = DocumentId.CreateNewId(projectId, debugName: newFileName);
solution = solution.AddDocument(documentId, newFileName, SourceText.From(source));
count++;
}
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticVerifier.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticVerifier.cs
index edaaabd4..4170042d 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticVerifier.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Framework/DiagnosticVerifier.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// <generated />
using System.Collections.Generic;
using System.Linq;
@@ -41,7 +43,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
/// <param name="expected"> DiagnosticResults that should appear after the analyzer is run on the source</param>
protected void VerifyCSharpDiagnostic(string source, params DiagnosticResult[] expected)
{
- VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected);
+ this.VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzer(), expected);
}
/// <summary>
@@ -52,7 +54,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
/// <param name="expected">DiagnosticResults that should appear after the analyzer is run on the sources</param>
protected void VerifyCSharpDiagnostic(string[] sources, params DiagnosticResult[] expected)
{
- VerifyDiagnostics(sources, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected);
+ this.VerifyDiagnostics(sources, LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzer(), expected);
}
/// <summary>
@@ -65,8 +67,8 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
/// <param name="expected">DiagnosticResults that should appear after the analyzer is run on the sources</param>
private void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expected)
{
- var diagnostics = GetSortedDiagnostics(sources, language, analyzer);
- VerifyDiagnosticResults(diagnostics, analyzer, expected);
+ var diagnostics = DiagnosticVerifier.GetSortedDiagnostics(sources, language, analyzer);
+ DiagnosticVerifier.VerifyDiagnosticResults(diagnostics, analyzer, expected);
}
#endregion
@@ -86,7 +88,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
if (expectedCount != actualCount)
{
- string diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzer, actualResults.ToArray()) : " NONE.";
+ string diagnosticsOutput = actualResults.Any() ? DiagnosticVerifier.FormatDiagnostics(analyzer, actualResults.ToArray()) : " NONE.";
Assert.IsTrue(false,
string.Format("Mismatch between number of diagnostics returned, expected \"{0}\" actual \"{1}\"\r\n\r\nDiagnostics:\r\n{2}\r\n", expectedCount, actualCount, diagnosticsOutput));
@@ -103,12 +105,12 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
{
Assert.IsTrue(false,
string.Format("Expected:\nA project diagnostic with No location\nActual:\n{0}",
- FormatDiagnostics(analyzer, actual)));
+ DiagnosticVerifier.FormatDiagnostics(analyzer, actual)));
}
}
else
{
- VerifyDiagnosticLocation(analyzer, actual, actual.Location, expected.Locations.First());
+ DiagnosticVerifier.VerifyDiagnosticLocation(analyzer, actual, actual.Location, expected.Locations.First());
var additionalLocations = actual.AdditionalLocations.ToArray();
if (additionalLocations.Length != expected.Locations.Length - 1)
@@ -116,12 +118,12 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
Assert.IsTrue(false,
string.Format("Expected {0} additional locations but got {1} for Diagnostic:\r\n {2}\r\n",
expected.Locations.Length - 1, additionalLocations.Length,
- FormatDiagnostics(analyzer, actual)));
+ DiagnosticVerifier.FormatDiagnostics(analyzer, actual)));
}
for (int j = 0; j < additionalLocations.Length; ++j)
{
- VerifyDiagnosticLocation(analyzer, actual, additionalLocations[j], expected.Locations[j + 1]);
+ DiagnosticVerifier.VerifyDiagnosticLocation(analyzer, actual, additionalLocations[j], expected.Locations[j + 1]);
}
}
@@ -129,21 +131,21 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
{
Assert.IsTrue(false,
string.Format("Expected diagnostic id to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Id, actual.Id, FormatDiagnostics(analyzer, actual)));
+ expected.Id, actual.Id, DiagnosticVerifier.FormatDiagnostics(analyzer, actual)));
}
if (actual.Severity != expected.Severity)
{
Assert.IsTrue(false,
string.Format("Expected diagnostic severity to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Severity, actual.Severity, FormatDiagnostics(analyzer, actual)));
+ expected.Severity, actual.Severity, DiagnosticVerifier.FormatDiagnostics(analyzer, actual)));
}
if (actual.GetMessage() != expected.Message)
{
Assert.IsTrue(false,
string.Format("Expected diagnostic message to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Message, actual.GetMessage(), FormatDiagnostics(analyzer, actual)));
+ expected.Message, actual.GetMessage(), DiagnosticVerifier.FormatDiagnostics(analyzer, actual)));
}
}
}
@@ -161,7 +163,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
Assert.IsTrue(actualSpan.Path == expected.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Path.Contains("Test.")),
string.Format("Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic)));
+ expected.Path, actualSpan.Path, DiagnosticVerifier.FormatDiagnostics(analyzer, diagnostic)));
var actualLinePosition = actualSpan.StartLinePosition;
@@ -172,7 +174,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
{
Assert.IsTrue(false,
string.Format("Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Line, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic)));
+ expected.Line, actualLinePosition.Line + 1, DiagnosticVerifier.FormatDiagnostics(analyzer, diagnostic)));
}
}
@@ -183,7 +185,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
{
Assert.IsTrue(false,
string.Format("Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Column, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic)));
+ expected.Column, actualLinePosition.Character + 1, DiagnosticVerifier.FormatDiagnostics(analyzer, diagnostic)));
}
}
}
@@ -201,7 +203,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
var builder = new StringBuilder();
for (int i = 0; i < diagnostics.Length; ++i)
{
- builder.AppendLine("// " + diagnostics[i].ToString());
+ builder.AppendLine("// " + diagnostics[i]);
var analyzerType = analyzer.GetType();
var rules = analyzer.SupportedDiagnostics;
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetCollection.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetCollection.cs
index d160610e..54aa1c6c 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetCollection.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetCollection.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// ReSharper disable CheckNamespace -- matches Stardew Valley's code
using System.Collections;
using System.Collections.Generic;
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetFieldBase.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetFieldBase.cs
index 140c6f59..1c349a0b 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetFieldBase.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetFieldBase.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// ReSharper disable CheckNamespace -- matches Stardew Valley's code
namespace Netcode
{
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetInt.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetInt.cs
index b3abc467..e8e1dc63 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetInt.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetInt.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// ReSharper disable CheckNamespace -- matches Stardew Valley's code
namespace Netcode
{
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetList.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetList.cs
index 1699f71c..f7fb9617 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetList.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetList.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// ReSharper disable CheckNamespace -- matches Stardew Valley's code
using System.Collections;
using System.Collections.Generic;
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetObjectList.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetObjectList.cs
index 7814e7d6..74c17843 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetObjectList.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/Netcode/NetObjectList.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// ReSharper disable CheckNamespace -- matches Stardew Valley's code
namespace Netcode
{
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Farmer.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Farmer.cs
index 13fab069..bdbf9b45 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Farmer.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Farmer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// ReSharper disable CheckNamespace, InconsistentNaming -- matches Stardew Valley's code
#pragma warning disable 649 // (never assigned) -- only used to test type conversions
using System.Collections.Generic;
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Item.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Item.cs
index 1b6317c1..d1f0afc4 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Item.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Item.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// ReSharper disable CheckNamespace, InconsistentNaming -- matches Stardew Valley's code
using Netcode;
@@ -7,27 +9,27 @@ namespace StardewValley
public class Item
{
/// <summary>A net int field with an equivalent non-net <c>Category</c> property.</summary>
- public readonly NetInt category = new NetInt { Value = 42 };
+ public readonly NetInt category = new() { Value = 42 };
/// <summary>A generic net int field with no equivalent non-net property.</summary>
- public readonly NetInt netIntField = new NetInt { Value = 42 };
+ public readonly NetInt netIntField = new() { Value = 42 };
/// <summary>A generic net ref field with no equivalent non-net property.</summary>
- public readonly NetRef<object> netRefField = new NetRef<object>();
+ public readonly NetRef<object> netRefField = new();
/// <summary>A generic net int property with no equivalent non-net property.</summary>
- public NetInt netIntProperty = new NetInt { Value = 42 };
+ public NetInt netIntProperty = new() { Value = 42 };
/// <summary>A generic net ref property with no equivalent non-net property.</summary>
- public NetRef<object> netRefProperty { get; } = new NetRef<object>();
+ public NetRef<object> netRefProperty { get; } = new();
/// <summary>A sample net list.</summary>
- public readonly NetList<int> netList = new NetList<int>();
+ public readonly NetList<int> netList = new();
/// <summary>A sample net object list.</summary>
- public readonly NetObjectList<int> netObjectList = new NetObjectList<int>();
+ public readonly NetObjectList<int> netObjectList = new();
/// <summary>A sample net collection.</summary>
- public readonly NetCollection<int> netCollection = new NetCollection<int>();
+ public readonly NetCollection<int> netCollection = new();
}
}
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Object.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Object.cs
index 3dd66a6d..f54b22fe 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Object.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/Mock/StardewValley/Object.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// ReSharper disable CheckNamespace, InconsistentNaming -- matches Stardew Valley's code
using Netcode;
@@ -7,6 +9,6 @@ namespace StardewValley
public class Object : Item
{
/// <summary>A net int field with an equivalent non-net property.</summary>
- public NetInt type = new NetInt { Value = 42 };
+ public NetInt type = new() { Value = 42 };
}
}
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs
index 89bd1be5..29f3b956 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/NetFieldAnalyzerTests.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;
@@ -93,7 +95,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests
{
// arrange
string code = NetFieldAnalyzerTests.SampleProgram.Replace("{{test-code}}", codeText);
- DiagnosticResult expected = new DiagnosticResult
+ DiagnosticResult expected = new()
{
Id = "AvoidImplicitNetFieldCast",
Message = $"This implicitly converts '{expression}' from {fromType} to {toType}, but {fromType} has unintuitive implicit conversion rules. Consider comparing against the actual value instead to avoid bugs. See https://smapi.io/package/avoid-implicit-net-field-cast for details.",
@@ -135,7 +137,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests
{
// arrange
string code = NetFieldAnalyzerTests.SampleProgram.Replace("{{test-code}}", codeText);
- DiagnosticResult expected = new DiagnosticResult
+ DiagnosticResult expected = new()
{
Id = "AvoidNetField",
Message = $"'{expression}' is a {netType} field; consider using the {suggestedProperty} property instead. See https://smapi.io/package/avoid-net-field for details.",
diff --git a/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs b/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs
index 12641e1a..1cf7369f 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer.Tests/ObsoleteFieldAnalyzerTests.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;
@@ -64,7 +66,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests
{
// arrange
string code = ObsoleteFieldAnalyzerTests.SampleProgram.Replace("{{test-code}}", codeText);
- DiagnosticResult expected = new DiagnosticResult
+ DiagnosticResult expected = new()
{
Id = "AvoidObsoleteField",
Message = $"The '{oldName}' field is obsolete and should be replaced with '{newName}'. See https://smapi.io/package/avoid-obsolete-field for details.",
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerReleases.Shipped.md b/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerReleases.Shipped.md
new file mode 100644
index 00000000..9a46676d
--- /dev/null
+++ b/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerReleases.Shipped.md
@@ -0,0 +1,7 @@
+## Release 2.1.0
+### New Rules
+Rule ID | Category | Severity | Notes
+------------------------- | ------------------ | -------- | ------------------------------------------------------------
+AvoidImplicitNetFieldCast | SMAPI.CommonErrors | Warning | See [documentation](https://smapi.io/package/code-warnings).
+AvoidNetField | SMAPI.CommonErrors | Warning | See [documentation](https://smapi.io/package/code-warnings).
+AvoidObsoleteField | SMAPI.CommonErrors | Warning | See [documentation](https://smapi.io/package/code-warnings).
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs b/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs
index 68b5001e..1cc37b38 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
@@ -64,7 +66,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
}
// conditional access
- if (node is ConditionalAccessExpressionSyntax conditionalAccess && conditionalAccess.WhenNotNull is MemberBindingExpressionSyntax conditionalBinding)
+ if (node is ConditionalAccessExpressionSyntax { WhenNotNull: MemberBindingExpressionSyntax conditionalBinding } conditionalAccess)
{
declaringType = semanticModel.GetTypeInfo(conditionalAccess.Expression).Type;
memberType = semanticModel.GetTypeInfo(node);
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs
index e03c72de..cb2856da 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -132,7 +134,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
};
/// <summary>The diagnostic info for an implicit net field cast.</summary>
- private readonly DiagnosticDescriptor AvoidImplicitNetFieldCastRule = new DiagnosticDescriptor(
+ private readonly DiagnosticDescriptor AvoidImplicitNetFieldCastRule = new(
id: "AvoidImplicitNetFieldCast",
title: "Netcode types shouldn't be implicitly converted",
messageFormat: "This implicitly converts '{0}' from {1} to {2}, but {1} has unintuitive implicit conversion rules. Consider comparing against the actual value instead to avoid bugs. See https://smapi.io/package/avoid-implicit-net-field-cast for details.",
@@ -143,7 +145,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
);
/// <summary>The diagnostic info for an avoidable net field access.</summary>
- private readonly DiagnosticDescriptor AvoidNetFieldRule = new DiagnosticDescriptor(
+ private readonly DiagnosticDescriptor AvoidNetFieldRule = new(
id: "AvoidNetField",
title: "Avoid Netcode types when possible",
messageFormat: "'{0}' is a {1} field; consider using the {2} property instead. See https://smapi.io/package/avoid-net-field for details.",
@@ -227,10 +229,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
// warn: implicit conversion
if (this.IsInvalidConversion(memberType.Type, memberType.ConvertedType))
- {
context.ReportDiagnostic(Diagnostic.Create(this.AvoidImplicitNetFieldCastRule, context.Node.GetLocation(), context.Node, memberType.Type.Name, memberType.ConvertedType));
- return;
- }
});
}
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
index 722d5227..158d7243 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -24,7 +26,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
/// <summary>Describes the diagnostic rule covered by the analyzer.</summary>
private readonly IDictionary<string, DiagnosticDescriptor> Rules = new Dictionary<string, DiagnosticDescriptor>
{
- ["AvoidObsoleteField"] = new DiagnosticDescriptor(
+ ["AvoidObsoleteField"] = new(
id: "AvoidObsoleteField",
title: "Reference to obsolete field",
messageFormat: "The '{0}' field is obsolete and should be replaced with '{1}'. See https://smapi.io/package/avoid-obsolete-field for details.",
@@ -77,7 +79,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
try
{
// get reference info
- if (!AnalyzerUtilities.TryGetMemberInfo(context.Node, context.SemanticModel, out ITypeSymbol declaringType, out TypeInfo memberType, out string memberName))
+ if (!AnalyzerUtilities.TryGetMemberInfo(context.Node, context.SemanticModel, out ITypeSymbol declaringType, out _, out string memberName))
return;
// suggest replacement
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/SMAPI.ModBuildConfig.Analyzer.csproj b/src/SMAPI.ModBuildConfig.Analyzer/SMAPI.ModBuildConfig.Analyzer.csproj
index 3fadc37a..7ac3277e 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/SMAPI.ModBuildConfig.Analyzer.csproj
+++ b/src/SMAPI.ModBuildConfig.Analyzer/SMAPI.ModBuildConfig.Analyzer.csproj
@@ -13,6 +13,10 @@
</ItemGroup>
<ItemGroup>
+ <AdditionalFiles Include="AnalyzerReleases.Shipped.md" />
+ </ItemGroup>
+
+ <ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>
diff --git a/src/SMAPI.ModBuildConfig/DeployModTask.cs b/src/SMAPI.ModBuildConfig/DeployModTask.cs
index 140933bd..43fac9d5 100644
--- a/src/SMAPI.ModBuildConfig/DeployModTask.cs
+++ b/src/SMAPI.ModBuildConfig/DeployModTask.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -88,7 +90,7 @@ namespace StardewModdingAPI.ModBuildConfig
Regex[] ignoreFilePatterns = this.GetCustomIgnorePatterns().ToArray();
// get mod info
- ModFileManager package = new ModFileManager(this.ProjectDir, this.TargetDir, ignoreFilePaths, ignoreFilePatterns, bundleAssemblyTypes, this.ModDllName, validateRequiredModFiles: this.EnableModDeploy || this.EnableModZip);
+ ModFileManager package = new(this.ProjectDir, this.TargetDir, ignoreFilePaths, ignoreFilePatterns, bundleAssemblyTypes, this.ModDllName, validateRequiredModFiles: this.EnableModDeploy || this.EnableModZip);
// deploy mod files
if (this.EnableModDeploy)
@@ -246,7 +248,7 @@ namespace StardewModdingAPI.ModBuildConfig
// create zip file
Directory.CreateDirectory(Path.GetDirectoryName(zipPath)!);
using Stream zipStream = new FileStream(zipPath, FileMode.Create, FileAccess.Write);
- using ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create);
+ using ZipArchive archive = new(zipStream, ZipArchiveMode.Create);
foreach (var fileEntry in files)
{
diff --git a/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs b/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs
index ad4ffdf9..ad2c0de3 100644
--- a/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs
+++ b/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -136,7 +138,7 @@ namespace StardewModdingAPI.ModBuildConfig.Framework
// project manifest
bool hasProjectManifest = false;
{
- FileInfo manifest = new FileInfo(Path.Combine(projectDir, this.ManifestFileName));
+ FileInfo manifest = new(Path.Combine(projectDir, this.ManifestFileName));
if (manifest.Exists)
{
yield return Tuple.Create(this.ManifestFileName, manifest);
@@ -146,7 +148,7 @@ namespace StardewModdingAPI.ModBuildConfig.Framework
// project i18n files
bool hasProjectTranslations = false;
- DirectoryInfo translationsFolder = new DirectoryInfo(Path.Combine(projectDir, "i18n"));
+ DirectoryInfo translationsFolder = new(Path.Combine(projectDir, "i18n"));
if (translationsFolder.Exists)
{
foreach (FileInfo file in translationsFolder.EnumerateFiles())
@@ -156,7 +158,7 @@ namespace StardewModdingAPI.ModBuildConfig.Framework
// project assets folder
bool hasAssetsFolder = false;
- DirectoryInfo assetsFolder = new DirectoryInfo(Path.Combine(projectDir, "assets"));
+ DirectoryInfo assetsFolder = new(Path.Combine(projectDir, "assets"));
if (assetsFolder.Exists)
{
foreach (FileInfo file in assetsFolder.EnumerateFiles("*", SearchOption.AllDirectories))
@@ -168,7 +170,7 @@ namespace StardewModdingAPI.ModBuildConfig.Framework
}
// build output
- DirectoryInfo buildFolder = new DirectoryInfo(targetDir);
+ DirectoryInfo buildFolder = new(targetDir);
foreach (FileInfo file in buildFolder.EnumerateFiles("*", SearchOption.AllDirectories))
{
// get path info
diff --git a/src/SMAPI.ModBuildConfig/Framework/UserErrorException.cs b/src/SMAPI.ModBuildConfig/Framework/UserErrorException.cs
index 64e31c29..588118ef 100644
--- a/src/SMAPI.ModBuildConfig/Framework/UserErrorException.cs
+++ b/src/SMAPI.ModBuildConfig/Framework/UserErrorException.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.ModBuildConfig.Framework
diff --git a/src/SMAPI.ModBuildConfig/SMAPI.ModBuildConfig.csproj b/src/SMAPI.ModBuildConfig/SMAPI.ModBuildConfig.csproj
index 0bc8c45e..82eac7f6 100644
--- a/src/SMAPI.ModBuildConfig/SMAPI.ModBuildConfig.csproj
+++ b/src/SMAPI.ModBuildConfig/SMAPI.ModBuildConfig.csproj
@@ -5,6 +5,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+ <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<!--NuGet package-->
<PackageId>Pathoschild.Stardew.ModBuildConfig</PackageId>
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs
index 7e157c38..8fcbf711 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ArgumentParser.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections;
using System.Collections.Generic;
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ConsoleCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ConsoleCommand.cs
index 01cab92e..a8dd41f5 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ConsoleCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/ConsoleCommand.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -100,7 +102,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands
List<string[]> lines = new List<string[]>(rows.Length + 2)
{
header,
- header.Select((value, i) => "".PadRight(widths[i], '-')).ToArray()
+ header.Select((_, i) => "".PadRight(widths[i], '-')).ToArray()
};
lines.AddRange(rows);
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/IConsoleCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/IConsoleCommand.cs
index 9c82bbd3..4c6df538 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/IConsoleCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/IConsoleCommand.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands
{
/// <summary>A console command to register.</summary>
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs
index 957b0e75..f31457ed 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs
@@ -1,10 +1,14 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
{
/// <summary>A command which runs one of the game's save migrations.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class ApplySaveFixCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/DebugCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/DebugCommand.cs
index 1955c14e..f289c669 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/DebugCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/DebugCommand.cs
@@ -1,8 +1,12 @@
-using StardewValley;
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
+using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
{
/// <summary>A command which sends a debug command to the game.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class DebugCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/RegenerateBundles.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/RegenerateBundles.cs
index 9beedb96..81a8c570 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/RegenerateBundles.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/RegenerateBundles.cs
@@ -1,5 +1,8 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Netcode;
@@ -9,6 +12,7 @@ using StardewValley.Network;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
{
/// <summary>A command which regenerates the game's bundles.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class RegenerateBundlesCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowDataFilesCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowDataFilesCommand.cs
index 27f6ce53..d762d8bf 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowDataFilesCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowDataFilesCommand.cs
@@ -1,8 +1,12 @@
-using System.Diagnostics;
+#nullable disable
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
{
/// <summary>A command which shows the data files.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class ShowDataFilesCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowGameFilesCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowGameFilesCommand.cs
index b97cb3e6..b5733eb9 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowGameFilesCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ShowGameFilesCommand.cs
@@ -1,8 +1,12 @@
+#nullable disable
+
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
{
/// <summary>A command which shows the game files.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class ShowGameFilesCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs
index 46583dc3..5484fc7c 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/TestInputCommand.cs
@@ -1,8 +1,12 @@
+#nullable disable
+
using System;
+using System.Diagnostics.CodeAnalysis;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
{
/// <summary>A command which logs the keys being pressed for 30 seconds once enabled.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class TestInputCommand : ConsoleCommand
{
/*********
@@ -37,9 +41,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other
public override void OnUpdated(IMonitor monitor)
{
// handle expiry
- if (this.ExpiryTicks == null)
- return;
- if (this.ExpiryTicks <= DateTime.UtcNow.Ticks)
+ if (this.ExpiryTicks != null && this.ExpiryTicks <= DateTime.UtcNow.Ticks)
{
monitor.Log("No longer logging input.", LogLevel.Info);
this.ExpiryTicks = null;
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs
index 0e8f7517..0d8db870 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/AddCommand.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Linq;
using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData;
@@ -13,7 +15,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
** Fields
*********/
/// <summary>Provides methods for searching and constructing items.</summary>
- private readonly ItemRepository Items = new ItemRepository();
+ private readonly ItemRepository Items = new();
/// <summary>The type names recognized by this command.</summary>
private readonly string[] ValidTypes = Enum.GetNames(typeof(ItemType)).Concat(new[] { "Name" }).ToArray();
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs
index 1f12e5f9..e57d4065 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemTypesCommand.cs
@@ -1,16 +1,20 @@
-using System.Linq;
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which list item types.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class ListItemTypesCommand : ConsoleCommand
{
/*********
** Fields
*********/
/// <summary>Provides methods for searching and constructing items.</summary>
- private readonly ItemRepository Items = new ItemRepository();
+ private readonly ItemRepository Items = new();
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs
index 67569298..5a21b459 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/ListItemsCommand.cs
@@ -1,18 +1,22 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which list items available to spawn.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class ListItemsCommand : ConsoleCommand
{
/*********
** Fields
*********/
/// <summary>Provides methods for searching and constructing items.</summary>
- private readonly ItemRepository Items = new ItemRepository();
+ private readonly ItemRepository Items = new();
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetColorCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetColorCommand.cs
index 7b7cbf83..e8605163 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetColorCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetColorCommand.cs
@@ -1,9 +1,13 @@
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
using Microsoft.Xna.Framework;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which edits the color of a player feature.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetColorCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs
index 6fb399ae..02670911 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetFarmTypeCommand.cs
@@ -1,6 +1,9 @@
+#nullable disable
+
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Text;
@@ -10,6 +13,7 @@ using StardewValley.GameData;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which changes the player's farm type.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetFarmTypeCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs
index f27b336f..1a1a9eab 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetHealthCommand.cs
@@ -1,9 +1,13 @@
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which edits the player's current health.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetHealthCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetImmunityCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetImmunityCommand.cs
index df90adf2..d1dede1f 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetImmunityCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetImmunityCommand.cs
@@ -1,9 +1,13 @@
-using System.Linq;
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which edits the player's current immunity.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetImmunityCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxHealthCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxHealthCommand.cs
index a5f7f444..2b3b140c 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxHealthCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxHealthCommand.cs
@@ -1,9 +1,13 @@
-using System.Linq;
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which edits the player's maximum health.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetMaxHealthCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxStaminaCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxStaminaCommand.cs
index e3c2f011..f9ed6c58 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxStaminaCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMaxStaminaCommand.cs
@@ -1,9 +1,13 @@
-using System.Linq;
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which edits the player's maximum stamina.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetMaxStaminaCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs
index 787ce920..56447a65 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetMoneyCommand.cs
@@ -1,9 +1,13 @@
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which edits the player's current money.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetMoneyCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetNameCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetNameCommand.cs
index 4911ad1c..4ce7e1f8 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetNameCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetNameCommand.cs
@@ -1,8 +1,12 @@
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which edits the player's name.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetNameCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs
index c78378ef..ea8d74c2 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStaminaCommand.cs
@@ -1,9 +1,13 @@
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which edits the player's current stamina.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetStaminaCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStyleCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStyleCommand.cs
index 98f6c330..84625a34 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStyleCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Player/SetStyleCommand.cs
@@ -1,8 +1,12 @@
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Player
{
/// <summary>A command which edits a player style.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetStyleCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs
index ceaeb278..92c73e08 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/ClearCommand.cs
@@ -1,4 +1,7 @@
+#nullable disable
+
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewValley;
using StardewValley.Locations;
@@ -9,6 +12,7 @@ using SObject = StardewValley.Object;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
/// <summary>A command which clears in-game objects.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class ClearCommand : ConsoleCommand
{
/*********
@@ -92,11 +96,10 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
removed +=
this.RemoveObjects(location, obj =>
- !(obj is Chest)
+ obj is not Chest
&& (
- obj.Name == "Weeds"
- || obj.Name == "Stone"
- || (obj.ParentSheetIndex == 294 || obj.ParentSheetIndex == 295)
+ obj.Name is "Weeds" or "Stone"
+ || obj.ParentSheetIndex is 294 or 295
)
)
+ this.RemoveResourceClumps(location, clump => this.DebrisClumps.Contains(clump.parentSheetIndex.Value));
@@ -114,7 +117,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
case "furniture":
{
- int removed = this.RemoveFurniture(location, furniture => true);
+ int removed = this.RemoveFurniture(location, _ => true);
monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
break;
}
@@ -138,11 +141,11 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
bool everything = type == "everything";
int removed =
- this.RemoveFurniture(location, p => true)
- + this.RemoveObjects(location, p => true)
- + this.RemoveTerrainFeatures(location, p => true)
- + this.RemoveLargeTerrainFeatures(location, p => everything || !(p is Bush bush) || bush.isDestroyable(location, p.currentTileLocation))
- + this.RemoveResourceClumps(location, p => true);
+ this.RemoveFurniture(location, _ => true)
+ + this.RemoveObjects(location, _ => true)
+ + this.RemoveTerrainFeatures(location, _ => true)
+ + this.RemoveLargeTerrainFeatures(location, p => everything || p is not Bush bush || bush.isDestroyable(location, p.currentTileLocation))
+ + this.RemoveResourceClumps(location, _ => true);
monitor.Log($"Done! Removed {removed} entities from {location.Name}.", LogLevel.Info);
break;
}
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/DownMineLevelCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/DownMineLevelCommand.cs
index 0aa9c9c3..0f18c760 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/DownMineLevelCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/DownMineLevelCommand.cs
@@ -1,9 +1,13 @@
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
using StardewValley;
using StardewValley.Locations;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
/// <summary>A command which moves the player to the next mine level.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class DownMineLevelCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs
index 16faa2fe..8808fe35 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/FreezeTimeCommand.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Linq;
using StardewValley;
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/HurryAllCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/HurryAllCommand.cs
index 2deac5f8..f9810dc3 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/HurryAllCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/HurryAllCommand.cs
@@ -1,9 +1,13 @@
+#nullable disable
+
using System;
+using System.Diagnostics.CodeAnalysis;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
/// <summary>A command which immediately warps all NPCs to their scheduled positions. To hurry a single NPC, see <c>debug hurry npc-name</c> instead.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class HurryAllCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetDayCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetDayCommand.cs
index 4028b3dc..8aa27d93 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetDayCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetDayCommand.cs
@@ -1,10 +1,14 @@
-using System.Linq;
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using StardewModdingAPI.Utilities;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
/// <summary>A command which sets the current day.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetDayCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetMineLevelCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetMineLevelCommand.cs
index 40f4b19f..ad6ac777 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetMineLevelCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetMineLevelCommand.cs
@@ -1,9 +1,13 @@
+#nullable disable
+
using System;
+using System.Diagnostics.CodeAnalysis;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
/// <summary>A command which moves the player to the given mine level.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetMineLevelCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs
index a4cb35bb..ebe58913 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetSeasonCommand.cs
@@ -1,3 +1,6 @@
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using StardewModdingAPI.Utilities;
using StardewValley;
@@ -5,6 +8,7 @@ using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
/// <summary>A command which sets the current season.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetSeasonCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs
index 2d4b4565..1e6bab96 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetTimeCommand.cs
@@ -1,3 +1,6 @@
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Xna.Framework;
using StardewValley;
@@ -5,6 +8,7 @@ using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
/// <summary>A command which sets the current time.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetTimeCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetYearCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetYearCommand.cs
index 95401962..995f222e 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetYearCommand.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/World/SetYearCommand.cs
@@ -1,10 +1,14 @@
-using System.Linq;
+#nullable disable
+
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using StardewModdingAPI.Utilities;
using StardewValley;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.World
{
/// <summary>A command which sets the current year.</summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Loaded using reflection")]
internal class SetYearCommand : ConsoleCommand
{
/*********
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemData/SearchableItem.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemData/SearchableItem.cs
index 72d01eb7..ab0b2e05 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemData/SearchableItem.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemData/SearchableItem.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewValley;
@@ -43,16 +45,6 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData
this.Item = createItem(this);
}
- /// <summary>Construct an instance.</summary>
- /// <param name="item">The item metadata to copy.</param>
- public SearchableItem(SearchableItem item)
- {
- this.Type = item.Type;
- this.ID = item.ID;
- this.CreateItem = item.CreateItem;
- this.Item = item.Item;
- }
-
/// <summary>Get whether the item name contains a case-insensitive substring.</summary>
/// <param name="substring">The substring to find.</param>
public bool NameContains(string substring)
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
index 8704a403..7d2a1662 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -106,8 +108,8 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
{
foreach (int id in this.TryLoad<int, string>("Data\\weapons").Keys)
{
- yield return this.TryCreate(ItemType.Weapon, id, p => (p.ID >= 32 && p.ID <= 34)
- ? (Item)new Slingshot(p.ID)
+ yield return this.TryCreate(ItemType.Weapon, id, p => p.ID is >= 32 and <= 34
+ ? new Slingshot(p.ID)
: new MeleeWeapon(p.ID)
);
}
@@ -285,7 +287,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
case SObject.flowersCategory:
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset * 5 + id, _ =>
{
- SObject honey = new SObject(Vector2.Zero, 340, $"{item.Name} Honey", false, true, false, false)
+ SObject honey = new(Vector2.Zero, 340, $"{item.Name} Honey", false, true, false, false)
{
Name = $"{item.Name} Honey",
preservedParentSheetIndex = { id }
diff --git a/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs b/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs
index 91437fd3..e3ca1a39 100644
--- a/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/ModEntry.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -71,7 +73,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands
/// <param name="args">The command arguments.</param>
private void HandleCommand(IConsoleCommand command, string commandName, string[] args)
{
- ArgumentParser argParser = new ArgumentParser(commandName, args, this.Monitor);
+ ArgumentParser argParser = new(commandName, args, this.Monitor);
command.Handle(this.Monitor, commandName, argParser);
}
diff --git a/src/SMAPI.Mods.ErrorHandler/ModEntry.cs b/src/SMAPI.Mods.ErrorHandler/ModEntry.cs
index 2d6242cf..fa171012 100644
--- a/src/SMAPI.Mods.ErrorHandler/ModEntry.cs
+++ b/src/SMAPI.Mods.ErrorHandler/ModEntry.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Reflection;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs
index 7a3af39c..b05c8cca 100644
--- a/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs
index 1b706147..63674d09 100644
--- a/src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatcher.cs
index 7df6b0a2..98aa4a38 100644
--- a/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatcher.cs
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/IClickableMenuPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/IClickableMenuPatcher.cs
index b65a695a..85ce8ac4 100644
--- a/src/SMAPI.Mods.ErrorHandler/Patches/IClickableMenuPatcher.cs
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/IClickableMenuPatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
using StardewModdingAPI.Internal.Patching;
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/NpcPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/NpcPatcher.cs
index 275bb5bf..5354f724 100644
--- a/src/SMAPI.Mods.ErrorHandler/Patches/NpcPatcher.cs
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/NpcPatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/ObjectPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/ObjectPatcher.cs
index fd4ea35c..499718b0 100644
--- a/src/SMAPI.Mods.ErrorHandler/Patches/ObjectPatcher.cs
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/ObjectPatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/SaveGamePatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/SaveGamePatcher.cs
index 0a7ed212..1941d2a8 100644
--- a/src/SMAPI.Mods.ErrorHandler/Patches/SaveGamePatcher.cs
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/SaveGamePatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -77,7 +79,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
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 _))
+ if (__exception?.Message.Contains("not a valid farm type") == true && !int.TryParse(SaveGame.loaded.whichFarm, out _))
{
SaveGamePatcher.Monitor.Log(__exception.GetLogSummary(), LogLevel.Error);
SaveGamePatcher.Monitor.Log($"Removed invalid custom farm type '{SaveGame.loaded.whichFarm}' to avoid a crash when loading save '{Constants.SaveFolderName}'. (Did you remove a custom farm type mod?)", LogLevel.Warn);
@@ -121,7 +123,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
{
try
{
- BluePrint _ = new BluePrint(building.buildingType.Value);
+ BluePrint _ = new(building.buildingType.Value);
}
catch (ContentLoadException)
{
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchPatcher.cs
index f243c6d1..b4c03bb9 100644
--- a/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchPatcher.cs
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchPatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/UtilityPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/UtilityPatcher.cs
index ce85d0c2..108ed585 100644
--- a/src/SMAPI.Mods.ErrorHandler/Patches/UtilityPatcher.cs
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/UtilityPatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs
index b89bb9c3..b2b41ca6 100644
--- a/src/SMAPI.Mods.SaveBackup/ModEntry.cs
+++ b/src/SMAPI.Mods.SaveBackup/ModEntry.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics;
using System.IO;
@@ -38,13 +40,13 @@ namespace StardewModdingAPI.Mods.SaveBackup
try
{
// init backup folder
- DirectoryInfo backupFolder = new DirectoryInfo(this.BackupFolder);
+ DirectoryInfo backupFolder = new(this.BackupFolder);
backupFolder.Create();
// back up & prune saves
Task
.Run(() => this.CreateBackup(backupFolder))
- .ContinueWith(backupTask => this.PruneBackups(backupFolder, this.BackupsToKeep));
+ .ContinueWith(_ => this.PruneBackups(backupFolder, this.BackupsToKeep));
}
catch (Exception ex)
{
@@ -63,8 +65,8 @@ namespace StardewModdingAPI.Mods.SaveBackup
try
{
// get target path
- FileInfo targetFile = new FileInfo(Path.Combine(backupFolder.FullName, this.FileName));
- DirectoryInfo fallbackDir = new DirectoryInfo(Path.Combine(backupFolder.FullName, this.BackupLabel));
+ FileInfo targetFile = new(Path.Combine(backupFolder.FullName, this.FileName));
+ DirectoryInfo fallbackDir = new(Path.Combine(backupFolder.FullName, this.BackupLabel));
if (targetFile.Exists || fallbackDir.Exists)
{
this.Monitor.Log("Already backed up today.");
@@ -72,7 +74,7 @@ namespace StardewModdingAPI.Mods.SaveBackup
}
// copy saves to fallback directory (ignore non-save files/folders)
- DirectoryInfo savesDir = new DirectoryInfo(Constants.SavesPath);
+ DirectoryInfo savesDir = new(Constants.SavesPath);
if (!this.RecursiveCopy(savesDir, fallbackDir, entry => this.MatchSaveFolders(savesDir, entry), copyRoot: false))
{
this.Monitor.Log("No saves found.");
@@ -170,8 +172,8 @@ namespace StardewModdingAPI.Mods.SaveBackup
try
{
// create compressed backup
- Assembly coreAssembly = Assembly.Load("System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ?? throw new InvalidOperationException("Can't load System.IO.Compression assembly.");
- Assembly fsAssembly = Assembly.Load("System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ?? throw new InvalidOperationException("Can't load System.IO.Compression assembly.");
+ Assembly coreAssembly = Assembly.Load("System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
+ Assembly fsAssembly = Assembly.Load("System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
Type compressionLevelType = coreAssembly.GetType("System.IO.Compression.CompressionLevel") ?? throw new InvalidOperationException("Can't load CompressionLevel type.");
Type zipFileType = fsAssembly.GetType("System.IO.Compression.ZipFile") ?? throw new InvalidOperationException("Can't load ZipFile type.");
createFromDirectory = zipFileType.GetMethod("CreateFromDirectory", new[] { typeof(string), typeof(string), compressionLevelType, typeof(bool) }) ?? throw new InvalidOperationException("Can't load ZipFile.CreateFromDirectory method.");
@@ -190,8 +192,8 @@ namespace StardewModdingAPI.Mods.SaveBackup
/// <param name="destination">The destination file to create.</param>
private void CompressUsingMacProcess(string sourcePath, FileInfo destination)
{
- DirectoryInfo saveFolder = new DirectoryInfo(sourcePath);
- ProcessStartInfo startInfo = new ProcessStartInfo
+ DirectoryInfo saveFolder = new(sourcePath);
+ ProcessStartInfo startInfo = new()
{
FileName = "zip",
Arguments = $"-rq \"{destination.FullName}\" \"{saveFolder.Name}\" -x \"*.DS_Store\" -x \"__MACOSX\"",
diff --git a/src/SMAPI.Tests.ModApiConsumer/ApiConsumer.cs b/src/SMAPI.Tests.ModApiConsumer/ApiConsumer.cs
index 2c7f9952..285dd259 100644
--- a/src/SMAPI.Tests.ModApiConsumer/ApiConsumer.cs
+++ b/src/SMAPI.Tests.ModApiConsumer/ApiConsumer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using SMAPI.Tests.ModApiConsumer.Interfaces;
@@ -17,7 +19,7 @@ namespace SMAPI.Tests.ModApiConsumer
// act
int calls = 0;
int lastValue = -1;
- api.OnEventRaised += (sender, value) =>
+ api.OnEventRaised += (_, value) =>
{
calls++;
lastValue = value;
@@ -34,7 +36,7 @@ namespace SMAPI.Tests.ModApiConsumer
// act
int calls = 0;
int lastValue = -1;
- api.OnEventRaisedProperty += (sender, value) =>
+ api.OnEventRaisedProperty += (_, value) =>
{
calls++;
lastValue = value;
diff --git a/src/SMAPI.Tests.ModApiConsumer/Interfaces/ISimpleApi.cs b/src/SMAPI.Tests.ModApiConsumer/Interfaces/ISimpleApi.cs
index 7f94e137..23491fd1 100644
--- a/src/SMAPI.Tests.ModApiConsumer/Interfaces/ISimpleApi.cs
+++ b/src/SMAPI.Tests.ModApiConsumer/Interfaces/ISimpleApi.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Reflection;
diff --git a/src/SMAPI.Tests.ModApiProvider/Framework/BaseApi.cs b/src/SMAPI.Tests.ModApiProvider/Framework/BaseApi.cs
index 8092e3e7..b5870baa 100644
--- a/src/SMAPI.Tests.ModApiProvider/Framework/BaseApi.cs
+++ b/src/SMAPI.Tests.ModApiProvider/Framework/BaseApi.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace SMAPI.Tests.ModApiProvider.Framework
{
/// <summary>The base class for <see cref="SimpleApi"/>.</summary>
diff --git a/src/SMAPI.Tests.ModApiProvider/Framework/SimpleApi.cs b/src/SMAPI.Tests.ModApiProvider/Framework/SimpleApi.cs
index 1100af36..82e902f5 100644
--- a/src/SMAPI.Tests.ModApiProvider/Framework/SimpleApi.cs
+++ b/src/SMAPI.Tests.ModApiProvider/Framework/SimpleApi.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Reflection;
diff --git a/src/SMAPI.Tests.ModApiProvider/ProviderMod.cs b/src/SMAPI.Tests.ModApiProvider/ProviderMod.cs
index c36e1c6d..3fc8d749 100644
--- a/src/SMAPI.Tests.ModApiProvider/ProviderMod.cs
+++ b/src/SMAPI.Tests.ModApiProvider/ProviderMod.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Reflection;
using SMAPI.Tests.ModApiProvider.Framework;
diff --git a/src/SMAPI.Tests/Core/AssetNameTests.cs b/src/SMAPI.Tests/Core/AssetNameTests.cs
index 8785aab8..ef8a08ef 100644
--- a/src/SMAPI.Tests/Core/AssetNameTests.cs
+++ b/src/SMAPI.Tests/Core/AssetNameTests.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using FluentAssertions;
@@ -32,8 +34,7 @@ namespace SMAPI.Tests.Core
name = PathUtilities.NormalizeAssetName(name);
// act
- string calledWithLocale = null;
- IAssetName assetName = AssetName.Parse(name, parseLocale: locale => expectedLanguageCode);
+ IAssetName assetName = AssetName.Parse(name, parseLocale: _ => expectedLanguageCode);
// assert
assetName.Name.Should()
@@ -161,7 +162,7 @@ namespace SMAPI.Tests.Core
AssetName name = AssetName.Parse(mainAssetName, _ => null);
// assert value is the same for any combination of options
- bool result = name.StartsWith(prefix, true, true);
+ bool result = name.StartsWith(prefix);
foreach (bool allowPartialWord in new[] { true, false })
{
foreach (bool allowSubfolder in new[] { true, true })
diff --git a/src/SMAPI.Tests/Core/InterfaceProxyTests.cs b/src/SMAPI.Tests/Core/InterfaceProxyTests.cs
index 99c1298f..1bf2ed68 100644
--- a/src/SMAPI.Tests/Core/InterfaceProxyTests.cs
+++ b/src/SMAPI.Tests/Core/InterfaceProxyTests.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/SMAPI.Tests/Core/ModResolverTests.cs b/src/SMAPI.Tests/Core/ModResolverTests.cs
index 1755f644..e1b56559 100644
--- a/src/SMAPI.Tests/Core/ModResolverTests.cs
+++ b/src/SMAPI.Tests/Core/ModResolverTests.cs
@@ -50,11 +50,11 @@ namespace SMAPI.Tests.Core
// act
IModMetadata[] mods = new ModResolver().ReadManifests(new ModToolkit(), rootFolder, new ModDatabase()).ToArray();
- IModMetadata mod = mods.FirstOrDefault();
+ IModMetadata? mod = mods.FirstOrDefault();
// assert
Assert.AreEqual(1, mods.Length, 0, $"Expected to find one manifest, found {mods.Length} instead.");
- Assert.AreEqual(ModMetadataStatus.Failed, mod.Status, "The mod metadata was not marked failed.");
+ Assert.AreEqual(ModMetadataStatus.Failed, mod!.Status, "The mod metadata was not marked failed.");
Assert.IsNotNull(mod.Error, "The mod metadata did not have an error message set.");
}
@@ -89,12 +89,12 @@ namespace SMAPI.Tests.Core
// act
IModMetadata[] mods = new ModResolver().ReadManifests(new ModToolkit(), rootFolder, new ModDatabase()).ToArray();
- IModMetadata mod = mods.FirstOrDefault();
+ IModMetadata? mod = mods.FirstOrDefault();
// assert
Assert.AreEqual(1, mods.Length, 0, "Expected to find one manifest.");
Assert.IsNotNull(mod, "The loaded manifest shouldn't be null.");
- Assert.AreEqual(null, mod.DataRecord, "The data record should be null since we didn't provide one.");
+ Assert.AreEqual(null, mod!.DataRecord, "The data record should be null since we didn't provide one.");
Assert.AreEqual(modFolder, mod.DirectoryPath, "The directory path doesn't match.");
Assert.AreEqual(null, mod.Error, "The error should be null since parsing should have succeeded.");
Assert.AreEqual(ModMetadataStatus.Found, mod.Status, "The status doesn't match.");
@@ -123,7 +123,7 @@ namespace SMAPI.Tests.Core
[Test(Description = "Assert that validation doesn't fail if there are no mods installed.")]
public void ValidateManifests_NoMods_DoesNothing()
{
- new ModResolver().ValidateManifests(Array.Empty<ModMetadata>(), apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
+ new ModResolver().ValidateManifests(Array.Empty<ModMetadata>(), apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null);
}
[Test(Description = "Assert that validation skips manifests that have already failed without calling any other properties.")]
@@ -134,7 +134,7 @@ namespace SMAPI.Tests.Core
mock.Setup(p => p.Status).Returns(ModMetadataStatus.Failed);
// act
- new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
+ new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null);
// assert
mock.VerifyGet(p => p.Status, Times.Once, "The validation did not check the manifest status.");
@@ -151,7 +151,7 @@ namespace SMAPI.Tests.Core
});
// act
- new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
+ new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null);
// assert
mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
@@ -166,7 +166,7 @@ namespace SMAPI.Tests.Core
this.SetupMetadataForValidation(mock);
// act
- new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
+ new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null);
// assert
mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
@@ -180,7 +180,7 @@ namespace SMAPI.Tests.Core
this.SetupMetadataForValidation(mock);
// act
- new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
+ new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null);
// assert
mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
@@ -197,7 +197,7 @@ namespace SMAPI.Tests.Core
this.SetupMetadataForValidation(mod);
// act
- new ModResolver().ValidateManifests(new[] { modA.Object, modB.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
+ new ModResolver().ValidateManifests(new[] { modA.Object, modB.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null);
// assert
modA.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the first mod with a unique ID.");
@@ -213,7 +213,7 @@ namespace SMAPI.Tests.Core
// create DLL
string modFolder = Path.Combine(this.GetTempFolderPath(), Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(modFolder);
- File.WriteAllText(Path.Combine(modFolder, manifest.EntryDll), "");
+ File.WriteAllText(Path.Combine(modFolder, manifest.EntryDll!), "");
// arrange
Mock<IModMetadata> mock = new Mock<IModMetadata>(MockBehavior.Strict);
@@ -223,7 +223,7 @@ namespace SMAPI.Tests.Core
mock.Setup(p => p.DirectoryPath).Returns(modFolder);
// act
- new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
+ new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: _ => null);
// assert
// if Moq doesn't throw a method-not-setup exception, the validation didn't override the status.
@@ -478,21 +478,20 @@ namespace SMAPI.Tests.Core
/// <param name="contentPackForID">The <see cref="IManifest.ContentPackFor"/> value.</param>
/// <param name="minimumApiVersion">The <see cref="IManifest.MinimumApiVersion"/> value.</param>
/// <param name="dependencies">The <see cref="IManifest.Dependencies"/> value.</param>
- private Manifest GetManifest(string id = null, string name = null, string version = null, string entryDll = null, string contentPackForID = null, string minimumApiVersion = null, IManifestDependency[] dependencies = null)
+ private Manifest GetManifest(string? id = null, string? name = null, string? version = null, string? entryDll = null, string? contentPackForID = null, string? minimumApiVersion = null, IManifestDependency[]? dependencies = null)
{
- return new Manifest
- {
- UniqueID = id ?? $"{Sample.String()}.{Sample.String()}",
- Name = name ?? id ?? Sample.String(),
- Author = Sample.String(),
- Description = Sample.String(),
- Version = version != null ? new SemanticVersion(version) : new SemanticVersion(Sample.Int(), Sample.Int(), Sample.Int(), Sample.String()),
- EntryDll = entryDll ?? $"{Sample.String()}.dll",
- ContentPackFor = contentPackForID != null ? new ManifestContentPackFor { UniqueID = contentPackForID } : null,
- MinimumApiVersion = minimumApiVersion != null ? new SemanticVersion(minimumApiVersion) : null,
- Dependencies = dependencies ?? Array.Empty<IManifestDependency>(),
- UpdateKeys = Array.Empty<string>()
- };
+ return new Manifest(
+ uniqueId: id ?? $"{Sample.String()}.{Sample.String()}",
+ name: name ?? id ?? Sample.String(),
+ author: Sample.String(),
+ description: Sample.String(),
+ version: version != null ? new SemanticVersion(version) : new SemanticVersion(Sample.Int(), Sample.Int(), Sample.Int(), Sample.String()),
+ entryDll: entryDll ?? $"{Sample.String()}.dll",
+ contentPackFor: contentPackForID != null ? new ManifestContentPackFor(contentPackForID, null) : null,
+ minimumApiVersion: minimumApiVersion != null ? new SemanticVersion(minimumApiVersion) : null,
+ dependencies: dependencies ?? Array.Empty<IManifestDependency>(),
+ updateKeys: Array.Empty<string>()
+ );
}
/// <summary>Get a randomized basic manifest.</summary>
@@ -508,7 +507,7 @@ namespace SMAPI.Tests.Core
/// <param name="allowStatusChange">Whether the code being tested is allowed to change the mod status.</param>
private Mock<IModMetadata> GetMetadata(string uniqueID, string[] dependencies, bool allowStatusChange = false)
{
- IManifest manifest = this.GetManifest(id: uniqueID, version: "1.0", dependencies: dependencies?.Select(dependencyID => (IManifestDependency)new ManifestDependency(dependencyID, null)).ToArray());
+ IManifest manifest = this.GetManifest(id: uniqueID, version: "1.0", dependencies: dependencies?.Select(dependencyID => (IManifestDependency)new ManifestDependency(dependencyID, null as ISemanticVersion)).ToArray());
return this.GetMetadata(manifest, allowStatusChange);
}
@@ -536,7 +535,7 @@ namespace SMAPI.Tests.Core
/// <summary>Set up a mock mod metadata for <see cref="ModResolver.ValidateManifests"/>.</summary>
/// <param name="mod">The mock mod metadata.</param>
/// <param name="modRecord">The extra metadata about the mod from SMAPI's internal data (if any).</param>
- private void SetupMetadataForValidation(Mock<IModMetadata> mod, ModDataRecordVersionedFields modRecord = null)
+ private void SetupMetadataForValidation(Mock<IModMetadata> mod, ModDataRecordVersionedFields? modRecord = null)
{
mod.Setup(p => p.Status).Returns(ModMetadataStatus.Found);
mod.Setup(p => p.DataRecord).Returns(() => null);
diff --git a/src/SMAPI.Tests/Core/TranslationTests.cs b/src/SMAPI.Tests/Core/TranslationTests.cs
index 457f9fad..f8f0e315 100644
--- a/src/SMAPI.Tests/Core/TranslationTests.cs
+++ b/src/SMAPI.Tests/Core/TranslationTests.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -116,7 +118,7 @@ namespace SMAPI.Tests.Core
public void Translation_ToString([ValueSource(nameof(TranslationTests.Samples))] string text)
{
// act
- Translation translation = new Translation("pt-BR", "key", text);
+ Translation translation = new("pt-BR", "key", text);
// assert
if (translation.HasValue())
@@ -129,7 +131,7 @@ namespace SMAPI.Tests.Core
public void Translation_ImplicitStringConversion([ValueSource(nameof(TranslationTests.Samples))] string text)
{
// act
- Translation translation = new Translation("pt-BR", "key", text);
+ Translation translation = new("pt-BR", "key", text);
// assert
if (translation.HasValue())
@@ -182,7 +184,7 @@ namespace SMAPI.Tests.Core
string expected = $"{start} tokens are properly replaced (including {middle} {middle}) {end}";
// act
- Translation translation = new Translation("pt-BR", "key", input);
+ Translation translation = new("pt-BR", "key", input);
switch (structure)
{
case "anonymous object":
diff --git a/src/SMAPI.Tests/Sample.cs b/src/SMAPI.Tests/Sample.cs
index f4f0d88e..6d4339ca 100644
--- a/src/SMAPI.Tests/Sample.cs
+++ b/src/SMAPI.Tests/Sample.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace SMAPI.Tests
@@ -9,7 +11,7 @@ namespace SMAPI.Tests
** Fields
*********/
/// <summary>A random number generator.</summary>
- private static readonly Random Random = new Random();
+ private static readonly Random Random = new();
/*********
diff --git a/src/SMAPI.Tests/Utilities/KeybindListTests.cs b/src/SMAPI.Tests/Utilities/KeybindListTests.cs
index 0bd6ec17..f5c156c4 100644
--- a/src/SMAPI.Tests/Utilities/KeybindListTests.cs
+++ b/src/SMAPI.Tests/Utilities/KeybindListTests.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using NUnit.Framework;
diff --git a/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs b/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs
index ab4c2618..ae2cc6ce 100644
--- a/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs
+++ b/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.IO;
using NUnit.Framework;
using StardewModdingAPI.Toolkit.Utilities;
@@ -14,7 +16,7 @@ namespace SMAPI.Tests.Utilities
/// <summary>Sample paths used in unit tests.</summary>
public static readonly SamplePath[] SamplePaths = {
// Windows absolute path
- new SamplePath
+ new()
{
OriginalPath = @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley",
@@ -26,7 +28,7 @@ namespace SMAPI.Tests.Utilities
},
// Windows absolute path (with trailing slash)
- new SamplePath
+ new()
{
OriginalPath = @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\",
@@ -38,7 +40,7 @@ namespace SMAPI.Tests.Utilities
},
// Windows relative path
- new SamplePath
+ new()
{
OriginalPath = @"Content\Characters\Dialogue\Abigail",
@@ -50,7 +52,7 @@ namespace SMAPI.Tests.Utilities
},
// Windows relative path (with directory climbing)
- new SamplePath
+ new()
{
OriginalPath = @"..\..\Content",
@@ -62,7 +64,7 @@ namespace SMAPI.Tests.Utilities
},
// Windows UNC path
- new SamplePath
+ new()
{
OriginalPath = @"\\unc\path",
@@ -74,7 +76,7 @@ namespace SMAPI.Tests.Utilities
},
// Linux absolute path
- new SamplePath
+ new()
{
OriginalPath = @"/home/.steam/steam/steamapps/common/Stardew Valley",
@@ -86,7 +88,7 @@ namespace SMAPI.Tests.Utilities
},
// Linux absolute path (with trailing slash)
- new SamplePath
+ new()
{
OriginalPath = @"/home/.steam/steam/steamapps/common/Stardew Valley/",
@@ -98,7 +100,7 @@ namespace SMAPI.Tests.Utilities
},
// Linux absolute path (with ~)
- new SamplePath
+ new()
{
OriginalPath = @"~/.steam/steam/steamapps/common/Stardew Valley",
@@ -110,7 +112,7 @@ namespace SMAPI.Tests.Utilities
},
// Linux relative path
- new SamplePath
+ new()
{
OriginalPath = @"Content/Characters/Dialogue/Abigail",
@@ -122,7 +124,7 @@ namespace SMAPI.Tests.Utilities
},
// Linux relative path (with directory climbing)
- new SamplePath
+ new()
{
OriginalPath = @"../../Content",
@@ -134,7 +136,7 @@ namespace SMAPI.Tests.Utilities
},
// Mixed directory separators
- new SamplePath
+ new()
{
OriginalPath = @"C:\some/mixed\path/separators",
diff --git a/src/SMAPI.Tests/Utilities/SDateTests.cs b/src/SMAPI.Tests/Utilities/SDateTests.cs
index 374f4921..a4a36828 100644
--- a/src/SMAPI.Tests/Utilities/SDateTests.cs
+++ b/src/SMAPI.Tests/Utilities/SDateTests.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -61,7 +63,7 @@ namespace SMAPI.Tests.Utilities
public void Constructor_SetsExpectedValues([ValueSource(nameof(SDateTests.SampleSeasonValues))] string season, [ValueSource(nameof(SDateTests.ValidDays))] int day, [Values(1, 2, 100)] int year)
{
// act
- SDate date = new SDate(day, season, year);
+ SDate date = new(day, season, year);
// assert
Assert.AreEqual(day, date.Day);
@@ -254,7 +256,7 @@ namespace SMAPI.Tests.Utilities
{
foreach (int day in SDateTests.ValidDays)
{
- SDate date = new SDate(day, season, year);
+ SDate date = new(day, season, year);
int hash = date.GetHashCode();
if (hashes.TryGetValue(hash, out SDate otherDate))
Assert.Fail($"Received identical hash code {hash} for dates {otherDate} and {date}.");
diff --git a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs
index ac4ef39b..fedadba6 100644
--- a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs
+++ b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs
@@ -61,10 +61,10 @@ namespace SMAPI.Tests.Utilities
[TestCase("apple")]
[TestCase("-apple")]
[TestCase("-5")]
- public void Constructor_FromString_WithInvalidValues(string input)
+ public void Constructor_FromString_WithInvalidValues(string? input)
{
if (input == null)
- this.AssertAndLogException<ArgumentNullException>(() => new SemanticVersion(input));
+ this.AssertAndLogException<ArgumentNullException>(() => new SemanticVersion(input!));
else
this.AssertAndLogException<FormatException>(() => new SemanticVersion(input));
}
@@ -91,7 +91,7 @@ namespace SMAPI.Tests.Utilities
[TestCase("1.2.3.4-some-tag.4 ")]
public void Constructor_FromString_Standard_DisallowsNonStandardVersion(string input)
{
- Assert.Throws<FormatException>(() => new SemanticVersion(input));
+ Assert.Throws<FormatException>(() => _ = new SemanticVersion(input));
}
/// <summary>Assert the parsed version when constructed from standard parts.</summary>
@@ -110,7 +110,7 @@ namespace SMAPI.Tests.Utilities
[TestCase(1, 2, 3, "some-tag.4 ", null, ExpectedResult = "1.2.3-some-tag.4")]
[TestCase(1, 2, 3, "some-tag.4 ", "build.004", ExpectedResult = "1.2.3-some-tag.4+build.004")]
[TestCase(1, 2, 0, null, "3.4.5-build.004", ExpectedResult = "1.2.0+3.4.5-build.004")]
- public string Constructor_FromParts(int major, int minor, int patch, string prerelease, string build)
+ public string Constructor_FromParts(int major, int minor, int patch, string? prerelease, string? build)
{
// act
ISemanticVersion version = new SemanticVersion(major, minor, patch, prerelease, build);
@@ -217,11 +217,16 @@ namespace SMAPI.Tests.Utilities
[TestCase("1.0-beta.2", "1.0-beta.1", ExpectedResult = 1)]
[TestCase("1.0-beta.10", "1.0-beta.2", ExpectedResult = 1)]
[TestCase("1.0-beta-10", "1.0-beta-2", ExpectedResult = 1)]
- public int CompareTo(string versionStrA, string versionStrB)
+
+ // null
+ [TestCase("1.0.0", null, ExpectedResult = 1)] // null is always less than any value per CompareTo remarks
+ public int CompareTo(string versionStrA, string? versionStrB)
{
// arrange
ISemanticVersion versionA = new SemanticVersion(versionStrA);
- ISemanticVersion versionB = new SemanticVersion(versionStrB);
+ ISemanticVersion? versionB = versionStrB != null
+ ? new SemanticVersion(versionStrB)
+ : null;
// assert
return versionA.CompareTo(versionB);
@@ -260,14 +265,19 @@ namespace SMAPI.Tests.Utilities
[TestCase("1.0-beta.2", "1.0-beta.1", ExpectedResult = false)]
[TestCase("1.0-beta.10", "1.0-beta.2", ExpectedResult = false)]
[TestCase("1.0-beta-10", "1.0-beta-2", ExpectedResult = false)]
- public bool IsOlderThan(string versionStrA, string versionStrB)
+
+ // null
+ [TestCase("1.0.0", null, ExpectedResult = false)] // null is always less than any value per CompareTo remarks
+ public bool IsOlderThan(string versionStrA, string? versionStrB)
{
// arrange
ISemanticVersion versionA = new SemanticVersion(versionStrA);
- ISemanticVersion versionB = new SemanticVersion(versionStrB);
+ ISemanticVersion? versionB = versionStrB != null
+ ? new SemanticVersion(versionStrB)
+ : null;
// assert
- Assert.AreEqual(versionA.IsOlderThan(versionB), versionA.IsOlderThan(versionB.ToString()), "The two signatures returned different results.");
+ Assert.AreEqual(versionA.IsOlderThan(versionB), versionA.IsOlderThan(versionB?.ToString()), "The two signatures returned different results.");
return versionA.IsOlderThan(versionB);
}
@@ -304,14 +314,19 @@ namespace SMAPI.Tests.Utilities
[TestCase("1.0-beta.2", "1.0-beta.1", ExpectedResult = true)]
[TestCase("1.0-beta.10", "1.0-beta.2", ExpectedResult = true)]
[TestCase("1.0-beta-10", "1.0-beta-2", ExpectedResult = true)]
- public bool IsNewerThan(string versionStrA, string versionStrB)
+
+ // null
+ [TestCase("1.0.0", null, ExpectedResult = true)] // null is always less than any value per CompareTo remarks
+ public bool IsNewerThan(string versionStrA, string? versionStrB)
{
// arrange
ISemanticVersion versionA = new SemanticVersion(versionStrA);
- ISemanticVersion versionB = new SemanticVersion(versionStrB);
+ ISemanticVersion? versionB = versionStrB != null
+ ? new SemanticVersion(versionStrB)
+ : null;
// assert
- Assert.AreEqual(versionA.IsNewerThan(versionB), versionA.IsNewerThan(versionB.ToString()), "The two signatures returned different results.");
+ Assert.AreEqual(versionA.IsNewerThan(versionB), versionA.IsNewerThan(versionB?.ToString()), "The two signatures returned different results.");
return versionA.IsNewerThan(versionB);
}
@@ -322,7 +337,7 @@ namespace SMAPI.Tests.Utilities
/// <param name="versionStr">The main version.</param>
/// <param name="lowerStr">The lower version number.</param>
/// <param name="upperStr">The upper version number.</param>
- [Test(Description = "Assert that version.IsNewerThan returns the expected value.")]
+ [Test(Description = "Assert that version.IsBetween returns the expected value.")]
// is between
[TestCase("0.5.7-beta.3", "0.5.7-beta.3", "0.5.7-beta.3", ExpectedResult = true)]
[TestCase("1.0", "1.0", "1.1", ExpectedResult = true)]
@@ -330,6 +345,7 @@ namespace SMAPI.Tests.Utilities
[TestCase("1.0", "0.5", "1.1", ExpectedResult = true)]
[TestCase("1.0-beta.2", "1.0-beta.1", "1.0-beta.3", ExpectedResult = true)]
[TestCase("1.0-beta-2", "1.0-beta-1", "1.0-beta-3", ExpectedResult = true)]
+ [TestCase("1.0.0", null, "1.0.0", ExpectedResult = true)] // null is always less than any value per CompareTo remarks
// is not between
[TestCase("1.0-beta", "1.0", "1.1", ExpectedResult = false)]
@@ -337,15 +353,20 @@ namespace SMAPI.Tests.Utilities
[TestCase("1.0-beta.2", "1.1", "1.0", ExpectedResult = false)]
[TestCase("1.0-beta.2", "1.0-beta.10", "1.0-beta.3", ExpectedResult = false)]
[TestCase("1.0-beta-2", "1.0-beta-10", "1.0-beta-3", ExpectedResult = false)]
- public bool IsBetween(string versionStr, string lowerStr, string upperStr)
+ [TestCase("1.0.0", "1.0.0", null, ExpectedResult = false)] // null is always less than any value per CompareTo remarks
+ public bool IsBetween(string versionStr, string? lowerStr, string? upperStr)
{
// arrange
- ISemanticVersion lower = new SemanticVersion(lowerStr);
- ISemanticVersion upper = new SemanticVersion(upperStr);
+ ISemanticVersion? lower = lowerStr != null
+ ? new SemanticVersion(lowerStr)
+ : null;
+ ISemanticVersion? upper = upperStr != null
+ ? new SemanticVersion(upperStr)
+ : null;
ISemanticVersion version = new SemanticVersion(versionStr);
// assert
- Assert.AreEqual(version.IsBetween(lower, upper), version.IsBetween(lower.ToString(), upper.ToString()), "The two signatures returned different results.");
+ Assert.AreEqual(version.IsBetween(lower, upper), version.IsBetween(lower?.ToString(), upper?.ToString()), "The two signatures returned different results.");
return version.IsBetween(lower, upper);
}
@@ -395,7 +416,7 @@ namespace SMAPI.Tests.Utilities
public void GameVersion(string versionStr)
{
// act
- GameVersion version = new GameVersion(versionStr);
+ GameVersion version = new(versionStr);
// assert
Assert.AreEqual(versionStr, version.ToString(), "The game version did not round-trip to the same value.");
@@ -413,7 +434,7 @@ namespace SMAPI.Tests.Utilities
/// <param name="prerelease">The prerelease tag.</param>
/// <param name="build">The build metadata.</param>
/// <param name="nonStandard">Whether the version should be marked as non-standard.</param>
- private void AssertParts(ISemanticVersion version, int major, int minor, int patch, string prerelease, string build, bool nonStandard)
+ private void AssertParts(ISemanticVersion version, int major, int minor, int patch, string? prerelease, string? build, bool nonStandard)
{
Assert.AreEqual(major, version.MajorVersion, "The major version doesn't match.");
Assert.AreEqual(minor, version.MinorVersion, "The minor version doesn't match.");
@@ -426,9 +447,8 @@ namespace SMAPI.Tests.Utilities
/// <summary>Assert that the expected exception type is thrown, and log the action output and thrown exception.</summary>
/// <typeparam name="T">The expected exception type.</typeparam>
/// <param name="action">The action which may throw the exception.</param>
- /// <param name="message">The message to log if the expected exception isn't thrown.</param>
[SuppressMessage("ReSharper", "UnusedParameter.Local", Justification = "The message argument is deliberately only used in precondition checks since this is an assertion method.")]
- private void AssertAndLogException<T>(Func<object> action, string message = null)
+ private void AssertAndLogException<T>(Func<object> action)
where T : Exception
{
this.AssertAndLogException<T>(() =>
@@ -443,7 +463,7 @@ namespace SMAPI.Tests.Utilities
/// <param name="action">The action which may throw the exception.</param>
/// <param name="message">The message to log if the expected exception isn't thrown.</param>
[SuppressMessage("ReSharper", "UnusedParameter.Local", Justification = "The message argument is deliberately only used in precondition checks since this is an assertion method.")]
- private void AssertAndLogException<T>(Action action, string message = null)
+ private void AssertAndLogException<T>(Action action, string? message = null)
where T : Exception
{
try
@@ -455,7 +475,7 @@ namespace SMAPI.Tests.Utilities
TestContext.WriteLine($"Exception thrown:\n{ex}");
return;
}
- catch (Exception ex) when (!(ex is AssertionException))
+ catch (Exception ex) when (ex is not AssertionException)
{
TestContext.WriteLine($"Exception thrown:\n{ex}");
Assert.Fail(message ?? $"Didn't throw the expected exception; expected {typeof(T).FullName}, got {ex.GetType().FullName}.");
diff --git a/src/SMAPI.Tests/WikiClient/ChangeDescriptorTests.cs b/src/SMAPI.Tests/WikiClient/ChangeDescriptorTests.cs
index b896b09c..7695fbf8 100644
--- a/src/SMAPI.Tests/WikiClient/ChangeDescriptorTests.cs
+++ b/src/SMAPI.Tests/WikiClient/ChangeDescriptorTests.cs
@@ -1,5 +1,6 @@
+#nullable disable
+
using System.Collections.Generic;
-using System.Linq;
using NUnit.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
diff --git a/src/SMAPI.Toolkit.CoreInterfaces/IManifest.cs b/src/SMAPI.Toolkit.CoreInterfaces/IManifest.cs
index 7375f005..ee6cc0b6 100644
--- a/src/SMAPI.Toolkit.CoreInterfaces/IManifest.cs
+++ b/src/SMAPI.Toolkit.CoreInterfaces/IManifest.cs
@@ -21,16 +21,16 @@ namespace StardewModdingAPI
ISemanticVersion Version { get; }
/// <summary>The minimum SMAPI version required by this mod, if any.</summary>
- ISemanticVersion MinimumApiVersion { get; }
+ ISemanticVersion? MinimumApiVersion { get; }
/// <summary>The unique mod ID.</summary>
string UniqueID { get; }
/// <summary>The name of the DLL in the directory that has the <c>Entry</c> method. Mutually exclusive with <see cref="ContentPackFor"/>.</summary>
- string EntryDll { get; }
+ string? EntryDll { get; }
/// <summary>The mod which will read this as a content pack. Mutually exclusive with <see cref="EntryDll"/>.</summary>
- IManifestContentPackFor ContentPackFor { get; }
+ IManifestContentPackFor? ContentPackFor { get; }
/// <summary>The other mods that must be loaded before this mod.</summary>
IManifestDependency[] Dependencies { get; }
diff --git a/src/SMAPI.Toolkit.CoreInterfaces/IManifestContentPackFor.cs b/src/SMAPI.Toolkit.CoreInterfaces/IManifestContentPackFor.cs
index f05a3873..52ac8f1c 100644
--- a/src/SMAPI.Toolkit.CoreInterfaces/IManifestContentPackFor.cs
+++ b/src/SMAPI.Toolkit.CoreInterfaces/IManifestContentPackFor.cs
@@ -7,6 +7,6 @@ namespace StardewModdingAPI
string UniqueID { get; }
/// <summary>The minimum required version (if any).</summary>
- ISemanticVersion MinimumVersion { get; }
+ ISemanticVersion? MinimumVersion { get; }
}
}
diff --git a/src/SMAPI.Toolkit.CoreInterfaces/IManifestDependency.cs b/src/SMAPI.Toolkit.CoreInterfaces/IManifestDependency.cs
index e86cd1f4..58425eb2 100644
--- a/src/SMAPI.Toolkit.CoreInterfaces/IManifestDependency.cs
+++ b/src/SMAPI.Toolkit.CoreInterfaces/IManifestDependency.cs
@@ -10,7 +10,7 @@ namespace StardewModdingAPI
string UniqueID { get; }
/// <summary>The minimum required version (if any).</summary>
- ISemanticVersion MinimumVersion { get; }
+ ISemanticVersion? MinimumVersion { get; }
/// <summary>Whether the dependency must be installed to use the mod.</summary>
bool IsRequired { get; }
diff --git a/src/SMAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs b/src/SMAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs
index b228b2d1..7998272f 100644
--- a/src/SMAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs
+++ b/src/SMAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs
@@ -18,10 +18,10 @@ namespace StardewModdingAPI
int PatchVersion { get; }
/// <summary>An optional prerelease tag.</summary>
- string PrereleaseTag { get; }
+ string? PrereleaseTag { get; }
/// <summary>Optional build metadata. This is ignored when determining version precedence.</summary>
- string BuildMetadata { get; }
+ string? BuildMetadata { get; }
/*********
@@ -32,32 +32,38 @@ namespace StardewModdingAPI
/// <summary>Get whether this version is older than the specified version.</summary>
/// <param name="other">The version to compare with this instance.</param>
- bool IsOlderThan(ISemanticVersion other);
+ /// <remarks>Although the <paramref name="other"/> parameter is nullable, it isn't optional. A <c>null</c> version is considered earlier than every possible valid version, so passing <c>null</c> to <paramref name="other"/> will always return false.</remarks>
+ bool IsOlderThan(ISemanticVersion? other);
/// <summary>Get whether this version is older than the specified version.</summary>
- /// <param name="other">The version to compare with this instance.</param>
+ /// <param name="other">The version to compare with this instance. A null value is never older.</param>
/// <exception cref="FormatException">The specified version is not a valid semantic version.</exception>
- bool IsOlderThan(string other);
+ /// <remarks>Although the <paramref name="other"/> parameter is nullable, it isn't optional. A <c>null</c> version is considered earlier than every possible valid version, so passing <c>null</c> to <paramref name="other"/> will always return false.</remarks>
+ bool IsOlderThan(string? other);
/// <summary>Get whether this version is newer than the specified version.</summary>
- /// <param name="other">The version to compare with this instance.</param>
- bool IsNewerThan(ISemanticVersion other);
+ /// <param name="other">The version to compare with this instance. A null value is always older.</param>
+ /// <remarks>Although the <paramref name="other"/> parameter is nullable, it isn't optional. A <c>null</c> version is considered earlier than every possible valid version, so passing <c>null</c> to <paramref name="other"/> will always return true.</remarks>
+ bool IsNewerThan(ISemanticVersion? other);
/// <summary>Get whether this version is newer than the specified version.</summary>
- /// <param name="other">The version to compare with this instance.</param>
+ /// <param name="other">The version to compare with this instance. A null value is always older.</param>
/// <exception cref="FormatException">The specified version is not a valid semantic version.</exception>
- bool IsNewerThan(string other);
+ /// <remarks>Although the <paramref name="other"/> parameter is nullable, it isn't optional. A <c>null</c> version is considered earlier than every possible valid version, so passing <c>null</c> to <paramref name="other"/> will always return true.</remarks>
+ bool IsNewerThan(string? other);
/// <summary>Get whether this version is between two specified versions (inclusively).</summary>
- /// <param name="min">The minimum version.</param>
- /// <param name="max">The maximum version.</param>
- bool IsBetween(ISemanticVersion min, ISemanticVersion max);
+ /// <param name="min">The minimum version. A null value is always older.</param>
+ /// <param name="max">The maximum version. A null value is never newer.</param>
+ /// <remarks>Although the <paramref name="min"/> and <paramref name="max"/> parameters are nullable, they are not optional. A <c>null</c> version is considered earlier than every possible valid version. For example, passing <c>null</c> to <paramref name="max"/> will always return false, since no valid version can be earlier than <c>null</c>.</remarks>
+ bool IsBetween(ISemanticVersion? min, ISemanticVersion? max);
/// <summary>Get whether this version is between two specified versions (inclusively).</summary>
- /// <param name="min">The minimum version.</param>
- /// <param name="max">The maximum version.</param>
+ /// <param name="min">The minimum version. A null value is always older.</param>
+ /// <param name="max">The maximum version. A null value is never newer.</param>
/// <exception cref="FormatException">One of the specified versions is not a valid semantic version.</exception>
- bool IsBetween(string min, string max);
+ /// <remarks>Although the <paramref name="min"/> and <paramref name="max"/> parameters are nullable, they are not optional. A <c>null</c> version is considered earlier than every possible valid version. For example, passing <c>null</c> to <paramref name="max"/> will always return false, since no valid version can be earlier than <c>null</c>.</remarks>
+ bool IsBetween(string? min, string? max);
/// <summary>Get a string representation of the version.</summary>
string ToString();
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
index 0115fbf3..d5ca2034 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs
index 188db31d..9aac7fd3 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Newtonsoft.Json;
using StardewModdingAPI.Toolkit.Serialization.Converters;
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
index 0fa4a74d..eb54ec78 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs
index 404d4618..8fe8fa2a 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs
index 73698173..393391f7 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Linq;
using StardewModdingAPI.Toolkit.Utilities;
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
index c2d906a0..56acb768 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -62,9 +64,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
private TResult Post<TBody, TResult>(string url, TBody content)
{
// note: avoid HttpClient for macOS compatibility
- using WebClient client = new WebClient();
+ using WebClient client = new();
- Uri fullUrl = new Uri(this.BaseUrl, url);
+ Uri fullUrl = new(this.BaseUrl, url);
string data = JsonConvert.SerializeObject(content);
client.Headers["Content-Type"] = "application/json";
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/ChangeDescriptor.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/ChangeDescriptor.cs
index 2ed255c8..910bf793 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/ChangeDescriptor.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/ChangeDescriptor.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
index 0f5a0ec3..86c3bd75 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -65,7 +67,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
foreach (var entry in this.ParseOverrideEntries(modNodes))
{
- if (entry.Ids?.Any() != true || !entry.HasChanges)
+ if (entry.Ids.Any() != true || !entry.HasChanges)
continue;
foreach (string id in entry.Ids)
@@ -127,7 +129,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
string pullRequestUrl = this.GetAttribute(node, "data-pr");
// parse stable compatibility
- WikiCompatibilityInfo compatibility = new WikiCompatibilityInfo
+ WikiCompatibilityInfo compatibility = new()
{
Status = this.GetAttributeAsEnum<WikiCompatibilityStatus>(node, "data-status") ?? WikiCompatibilityStatus.Ok,
BrokeIn = this.GetAttribute(node, "data-broke-in"),
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs
index 204acd2b..30e76d04 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
/// <summary>Compatibility info for a mod.</summary>
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs
index 5cdf489f..2c222b71 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
/// <summary>The compatibility status for a mod.</summary>
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiDataOverrideEntry.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiDataOverrideEntry.cs
index 03c0d214..a6f5a88f 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiDataOverrideEntry.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiDataOverrideEntry.cs
@@ -1,7 +1,5 @@
using System;
-#nullable enable
-
namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
/// <summary>The data overrides to apply to matching mods.</summary>
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs
index 4e0104da..91943ff9 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
/// <summary>A mod entry in the wiki list.</summary>
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs
index 0d614f28..1787197a 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModList.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
/// <summary>Metadata from the wiki's mod compatibility list.</summary>
diff --git a/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs b/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs
index 8d4198de..4f872f1c 100644
--- a/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs
+++ b/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs
@@ -39,13 +39,13 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
IEnumerable<string> paths = this
.GetCustomInstallPaths()
.Concat(this.GetDefaultInstallPaths())
- .Select(PathUtilities.NormalizePath)
+ .Select(path => PathUtilities.NormalizePath(path))
.Distinct(StringComparer.OrdinalIgnoreCase);
// yield valid folders
foreach (string path in paths)
{
- DirectoryInfo folder = new DirectoryInfo(path);
+ DirectoryInfo folder = new(path);
if (this.LooksLikeGameFolder(folder))
yield return folder;
}
@@ -78,10 +78,12 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
return GameFolderType.NoGameFound;
// get assembly version
- Version version;
+ Version? version;
try
{
version = AssemblyName.GetAssemblyName(executable.FullName).Version;
+ if (version == null)
+ return GameFolderType.InvalidUnknown;
}
catch
{
@@ -121,7 +123,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
case Platform.Linux:
case Platform.Mac:
{
- string home = Environment.GetEnvironmentVariable("HOME");
+ string home = Environment.GetEnvironmentVariable("HOME")!;
// Linux
yield return $"{home}/GOG Games/Stardew Valley/game";
@@ -146,13 +148,13 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
};
foreach (var pair in registryKeys)
{
- string path = this.GetLocalMachineRegistryValue(pair.Key, pair.Value);
+ string? path = this.GetLocalMachineRegistryValue(pair.Key, pair.Value);
if (!string.IsNullOrWhiteSpace(path))
yield return path;
}
// via Steam library path
- string steamPath = this.GetCurrentUserRegistryValue(@"Software\Valve\Steam", "SteamPath");
+ string? steamPath = this.GetCurrentUserRegistryValue(@"Software\Valve\Steam", "SteamPath");
if (steamPath != null)
yield return Path.Combine(steamPath.Replace('/', '\\'), @"steamapps\common\Stardew Valley");
#endif
@@ -186,12 +188,12 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
private IEnumerable<string> GetCustomInstallPaths()
{
// get home path
- string homePath = Environment.GetEnvironmentVariable(this.Platform == Platform.Windows ? "USERPROFILE" : "HOME");
+ string homePath = Environment.GetEnvironmentVariable(this.Platform == Platform.Windows ? "USERPROFILE" : "HOME")!;
if (string.IsNullOrWhiteSpace(homePath))
yield break;
// get targets file
- FileInfo file = new FileInfo(Path.Combine(homePath, "stardewvalley.targets"));
+ FileInfo file = new(Path.Combine(homePath, "stardewvalley.targets"));
if (!file.Exists)
yield break;
@@ -208,7 +210,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
}
// get install path
- XElement element = root.XPathSelectElement("//*[local-name() = 'GamePath']"); // can't use '//GamePath' due to the default namespace
+ XElement? element = root.XPathSelectElement("//*[local-name() = 'GamePath']"); // can't use '//GamePath' due to the default namespace
if (!string.IsNullOrWhiteSpace(element?.Value))
yield return element.Value.Trim();
}
@@ -217,27 +219,27 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
/// <summary>Get the value of a key in the Windows HKLM registry.</summary>
/// <param name="key">The full path of the registry key relative to HKLM.</param>
/// <param name="name">The name of the value.</param>
- private string GetLocalMachineRegistryValue(string key, string name)
+ private string? GetLocalMachineRegistryValue(string key, string name)
{
RegistryKey localMachine = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) : Registry.LocalMachine;
- RegistryKey openKey = localMachine.OpenSubKey(key);
+ RegistryKey? openKey = localMachine.OpenSubKey(key);
if (openKey == null)
return null;
using (openKey)
- return (string)openKey.GetValue(name);
+ return (string?)openKey.GetValue(name);
}
/// <summary>Get the value of a key in the Windows HKCU registry.</summary>
/// <param name="key">The full path of the registry key relative to HKCU.</param>
/// <param name="name">The name of the value.</param>
- private string GetCurrentUserRegistryValue(string key, string name)
+ private string? GetCurrentUserRegistryValue(string key, string name)
{
- RegistryKey currentuser = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64) : Registry.CurrentUser;
- RegistryKey openKey = currentuser.OpenSubKey(key);
+ RegistryKey currentUser = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64) : Registry.CurrentUser;
+ RegistryKey? openKey = currentUser.OpenSubKey(key);
if (openKey == null)
return null;
using (openKey)
- return (string)openKey.GetValue(name);
+ return (string?)openKey.GetValue(name);
}
#endif
}
diff --git a/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs b/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs
index 8b6eb5fb..6978567e 100644
--- a/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs
+++ b/src/SMAPI.Toolkit/Framework/LowLevelEnvironmentUtility.cs
@@ -57,11 +57,13 @@ namespace StardewModdingAPI.Toolkit.Framework
#if SMAPI_FOR_WINDOWS
try
{
- return new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem")
+ string? result = new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem")
.Get()
.Cast<ManagementObject>()
.Select(entry => entry.GetPropertyValue("Caption").ToString())
.FirstOrDefault();
+
+ return result ?? "Windows";
}
catch { }
#endif
@@ -98,7 +100,7 @@ namespace StardewModdingAPI.Toolkit.Framework
/// </remarks>
private static bool IsRunningAndroid()
{
- using Process process = new Process
+ using Process process = new()
{
StartInfo =
{
@@ -135,7 +137,7 @@ namespace StardewModdingAPI.Toolkit.Framework
buffer = Marshal.AllocHGlobal(8192);
if (LowLevelEnvironmentUtility.uname(buffer) == 0)
{
- string os = Marshal.PtrToStringAnsi(buffer);
+ string? os = Marshal.PtrToStringAnsi(buffer);
return os == "Darwin";
}
return false;
diff --git a/src/SMAPI.Toolkit/Framework/ModData/MetadataModel.cs b/src/SMAPI.Toolkit/Framework/ModData/MetadataModel.cs
index ef6d4dd9..3fa70615 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/MetadataModel.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/MetadataModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
namespace StardewModdingAPI.Toolkit.Framework.ModData
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataField.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataField.cs
index b02be3e4..46cb81e1 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataField.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataField.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Linq;
namespace StardewModdingAPI.Toolkit.Framework.ModData
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataModel.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataModel.cs
index 2167d3e5..4d96a555 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataModel.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs
index 5dd32acf..4c09e1ba 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -82,7 +84,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// <param name="manifest">The manifest to match.</param>
public ModDataRecordVersionedFields GetVersionedFields(IManifest manifest)
{
- ModDataRecordVersionedFields parsed = new ModDataRecordVersionedFields { DisplayName = this.DisplayName, DataRecord = this };
+ ModDataRecordVersionedFields parsed = new() { DisplayName = this.DisplayName, DataRecord = this };
foreach (ModDataField field in this.Fields.Where(field => field.IsMatch(manifest)))
{
switch (field.Key)
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs
index 5aaabd51..b599b343 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Toolkit.Framework.ModData
{
/// <summary>The versioned fields from a <see cref="ModDataRecord"/> for a specific manifest.</summary>
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDatabase.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDatabase.cs
index 5b7e2a02..a5237334 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDatabase.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDatabase.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -22,7 +24,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
*********/
/// <summary>Construct an empty instance.</summary>
public ModDatabase()
- : this(Array.Empty<ModDataRecord>(), key => null) { }
+ : this(Array.Empty<ModDataRecord>(), _ => null) { }
/// <summary>Construct an instance.</summary>
/// <param name="records">The underlying mod data records indexed by default display name.</param>
diff --git a/src/SMAPI.Toolkit/Framework/ModScanning/ModFolder.cs b/src/SMAPI.Toolkit/Framework/ModScanning/ModFolder.cs
index 825b98e5..da2a3c85 100644
--- a/src/SMAPI.Toolkit/Framework/ModScanning/ModFolder.cs
+++ b/src/SMAPI.Toolkit/Framework/ModScanning/ModFolder.cs
@@ -22,13 +22,13 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
public ModType Type { get; }
/// <summary>The mod manifest.</summary>
- public Manifest Manifest { get; }
+ public Manifest? Manifest { get; }
/// <summary>The error which occurred parsing the manifest, if any.</summary>
public ModParseError ManifestParseError { get; set; }
/// <summary>A human-readable message for the <see cref="ManifestParseError"/>, if any.</summary>
- public string ManifestParseErrorText { get; set; }
+ public string? ManifestParseErrorText { get; set; }
/*********
@@ -49,7 +49,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
/// <param name="manifest">The mod manifest.</param>
/// <param name="manifestParseError">The error which occurred parsing the manifest, if any.</param>
/// <param name="manifestParseErrorText">A human-readable message for the <paramref name="manifestParseError"/>, if any.</param>
- public ModFolder(DirectoryInfo root, DirectoryInfo directory, ModType type, Manifest manifest, ModParseError manifestParseError, string manifestParseErrorText)
+ public ModFolder(DirectoryInfo root, DirectoryInfo directory, ModType type, Manifest? manifest, ModParseError manifestParseError, string? manifestParseErrorText)
{
// save info
this.Directory = directory;
@@ -59,9 +59,9 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
this.ManifestParseErrorText = manifestParseErrorText;
// set display name
- this.DisplayName = manifest?.Name;
- if (string.IsNullOrWhiteSpace(this.DisplayName))
- this.DisplayName = PathUtilities.GetRelativePath(root.FullName, directory.FullName);
+ this.DisplayName = !string.IsNullOrWhiteSpace(manifest?.Name)
+ ? manifest.Name
+ : PathUtilities.GetRelativePath(root.FullName, directory.FullName);
}
/// <summary>Get the update keys for a mod.</summary>
diff --git a/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs
index e6105f9c..2af30092 100644
--- a/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs
+++ b/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs
@@ -18,7 +18,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
private readonly JsonHelper JsonHelper;
/// <summary>A list of filesystem entry names to ignore when checking whether a folder should be treated as a mod.</summary>
- private readonly HashSet<Regex> IgnoreFilesystemNames = new HashSet<Regex>
+ private readonly HashSet<Regex> IgnoreFilesystemNames = new()
{
new Regex(@"^__folder_managed_by_vortex$", RegexOptions.Compiled | RegexOptions.IgnoreCase), // Vortex mod manager
new Regex(@"(?:^\._|^\.DS_Store$|^__MACOSX$|^mcs$)", RegexOptions.Compiled | RegexOptions.IgnoreCase), // macOS
@@ -26,7 +26,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
};
/// <summary>A list of file extensions to ignore when searching for mod files.</summary>
- private readonly HashSet<string> IgnoreFileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ private readonly HashSet<string> IgnoreFileExtensions = new(StringComparer.OrdinalIgnoreCase)
{
// text
".doc",
@@ -60,7 +60,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
};
/// <summary>The extensions for packed content files.</summary>
- private readonly HashSet<string> StrictXnbModExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ private readonly HashSet<string> StrictXnbModExtensions = new(StringComparer.OrdinalIgnoreCase)
{
".xgs",
".xnb",
@@ -69,7 +69,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
};
/// <summary>The extensions for files which an XNB mod may contain, in addition to <see cref="StrictXnbModExtensions"/>.</summary>
- private readonly HashSet<string> PotentialXnbModExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ private readonly HashSet<string> PotentialXnbModExtensions = new(StringComparer.OrdinalIgnoreCase)
{
".json",
".yaml"
@@ -96,7 +96,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
/// <param name="rootPath">The root folder containing mods.</param>
public IEnumerable<ModFolder> GetModFolders(string rootPath)
{
- DirectoryInfo root = new DirectoryInfo(rootPath);
+ DirectoryInfo root = new(rootPath);
return this.GetModFolders(root, root);
}
@@ -115,7 +115,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
public ModFolder ReadFolder(DirectoryInfo root, DirectoryInfo searchFolder)
{
// find manifest.json
- FileInfo manifestFile = this.FindManifest(searchFolder);
+ FileInfo? manifestFile = this.FindManifest(searchFolder);
// set appropriate invalid-mod error
if (manifestFile == null)
@@ -137,7 +137,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
return new ModFolder(root, searchFolder, ModType.Xnb, null, ModParseError.XnbMod, "it's not a SMAPI mod (see https://smapi.io/xnb for info).");
// SMAPI installer
- if (relevantFiles.Any(p => p.Name == "install on Linux.sh" || p.Name == "install on macOS.command" || p.Name == "install on Windows.bat"))
+ if (relevantFiles.Any(p => p.Name is "install on Linux.sh" or "install on macOS.command" or "install on Windows.bat"))
return new ModFolder(root, searchFolder, ModType.Invalid, null, ModParseError.ManifestMissing, "the SMAPI installer isn't a mod (you can delete this folder after running the installer file).");
// not a mod?
@@ -145,13 +145,13 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
}
// read mod info
- Manifest manifest = null;
+ Manifest? manifest = null;
ModParseError error = ModParseError.None;
- string errorText = null;
+ string? errorText = null;
{
try
{
- if (!this.JsonHelper.ReadJsonFileIfExists<Manifest>(manifestFile.FullName, out manifest) || manifest == null)
+ if (!this.JsonHelper.ReadJsonFileIfExists<Manifest>(manifestFile.FullName, out manifest))
{
error = ModParseError.ManifestInvalid;
errorText = "its manifest is invalid.";
@@ -169,14 +169,6 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
}
}
- // normalize display fields
- if (manifest != null)
- {
- manifest.Name = this.StripNewlines(manifest.Name);
- manifest.Description = this.StripNewlines(manifest.Description);
- manifest.Author = this.StripNewlines(manifest.Author);
- }
-
// get mod type
ModType type;
{
@@ -192,7 +184,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
}
// build result
- return new ModFolder(root, manifestFile.Directory, type, manifest, error, errorText);
+ return new ModFolder(root, manifestFile.Directory!, type, manifest, error, errorText);
}
@@ -255,12 +247,12 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
/// <summary>Find the manifest for a mod folder.</summary>
/// <param name="folder">The folder to search.</param>
- private FileInfo FindManifest(DirectoryInfo folder)
+ private FileInfo? FindManifest(DirectoryInfo folder)
{
while (true)
{
// check for manifest in current folder
- FileInfo file = new FileInfo(Path.Combine(folder.FullName, "manifest.json"));
+ FileInfo file = new(Path.Combine(folder.FullName, "manifest.json"));
if (file.Exists)
return file;
@@ -363,12 +355,5 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
return hasVortexMarker;
}
-
- /// <summary>Strip newlines from a string.</summary>
- /// <param name="input">The input to strip.</param>
- private string StripNewlines(string input)
- {
- return input?.Replace("\r", "").Replace("\n", "");
- }
}
}
diff --git a/src/SMAPI.Toolkit/Framework/SemanticVersionReader.cs b/src/SMAPI.Toolkit/Framework/SemanticVersionReader.cs
index 489e1c4d..939be771 100644
--- a/src/SMAPI.Toolkit/Framework/SemanticVersionReader.cs
+++ b/src/SMAPI.Toolkit/Framework/SemanticVersionReader.cs
@@ -1,3 +1,5 @@
+using System.Diagnostics.CodeAnalysis;
+
namespace StardewModdingAPI.Toolkit.Framework
{
/// <summary>Reads strings into a semantic version.</summary>
@@ -16,7 +18,7 @@ namespace StardewModdingAPI.Toolkit.Framework
/// <param name="prereleaseTag">An optional prerelease tag.</param>
/// <param name="buildMetadata">Optional build metadata. This is ignored when determining version precedence.</param>
/// <returns>Returns whether the version was successfully parsed.</returns>
- public static bool TryParse(string versionStr, bool allowNonStandard, out int major, out int minor, out int patch, out int platformRelease, out string prereleaseTag, out string buildMetadata)
+ public static bool TryParse(string? versionStr, bool allowNonStandard, out int major, out int minor, out int patch, out int platformRelease, out string? prereleaseTag, out string? buildMetadata)
{
// init
major = 0;
@@ -103,7 +105,12 @@ namespace StardewModdingAPI.Toolkit.Framework
/// <param name="raw">The raw characters to parse.</param>
/// <param name="index">The index of the next character to read.</param>
/// <param name="tag">The parsed tag.</param>
- private static bool TryParseTag(char[] raw, ref int index, out string tag)
+ private static bool TryParseTag(char[] raw, ref int index,
+#if NET5_0_OR_GREATER
+ [NotNullWhen(true)]
+#endif
+ out string? tag
+ )
{
// read tag length
int length = 0;
diff --git a/src/SMAPI.Toolkit/Framework/UpdateData/UpdateKey.cs b/src/SMAPI.Toolkit/Framework/UpdateData/UpdateKey.cs
index 077c0361..4c9ca2ff 100644
--- a/src/SMAPI.Toolkit/Framework/UpdateData/UpdateKey.cs
+++ b/src/SMAPI.Toolkit/Framework/UpdateData/UpdateKey.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics.CodeAnalysis;
namespace StardewModdingAPI.Toolkit.Framework.UpdateData
{
@@ -15,10 +16,13 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
public ModSiteKey Site { get; }
/// <summary>The mod ID within the repository.</summary>
- public string ID { get; }
+#if NET5_0_OR_GREATER
+ [MemberNotNullWhen(true, nameof(LooksValid))]
+#endif
+ public string? ID { get; }
/// <summary>If specified, a substring in download names/descriptions to match.</summary>
- public string Subkey { get; }
+ public string? Subkey { get; }
/// <summary>Whether the update key seems to be valid.</summary>
public bool LooksValid { get; }
@@ -32,9 +36,9 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
/// <param name="site">The mod site containing the mod.</param>
/// <param name="id">The mod ID within the site.</param>
/// <param name="subkey">If specified, a substring in download names/descriptions to match.</param>
- public UpdateKey(string rawText, ModSiteKey site, string id, string subkey)
+ public UpdateKey(string? rawText, ModSiteKey site, string? id, string? subkey)
{
- this.RawText = rawText?.Trim();
+ this.RawText = rawText?.Trim() ?? string.Empty;
this.Site = site;
this.ID = id?.Trim();
this.Subkey = subkey?.Trim();
@@ -47,19 +51,19 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
/// <param name="site">The mod site containing the mod.</param>
/// <param name="id">The mod ID within the site.</param>
/// <param name="subkey">If specified, a substring in download names/descriptions to match.</param>
- public UpdateKey(ModSiteKey site, string id, string subkey)
+ public UpdateKey(ModSiteKey site, string? id, string? subkey)
: this(UpdateKey.GetString(site, id, subkey), site, id, subkey) { }
/// <summary>Parse a raw update key.</summary>
/// <param name="raw">The raw update key to parse.</param>
- public static UpdateKey Parse(string raw)
+ public static UpdateKey Parse(string? raw)
{
// extract site + ID
- string rawSite;
- string id;
+ string? rawSite;
+ string? id;
{
- string[] parts = raw?.Trim().Split(':');
- if (parts == null || parts.Length != 2)
+ string[]? parts = raw?.Trim().Split(':');
+ if (parts?.Length != 2)
return new UpdateKey(raw, ModSiteKey.Unknown, null, null);
rawSite = parts[0].Trim();
@@ -69,7 +73,7 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
id = null;
// extract subkey
- string subkey = null;
+ string? subkey = null;
if (id != null)
{
string[] parts = id.Split('@');
@@ -109,7 +113,7 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
/// <param name="other">An object to compare with this object.</param>
- public bool Equals(UpdateKey other)
+ public bool Equals(UpdateKey? other)
{
if (!this.LooksValid)
{
@@ -127,7 +131,7 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
/// <summary>Determines whether the specified object is equal to the current object.</summary>
/// <param name="obj">The object to compare with the current object.</param>
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is UpdateKey other && this.Equals(other);
}
@@ -143,7 +147,7 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
/// <param name="site">The mod site containing the mod.</param>
/// <param name="id">The mod ID within the repository.</param>
/// <param name="subkey">If specified, a substring in download names/descriptions to match.</param>
- public static string GetString(ModSiteKey site, string id, string subkey = null)
+ public static string GetString(ModSiteKey site, string? id, string? subkey = null)
{
return $"{site}:{id}{subkey}".Trim();
}
diff --git a/src/SMAPI.Toolkit/ModToolkit.cs b/src/SMAPI.Toolkit/ModToolkit.cs
index 38a67ae5..51f6fa24 100644
--- a/src/SMAPI.Toolkit/ModToolkit.cs
+++ b/src/SMAPI.Toolkit/ModToolkit.cs
@@ -22,7 +22,7 @@ namespace StardewModdingAPI.Toolkit
private readonly string UserAgent;
/// <summary>Maps vendor keys (like <c>Nexus</c>) to their mod URL template (where <c>{0}</c> is the mod ID). This doesn't affect update checks, which defer to the remote web API.</summary>
- private readonly IDictionary<ModSiteKey, string> VendorModUrls = new Dictionary<ModSiteKey, string>()
+ private readonly Dictionary<ModSiteKey, string> VendorModUrls = new()
{
[ModSiteKey.Chucklefish] = "https://community.playstarbound.com/resources/{0}",
[ModSiteKey.GitHub] = "https://github.com/{0}/releases",
@@ -34,7 +34,7 @@ namespace StardewModdingAPI.Toolkit
** Accessors
*********/
/// <summary>Encapsulates SMAPI's JSON parsing.</summary>
- public JsonHelper JsonHelper { get; } = new JsonHelper();
+ public JsonHelper JsonHelper { get; } = new();
/*********
@@ -43,7 +43,7 @@ namespace StardewModdingAPI.Toolkit
/// <summary>Construct an instance.</summary>
public ModToolkit()
{
- ISemanticVersion version = new SemanticVersion(this.GetType().Assembly.GetName().Version);
+ ISemanticVersion version = new SemanticVersion(this.GetType().Assembly.GetName().Version!);
this.UserAgent = $"SMAPI Mod Handler Toolkit/{version}";
}
@@ -57,7 +57,7 @@ namespace StardewModdingAPI.Toolkit
/// <summary>Extract mod metadata from the wiki compatibility list.</summary>
public async Task<WikiModList> GetWikiCompatibilityListAsync()
{
- var client = new WikiClient(this.UserAgent);
+ WikiClient client = new(this.UserAgent);
return await client.FetchModsAsync();
}
@@ -87,13 +87,13 @@ namespace StardewModdingAPI.Toolkit
/// <summary>Get an update URL for an update key (if valid).</summary>
/// <param name="updateKey">The update key.</param>
- public string GetUpdateUrl(string updateKey)
+ public string? GetUpdateUrl(string updateKey)
{
UpdateKey parsed = UpdateKey.Parse(updateKey);
if (!parsed.LooksValid)
return null;
- if (this.VendorModUrls.TryGetValue(parsed.Site, out string urlTemplate))
+ if (this.VendorModUrls.TryGetValue(parsed.Site, out string? urlTemplate))
return string.Format(urlTemplate, parsed.ID);
return null;
diff --git a/src/SMAPI.Toolkit/SemanticVersion.cs b/src/SMAPI.Toolkit/SemanticVersion.cs
index 2f3e282b..2cb27e11 100644
--- a/src/SMAPI.Toolkit/SemanticVersion.cs
+++ b/src/SMAPI.Toolkit/SemanticVersion.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using StardewModdingAPI.Toolkit.Framework;
@@ -38,10 +39,10 @@ namespace StardewModdingAPI.Toolkit
public int PlatformRelease { get; }
/// <inheritdoc />
- public string PrereleaseTag { get; }
+ public string? PrereleaseTag { get; }
/// <inheritdoc />
- public string BuildMetadata { get; }
+ public string? BuildMetadata { get; }
/*********
@@ -54,7 +55,7 @@ namespace StardewModdingAPI.Toolkit
/// <param name="platformRelease">The platform-specific version (if applicable).</param>
/// <param name="prereleaseTag">An optional prerelease tag.</param>
/// <param name="buildMetadata">Optional build metadata. This is ignored when determining version precedence.</param>
- public SemanticVersion(int major, int minor, int patch, int platformRelease = 0, string prereleaseTag = null, string buildMetadata = null)
+ public SemanticVersion(int major, int minor, int patch, int platformRelease = 0, string? prereleaseTag = null, string? buildMetadata = null)
{
this.MajorVersion = major;
this.MinorVersion = minor;
@@ -90,7 +91,7 @@ namespace StardewModdingAPI.Toolkit
{
if (version == null)
throw new ArgumentNullException(nameof(version), "The input version string can't be null.");
- if (!SemanticVersionReader.TryParse(version, allowNonStandard, out int major, out int minor, out int patch, out int platformRelease, out string prereleaseTag, out string buildMetadata) || (!allowNonStandard && platformRelease != 0))
+ if (!SemanticVersionReader.TryParse(version, allowNonStandard, out int major, out int minor, out int patch, out int platformRelease, out string? prereleaseTag, out string? buildMetadata) || (!allowNonStandard && platformRelease != 0))
throw new FormatException($"The input '{version}' isn't a valid semantic version.");
this.MajorVersion = major;
@@ -104,15 +105,15 @@ namespace StardewModdingAPI.Toolkit
}
/// <inheritdoc />
- public int CompareTo(ISemanticVersion other)
+ public int CompareTo(ISemanticVersion? other)
{
- if (other == null)
- throw new ArgumentNullException(nameof(other));
- return this.CompareTo(other.MajorVersion, other.MinorVersion, other.PatchVersion, (other as SemanticVersion)?.PlatformRelease ?? 0, other.PrereleaseTag);
+ return other == null
+ ? 1
+ : this.CompareTo(other.MajorVersion, other.MinorVersion, other.PatchVersion, (other as SemanticVersion)?.PlatformRelease ?? 0, other.PrereleaseTag);
}
/// <inheritdoc />
- public bool Equals(ISemanticVersion other)
+ public bool Equals(ISemanticVersion? other)
{
return other != null && this.CompareTo(other) == 0;
}
@@ -124,39 +125,54 @@ namespace StardewModdingAPI.Toolkit
}
/// <inheritdoc />
- public bool IsOlderThan(ISemanticVersion other)
+ public bool IsOlderThan(ISemanticVersion? other)
{
return this.CompareTo(other) < 0;
}
/// <inheritdoc />
- public bool IsOlderThan(string other)
+ public bool IsOlderThan(string? other)
{
- return this.IsOlderThan(new SemanticVersion(other, allowNonStandard: true));
+ ISemanticVersion? otherVersion = other != null
+ ? new SemanticVersion(other, allowNonStandard: true)
+ : null;
+
+ return this.IsOlderThan(otherVersion);
}
/// <inheritdoc />
- public bool IsNewerThan(ISemanticVersion other)
+ public bool IsNewerThan(ISemanticVersion? other)
{
return this.CompareTo(other) > 0;
}
/// <inheritdoc />
- public bool IsNewerThan(string other)
+ public bool IsNewerThan(string? other)
{
- return this.IsNewerThan(new SemanticVersion(other, allowNonStandard: true));
+ ISemanticVersion? otherVersion = other != null
+ ? new SemanticVersion(other, allowNonStandard: true)
+ : null;
+
+ return this.IsNewerThan(otherVersion);
}
/// <inheritdoc />
- public bool IsBetween(ISemanticVersion min, ISemanticVersion max)
+ public bool IsBetween(ISemanticVersion? min, ISemanticVersion? max)
{
return this.CompareTo(min) >= 0 && this.CompareTo(max) <= 0;
}
/// <inheritdoc />
- public bool IsBetween(string min, string max)
+ public bool IsBetween(string? min, string? max)
{
- return this.IsBetween(new SemanticVersion(min, allowNonStandard: true), new SemanticVersion(max, allowNonStandard: true));
+ ISemanticVersion? minVersion = min != null
+ ? new SemanticVersion(min, allowNonStandard: true)
+ : null;
+ ISemanticVersion? maxVersion = max != null
+ ? new SemanticVersion(max, allowNonStandard: true)
+ : null;
+
+ return this.IsBetween(minVersion, maxVersion);
}
/// <inheritdoc cref="ISemanticVersion.ToString" />
@@ -182,7 +198,12 @@ namespace StardewModdingAPI.Toolkit
/// <param name="version">The version string.</param>
/// <param name="parsed">The parsed representation.</param>
/// <returns>Returns whether parsing the version succeeded.</returns>
- public static bool TryParse(string version, out ISemanticVersion parsed)
+ public static bool TryParse(string? version,
+#if NET5_0_OR_GREATER
+ [NotNullWhen(true)]
+#endif
+ out ISemanticVersion? parsed
+ )
{
return SemanticVersion.TryParse(version, allowNonStandard: false, out parsed);
}
@@ -192,8 +213,19 @@ namespace StardewModdingAPI.Toolkit
/// <param name="allowNonStandard">Whether to allow non-standard extensions to semantic versioning.</param>
/// <param name="parsed">The parsed representation.</param>
/// <returns>Returns whether parsing the version succeeded.</returns>
- public static bool TryParse(string version, bool allowNonStandard, out ISemanticVersion parsed)
+ public static bool TryParse(string? version, bool allowNonStandard,
+#if NET5_0_OR_GREATER
+ [NotNullWhen(true)]
+#endif
+ out ISemanticVersion? parsed
+ )
{
+ if (version == null)
+ {
+ parsed = null;
+ return false;
+ }
+
try
{
parsed = new SemanticVersion(version, allowNonStandard);
@@ -212,7 +244,7 @@ namespace StardewModdingAPI.Toolkit
*********/
/// <summary>Get a normalized prerelease or build tag.</summary>
/// <param name="tag">The tag to normalize.</param>
- private string GetNormalizedTag(string tag)
+ private string? GetNormalizedTag(string? tag)
{
tag = tag?.Trim();
return !string.IsNullOrWhiteSpace(tag) ? tag : null;
@@ -224,7 +256,7 @@ namespace StardewModdingAPI.Toolkit
/// <param name="otherPatch">The patch version to compare with this instance.</param>
/// <param name="otherPlatformRelease">The non-standard platform release to compare with this instance.</param>
/// <param name="otherTag">The prerelease tag to compare with this instance.</param>
- private int CompareTo(int otherMajor, int otherMinor, int otherPatch, int otherPlatformRelease, string otherTag)
+ private int CompareTo(int otherMajor, int otherMinor, int otherPatch, int otherPlatformRelease, string? otherTag)
{
const int same = 0;
const int curNewer = 1;
@@ -253,8 +285,8 @@ namespace StardewModdingAPI.Toolkit
return curOlder;
// compare two prerelease tag values
- string[] curParts = this.PrereleaseTag.Split('.', '-');
- string[] otherParts = otherTag.Split('.', '-');
+ string[] curParts = this.PrereleaseTag?.Split('.', '-') ?? Array.Empty<string>();
+ string[] otherParts = otherTag?.Split('.', '-') ?? Array.Empty<string>();
int length = Math.Max(curParts.Length, otherParts.Length);
for (int i = 0; i < length; i++)
{
diff --git a/src/SMAPI.Toolkit/SemanticVersionComparer.cs b/src/SMAPI.Toolkit/SemanticVersionComparer.cs
index 9f6b57a2..85c974bd 100644
--- a/src/SMAPI.Toolkit/SemanticVersionComparer.cs
+++ b/src/SMAPI.Toolkit/SemanticVersionComparer.cs
@@ -9,14 +9,14 @@ namespace StardewModdingAPI.Toolkit
** Accessors
*********/
/// <summary>A singleton instance of the comparer.</summary>
- public static SemanticVersionComparer Instance { get; } = new SemanticVersionComparer();
+ public static SemanticVersionComparer Instance { get; } = new();
/*********
** Public methods
*********/
/// <inheritdoc />
- public int Compare(ISemanticVersion x, ISemanticVersion y)
+ public int Compare(ISemanticVersion? x, ISemanticVersion? y)
{
if (object.ReferenceEquals(x, y))
return 0;
diff --git a/src/SMAPI.Toolkit/Serialization/Converters/ManifestContentPackForConverter.cs b/src/SMAPI.Toolkit/Serialization/Converters/ManifestContentPackForConverter.cs
index 5cabe9d8..faaeedea 100644
--- a/src/SMAPI.Toolkit/Serialization/Converters/ManifestContentPackForConverter.cs
+++ b/src/SMAPI.Toolkit/Serialization/Converters/ManifestContentPackForConverter.cs
@@ -33,7 +33,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
/// <param name="objectType">The object type.</param>
/// <param name="existingValue">The object being read.</param>
/// <param name="serializer">The calling serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<ManifestContentPackFor>(reader);
}
@@ -42,7 +42,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
/// <param name="writer">The JSON writer.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
throw new InvalidOperationException("This converter does not write JSON.");
}
diff --git a/src/SMAPI.Toolkit/Serialization/Converters/ManifestDependencyArrayConverter.cs b/src/SMAPI.Toolkit/Serialization/Converters/ManifestDependencyArrayConverter.cs
index 7b88d6b7..c499a2c6 100644
--- a/src/SMAPI.Toolkit/Serialization/Converters/ManifestDependencyArrayConverter.cs
+++ b/src/SMAPI.Toolkit/Serialization/Converters/ManifestDependencyArrayConverter.cs
@@ -35,13 +35,13 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
/// <param name="objectType">The object type.</param>
/// <param name="existingValue">The object being read.</param>
/// <param name="serializer">The calling serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
List<ManifestDependency> result = new List<ManifestDependency>();
foreach (JObject obj in JArray.Load(reader).Children<JObject>())
{
- string uniqueID = obj.ValueIgnoreCase<string>(nameof(ManifestDependency.UniqueID));
- string minVersion = obj.ValueIgnoreCase<string>(nameof(ManifestDependency.MinimumVersion));
+ string uniqueID = obj.ValueIgnoreCase<string>(nameof(ManifestDependency.UniqueID))!; // will be validated separately if null
+ string? minVersion = obj.ValueIgnoreCase<string>(nameof(ManifestDependency.MinimumVersion));
bool required = obj.ValueIgnoreCase<bool?>(nameof(ManifestDependency.IsRequired)) ?? true;
result.Add(new ManifestDependency(uniqueID, minVersion, required));
}
@@ -52,7 +52,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
/// <param name="writer">The JSON writer.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
throw new InvalidOperationException("This converter does not write JSON.");
}
diff --git a/src/SMAPI.Toolkit/Serialization/Converters/SemanticVersionConverter.cs b/src/SMAPI.Toolkit/Serialization/Converters/SemanticVersionConverter.cs
index cf69104d..c32c3185 100644
--- a/src/SMAPI.Toolkit/Serialization/Converters/SemanticVersionConverter.cs
+++ b/src/SMAPI.Toolkit/Serialization/Converters/SemanticVersionConverter.cs
@@ -39,15 +39,17 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
/// <param name="objectType">The object type.</param>
/// <param name="existingValue">The object being read.</param>
/// <param name="serializer">The calling serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
string path = reader.Path;
switch (reader.TokenType)
{
case JsonToken.StartObject:
return this.ReadObject(JObject.Load(reader));
+
case JsonToken.String:
return this.ReadString(JToken.Load(reader).Value<string>(), path);
+
default:
throw new SParseException($"Can't parse {nameof(ISemanticVersion)} from {reader.TokenType} node (path: {reader.Path}).");
}
@@ -57,7 +59,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
/// <param name="writer">The JSON writer.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
writer.WriteValue(value?.ToString());
}
@@ -73,7 +75,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
int major = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.MajorVersion));
int minor = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.MinorVersion));
int patch = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.PatchVersion));
- string prereleaseTag = obj.ValueIgnoreCase<string>(nameof(ISemanticVersion.PrereleaseTag));
+ string? prereleaseTag = obj.ValueIgnoreCase<string>(nameof(ISemanticVersion.PrereleaseTag));
return new SemanticVersion(major, minor, patch, prereleaseTag: prereleaseTag);
}
@@ -81,11 +83,11 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
/// <summary>Read a JSON string.</summary>
/// <param name="str">The JSON string value.</param>
/// <param name="path">The path to the current JSON node.</param>
- private ISemanticVersion ReadString(string str, string path)
+ private ISemanticVersion? ReadString(string str, string path)
{
if (string.IsNullOrWhiteSpace(str))
return null;
- if (!SemanticVersion.TryParse(str, allowNonStandard: this.AllowNonStandard, out ISemanticVersion version))
+ if (!SemanticVersion.TryParse(str, allowNonStandard: this.AllowNonStandard, out ISemanticVersion? version))
throw new SParseException($"Can't parse semantic version from invalid value '{str}', should be formatted like 1.2, 1.2.30, or 1.2.30-beta (path: {path}).");
return version;
}
diff --git a/src/SMAPI.Toolkit/Serialization/Converters/SimpleReadOnlyConverter.cs b/src/SMAPI.Toolkit/Serialization/Converters/SimpleReadOnlyConverter.cs
index ccc5158b..1c59f5e7 100644
--- a/src/SMAPI.Toolkit/Serialization/Converters/SimpleReadOnlyConverter.cs
+++ b/src/SMAPI.Toolkit/Serialization/Converters/SimpleReadOnlyConverter.cs
@@ -25,21 +25,12 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
return objectType == typeof(T) || Nullable.GetUnderlyingType(objectType) == typeof(T);
}
- /// <summary>Writes the JSON representation of the object.</summary>
- /// <param name="writer">The JSON writer.</param>
- /// <param name="value">The value.</param>
- /// <param name="serializer">The calling serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- throw new InvalidOperationException("This converter does not write JSON.");
- }
-
/// <summary>Reads the JSON representation of the object.</summary>
/// <param name="reader">The JSON reader.</param>
/// <param name="objectType">The object type.</param>
/// <param name="existingValue">The object being read.</param>
/// <param name="serializer">The calling serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
string path = reader.Path;
switch (reader.TokenType)
@@ -58,6 +49,15 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
}
}
+ /// <summary>Writes the JSON representation of the object.</summary>
+ /// <param name="writer">The JSON writer.</param>
+ /// <param name="value">The value.</param>
+ /// <param name="serializer">The calling serializer.</param>
+ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
+ {
+ throw new InvalidOperationException("This converter does not write JSON.");
+ }
+
/*********
** Protected methods
diff --git a/src/SMAPI.Toolkit/Serialization/InternalExtensions.cs b/src/SMAPI.Toolkit/Serialization/InternalExtensions.cs
index 10f88dde..78297035 100644
--- a/src/SMAPI.Toolkit/Serialization/InternalExtensions.cs
+++ b/src/SMAPI.Toolkit/Serialization/InternalExtensions.cs
@@ -10,12 +10,12 @@ namespace StardewModdingAPI.Toolkit.Serialization
/// <typeparam name="T">The value type.</typeparam>
/// <param name="obj">The JSON object to search.</param>
/// <param name="fieldName">The field name.</param>
- public static T ValueIgnoreCase<T>(this JObject obj, string fieldName)
+ public static T? ValueIgnoreCase<T>(this JObject obj, string fieldName)
{
- JToken token = obj.GetValue(fieldName, StringComparison.OrdinalIgnoreCase);
+ JToken? token = obj.GetValue(fieldName, StringComparison.OrdinalIgnoreCase);
return token != null
? token.Value<T>()
- : default(T);
+ : default;
}
}
}
diff --git a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
index 00db9903..3c9308f2 100644
--- a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
+++ b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
@@ -14,7 +15,7 @@ namespace StardewModdingAPI.Toolkit.Serialization
** Accessors
*********/
/// <summary>The JSON settings to use when serializing and deserializing files.</summary>
- public JsonSerializerSettings JsonSettings { get; } = new JsonSerializerSettings
+ public JsonSerializerSettings JsonSettings { get; } = new()
{
Formatting = Formatting.Indented,
ObjectCreationHandling = ObjectCreationHandling.Replace, // avoid issue where default ICollection<T> values are duplicated each time the config is loaded
@@ -36,7 +37,12 @@ namespace StardewModdingAPI.Toolkit.Serialization
/// <returns>Returns false if the file doesn't exist, else true.</returns>
/// <exception cref="ArgumentException">The given <paramref name="fullPath"/> is empty or invalid.</exception>
/// <exception cref="JsonReaderException">The file contains invalid JSON.</exception>
- public bool ReadJsonFileIfExists<TModel>(string fullPath, out TModel result)
+ public bool ReadJsonFileIfExists<TModel>(string fullPath,
+#if NET5_0_OR_GREATER
+ [NotNullWhen(true)]
+#endif
+ out TModel? result
+ )
{
// validate
if (string.IsNullOrWhiteSpace(fullPath))
@@ -48,9 +54,9 @@ namespace StardewModdingAPI.Toolkit.Serialization
{
json = File.ReadAllText(fullPath);
}
- catch (Exception ex) when (ex is DirectoryNotFoundException || ex is FileNotFoundException)
+ catch (Exception ex) when (ex is DirectoryNotFoundException or FileNotFoundException)
{
- result = default(TModel);
+ result = default;
return false;
}
@@ -58,7 +64,7 @@ namespace StardewModdingAPI.Toolkit.Serialization
try
{
result = this.Deserialize<TModel>(json);
- return true;
+ return result != null;
}
catch (Exception ex)
{
@@ -88,7 +94,7 @@ namespace StardewModdingAPI.Toolkit.Serialization
throw new ArgumentException("The file path is empty or invalid.", nameof(fullPath));
// create directory if needed
- string dir = Path.GetDirectoryName(fullPath);
+ string dir = Path.GetDirectoryName(fullPath)!;
if (dir == null)
throw new ArgumentException("The file path is invalid.", nameof(fullPath));
if (!Directory.Exists(dir))
@@ -106,7 +112,8 @@ namespace StardewModdingAPI.Toolkit.Serialization
{
try
{
- return JsonConvert.DeserializeObject<TModel>(json, this.JsonSettings);
+ return JsonConvert.DeserializeObject<TModel>(json, this.JsonSettings)
+ ?? throw new InvalidOperationException($"Couldn't deserialize model type '{typeof(TModel)}' from empty or null JSON.");
}
catch (JsonReaderException)
{
@@ -115,7 +122,8 @@ namespace StardewModdingAPI.Toolkit.Serialization
{
try
{
- return JsonConvert.DeserializeObject<TModel>(json.Replace('“', '"').Replace('”', '"'), this.JsonSettings);
+ return JsonConvert.DeserializeObject<TModel>(json.Replace('“', '"').Replace('”', '"'), this.JsonSettings)
+ ?? throw new InvalidOperationException($"Couldn't deserialize model type '{typeof(TModel)}' from empty or null JSON.");
}
catch { /* rethrow original error */ }
}
diff --git a/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs b/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
index 4ad97b6d..01010602 100644
--- a/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
+++ b/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Runtime.Serialization;
+using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
using StardewModdingAPI.Toolkit.Serialization.Converters;
@@ -13,48 +13,45 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
** Accessors
*********/
/// <summary>The mod name.</summary>
- public string Name { get; set; }
+ public string Name { get; }
/// <summary>A brief description of the mod.</summary>
- public string Description { get; set; }
+ public string Description { get; }
/// <summary>The mod author's name.</summary>
- public string Author { get; set; }
+ public string Author { get; }
/// <summary>The mod version.</summary>
- public ISemanticVersion Version { get; set; }
+ public ISemanticVersion Version { get; }
/// <summary>The minimum SMAPI version required by this mod, if any.</summary>
- public ISemanticVersion MinimumApiVersion { get; set; }
+ public ISemanticVersion? MinimumApiVersion { get; }
/// <summary>The name of the DLL in the directory that has the <c>Entry</c> method. Mutually exclusive with <see cref="ContentPackFor"/>.</summary>
- public string EntryDll { get; set; }
+ public string? EntryDll { get; }
/// <summary>The mod which will read this as a content pack. Mutually exclusive with <see cref="Manifest.EntryDll"/>.</summary>
[JsonConverter(typeof(ManifestContentPackForConverter))]
- public IManifestContentPackFor ContentPackFor { get; set; }
+ public IManifestContentPackFor? ContentPackFor { get; }
/// <summary>The other mods that must be loaded before this mod.</summary>
[JsonConverter(typeof(ManifestDependencyArrayConverter))]
- public IManifestDependency[] Dependencies { get; set; }
+ public IManifestDependency[] Dependencies { get; }
/// <summary>The namespaced mod IDs to query for updates (like <c>Nexus:541</c>).</summary>
- public string[] UpdateKeys { get; set; }
+ public string[] UpdateKeys { get; private set; }
/// <summary>The unique mod ID.</summary>
- public string UniqueID { get; set; }
+ public string UniqueID { get; }
/// <summary>Any manifest fields which didn't match a valid field.</summary>
[JsonExtensionData]
- public IDictionary<string, object> ExtraFields { get; set; }
+ public IDictionary<string, object> ExtraFields { get; set; } = new Dictionary<string, object>();
/*********
** Public methods
*********/
- /// <summary>Construct an instance.</summary>
- public Manifest() { }
-
/// <summary>Construct an instance for a transitional content pack.</summary>
/// <param name="uniqueID">The unique mod ID.</param>
/// <param name="name">The mod name.</param>
@@ -62,24 +59,71 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
/// <param name="description">A brief description of the mod.</param>
/// <param name="version">The mod version.</param>
/// <param name="contentPackFor">The modID which will read this as a content pack.</param>
- public Manifest(string uniqueID, string name, string author, string description, ISemanticVersion version, string contentPackFor = null)
+ public Manifest(string uniqueID, string name, string author, string description, ISemanticVersion version, string? contentPackFor = null)
+ : this(
+ uniqueId: uniqueID,
+ name: name,
+ author: author,
+ description: description,
+ version: version,
+ minimumApiVersion: null,
+ entryDll: null,
+ contentPackFor: contentPackFor != null
+ ? new ManifestContentPackFor(contentPackFor, null)
+ : null,
+ dependencies: null,
+ updateKeys: null
+ )
+ { }
+
+ /// <summary>Construct an instance for a transitional content pack.</summary>
+ /// <param name="uniqueId">The unique mod ID.</param>
+ /// <param name="name">The mod name.</param>
+ /// <param name="author">The mod author's name.</param>
+ /// <param name="description">A brief description of the mod.</param>
+ /// <param name="version">The mod version.</param>
+ /// <param name="minimumApiVersion">The minimum SMAPI version required by this mod, if any.</param>
+ /// <param name="entryDll">The name of the DLL in the directory that has the <c>Entry</c> method. Mutually exclusive with <see cref="ContentPackFor"/>.</param>
+ /// <param name="contentPackFor">The modID which will read this as a content pack.</param>
+ /// <param name="dependencies">The other mods that must be loaded before this mod.</param>
+ /// <param name="updateKeys">The namespaced mod IDs to query for updates (like <c>Nexus:541</c>).</param>
+ [JsonConstructor]
+ public Manifest(string uniqueId, string name, string author, string description, ISemanticVersion version, ISemanticVersion? minimumApiVersion, string? entryDll, IManifestContentPackFor? contentPackFor, IManifestDependency[]? dependencies, string[]? updateKeys)
{
- this.Name = name;
- this.Author = author;
- this.Description = description;
+ this.UniqueID = this.NormalizeWhitespace(uniqueId);
+ this.Name = this.NormalizeWhitespace(name);
+ this.Author = this.NormalizeWhitespace(author);
+ this.Description = this.NormalizeWhitespace(description);
this.Version = version;
- this.UniqueID = uniqueID;
- this.UpdateKeys = Array.Empty<string>();
- this.ContentPackFor = new ManifestContentPackFor { UniqueID = contentPackFor };
+ this.MinimumApiVersion = minimumApiVersion;
+ this.EntryDll = this.NormalizeWhitespace(entryDll);
+ this.ContentPackFor = contentPackFor;
+ this.Dependencies = dependencies ?? Array.Empty<IManifestDependency>();
+ this.UpdateKeys = updateKeys ?? Array.Empty<string>();
+ }
+
+ /// <summary>Override the update keys loaded from the mod info.</summary>
+ /// <param name="updateKeys">The new update keys to set.</param>
+ internal void OverrideUpdateKeys(params string[] updateKeys)
+ {
+ this.UpdateKeys = updateKeys;
}
- /// <summary>Normalize the model after it's deserialized.</summary>
- /// <param name="context">The deserialization context.</param>
- [OnDeserialized]
- public void OnDeserialized(StreamingContext context)
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Normalize whitespace in a raw string.</summary>
+ /// <param name="input">The input to strip.</param>
+#if NET5_0_OR_GREATER
+ [return: NotNullIfNotNull("input")]
+#endif
+ private string? NormalizeWhitespace(string? input)
{
- this.Dependencies ??= Array.Empty<IManifestDependency>();
- this.UpdateKeys ??= Array.Empty<string>();
+ return input
+ ?.Trim()
+ .Replace("\r", "")
+ .Replace("\n", "");
}
}
}
diff --git a/src/SMAPI.Toolkit/Serialization/Models/ManifestContentPackFor.cs b/src/SMAPI.Toolkit/Serialization/Models/ManifestContentPackFor.cs
index 1eb80889..f7dc8aa8 100644
--- a/src/SMAPI.Toolkit/Serialization/Models/ManifestContentPackFor.cs
+++ b/src/SMAPI.Toolkit/Serialization/Models/ManifestContentPackFor.cs
@@ -1,3 +1,5 @@
+using System.Diagnostics.CodeAnalysis;
+
namespace StardewModdingAPI.Toolkit.Serialization.Models
{
/// <summary>Indicates which mod can read the content pack represented by the containing manifest.</summary>
@@ -7,9 +9,36 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
** Accessors
*********/
/// <summary>The unique ID of the mod which can read this content pack.</summary>
- public string UniqueID { get; set; }
+ public string UniqueID { get; }
/// <summary>The minimum required version (if any).</summary>
- public ISemanticVersion MinimumVersion { get; set; }
+ public ISemanticVersion? MinimumVersion { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="uniqueId">The unique ID of the mod which can read this content pack.</param>
+ /// <param name="minimumVersion">The minimum required version (if any).</param>
+ public ManifestContentPackFor(string uniqueId, ISemanticVersion? minimumVersion)
+ {
+ this.UniqueID = this.NormalizeWhitespace(uniqueId);
+ this.MinimumVersion = minimumVersion;
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Normalize whitespace in a raw string.</summary>
+ /// <param name="input">The input to strip.</param>
+#if NET5_0_OR_GREATER
+ [return: NotNullIfNotNull("input")]
+#endif
+ private string? NormalizeWhitespace(string? input)
+ {
+ return input?.Trim();
+ }
}
}
diff --git a/src/SMAPI.Toolkit/Serialization/Models/ManifestDependency.cs b/src/SMAPI.Toolkit/Serialization/Models/ManifestDependency.cs
index 00f168f4..fa254ea7 100644
--- a/src/SMAPI.Toolkit/Serialization/Models/ManifestDependency.cs
+++ b/src/SMAPI.Toolkit/Serialization/Models/ManifestDependency.cs
@@ -1,3 +1,6 @@
+using System.Diagnostics.CodeAnalysis;
+using Newtonsoft.Json;
+
namespace StardewModdingAPI.Toolkit.Serialization.Models
{
/// <summary>A mod dependency listed in a mod manifest.</summary>
@@ -7,13 +10,13 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
** Accessors
*********/
/// <summary>The unique mod ID to require.</summary>
- public string UniqueID { get; set; }
+ public string UniqueID { get; }
/// <summary>The minimum required version (if any).</summary>
- public ISemanticVersion MinimumVersion { get; set; }
+ public ISemanticVersion? MinimumVersion { get; }
/// <summary>Whether the dependency must be installed to use the mod.</summary>
- public bool IsRequired { get; set; }
+ public bool IsRequired { get; }
/*********
@@ -23,13 +26,40 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
/// <param name="uniqueID">The unique mod ID to require.</param>
/// <param name="minimumVersion">The minimum required version (if any).</param>
/// <param name="required">Whether the dependency must be installed to use the mod.</param>
- public ManifestDependency(string uniqueID, string minimumVersion, bool required = true)
+ public ManifestDependency(string uniqueID, string? minimumVersion, bool required = true)
+ : this(
+ uniqueID: uniqueID,
+ minimumVersion: !string.IsNullOrWhiteSpace(minimumVersion)
+ ? new SemanticVersion(minimumVersion)
+ : null,
+ required: required
+ )
+ { }
+
+ /// <summary>Construct an instance.</summary>
+ /// <param name="uniqueID">The unique mod ID to require.</param>
+ /// <param name="minimumVersion">The minimum required version (if any).</param>
+ /// <param name="required">Whether the dependency must be installed to use the mod.</param>
+ [JsonConstructor]
+ public ManifestDependency(string uniqueID, ISemanticVersion? minimumVersion, bool required = true)
{
- this.UniqueID = uniqueID;
- this.MinimumVersion = !string.IsNullOrWhiteSpace(minimumVersion)
- ? new SemanticVersion(minimumVersion)
- : null;
+ this.UniqueID = this.NormalizeWhitespace(uniqueID);
+ this.MinimumVersion = minimumVersion;
this.IsRequired = required;
}
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Normalize whitespace in a raw string.</summary>
+ /// <param name="input">The input to strip.</param>
+#if NET5_0_OR_GREATER
+ [return: NotNullIfNotNull("input")]
+#endif
+ private string? NormalizeWhitespace(string? input)
+ {
+ return input?.Trim();
+ }
}
}
diff --git a/src/SMAPI.Toolkit/Serialization/SParseException.cs b/src/SMAPI.Toolkit/Serialization/SParseException.cs
index 5f58b5b8..c2b3f68e 100644
--- a/src/SMAPI.Toolkit/Serialization/SParseException.cs
+++ b/src/SMAPI.Toolkit/Serialization/SParseException.cs
@@ -11,7 +11,7 @@ namespace StardewModdingAPI.Toolkit.Serialization
/// <summary>Construct an instance.</summary>
/// <param name="message">The error message.</param>
/// <param name="ex">The underlying exception, if any.</param>
- public SParseException(string message, Exception ex = null)
+ public SParseException(string message, Exception? ex = null)
: base(message, ex) { }
}
}
diff --git a/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs b/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs
index 7536337a..1791c5b3 100644
--- a/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs
+++ b/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs
@@ -1,5 +1,4 @@
using System;
-using System.Diagnostics.CodeAnalysis;
using StardewModdingAPI.Toolkit.Framework;
namespace StardewModdingAPI.Toolkit.Utilities
@@ -34,7 +33,6 @@ namespace StardewModdingAPI.Toolkit.Utilities
/// <summary>Get the human-readable OS name and version.</summary>
/// <param name="platform">The current platform.</param>
- [SuppressMessage("ReSharper", "EmptyGeneralCatchClause", Justification = "Error suppressed deliberately to fallback to default behaviour.")]
public static string GetFriendlyPlatformName(Platform platform)
{
return LowLevelEnvironmentUtility.GetFriendlyPlatformName(platform.ToString());
diff --git a/src/SMAPI.Toolkit/Utilities/PathUtilities.cs b/src/SMAPI.Toolkit/Utilities/PathUtilities.cs
index 2e9e5eac..136279f2 100644
--- a/src/SMAPI.Toolkit/Utilities/PathUtilities.cs
+++ b/src/SMAPI.Toolkit/Utilities/PathUtilities.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
@@ -36,8 +37,11 @@ namespace StardewModdingAPI.Toolkit.Utilities
/// <param name="path">The path to split.</param>
/// <param name="limit">The number of segments to match. Any additional segments will be merged into the last returned part.</param>
[Pure]
- public static string[] GetSegments(string path, int? limit = null)
+ public static string[] GetSegments(string? path, int? limit = null)
{
+ if (path == null)
+ return Array.Empty<string>();
+
return limit.HasValue
? path.Split(PathUtilities.PossiblePathSeparators, limit.Value, StringSplitOptions.RemoveEmptyEntries)
: path.Split(PathUtilities.PossiblePathSeparators, StringSplitOptions.RemoveEmptyEntries);
@@ -45,8 +49,16 @@ namespace StardewModdingAPI.Toolkit.Utilities
/// <summary>Normalize an asset name to match how MonoGame's content APIs would normalize and cache it.</summary>
/// <param name="assetName">The asset name to normalize.</param>
- public static string NormalizeAssetName(string assetName)
+ [Pure]
+#if NET5_0_OR_GREATER
+ [return: NotNullIfNotNull("assetName")]
+#endif
+ public static string? NormalizeAssetName(string? assetName)
{
+ assetName = assetName?.Trim();
+ if (string.IsNullOrEmpty(assetName))
+ return assetName;
+
return string.Join(PathUtilities.PreferredAssetSeparator.ToString(), PathUtilities.GetSegments(assetName)); // based on MonoGame's ContentManager.Load<T> logic
}
@@ -54,7 +66,10 @@ namespace StardewModdingAPI.Toolkit.Utilities
/// <param name="path">The file path to normalize.</param>
/// <remarks>This should only be used for file paths. For asset names, use <see cref="NormalizeAssetName"/> instead.</remarks>
[Pure]
- public static string NormalizePath(string path)
+#if NET5_0_OR_GREATER
+ [return: NotNullIfNotNull("path")]
+#endif
+ public static string? NormalizePath(string? path)
{
path = path?.Trim();
if (string.IsNullOrEmpty(path))
@@ -100,8 +115,8 @@ namespace StardewModdingAPI.Toolkit.Utilities
// though, this is only for compatibility with the mod build package.
// convert to URIs
- Uri from = new Uri(sourceDir.TrimEnd(PathUtilities.PossiblePathSeparators) + "/");
- Uri to = new Uri(targetPath.TrimEnd(PathUtilities.PossiblePathSeparators) + "/");
+ Uri from = new(sourceDir.TrimEnd(PathUtilities.PossiblePathSeparators) + "/");
+ Uri to = new(targetPath.TrimEnd(PathUtilities.PossiblePathSeparators) + "/");
if (from.Scheme != to.Scheme)
throw new InvalidOperationException($"Can't get path for '{targetPath}' relative to '{sourceDir}'.");
@@ -132,7 +147,7 @@ namespace StardewModdingAPI.Toolkit.Utilities
/// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary>
/// <param name="path">The path to check.</param>
[Pure]
- public static bool IsSafeRelativePath(string path)
+ public static bool IsSafeRelativePath(string? path)
{
if (string.IsNullOrWhiteSpace(path))
return true;
@@ -145,9 +160,11 @@ namespace StardewModdingAPI.Toolkit.Utilities
/// <summary>Get whether a string is a valid 'slug', containing only basic characters that are safe in all contexts (e.g. filenames, URLs, etc).</summary>
/// <param name="str">The string to check.</param>
[Pure]
- public static bool IsSlug(string str)
+ public static bool IsSlug(string? str)
{
- return !Regex.IsMatch(str, "[^a-z0-9_.-]", RegexOptions.IgnoreCase);
+ return
+ string.IsNullOrWhiteSpace(str)
+ || !Regex.IsMatch(str, "[^a-z0-9_.-]", RegexOptions.IgnoreCase);
}
}
}
diff --git a/src/SMAPI.Web/BackgroundService.cs b/src/SMAPI.Web/BackgroundService.cs
index 64bd5ca5..7706b276 100644
--- a/src/SMAPI.Web/BackgroundService.cs
+++ b/src/SMAPI.Web/BackgroundService.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs
index 5097997c..f7834b9c 100644
--- a/src/SMAPI.Web/Controllers/IndexController.cs
+++ b/src/SMAPI.Web/Controllers/IndexController.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -94,7 +96,7 @@ namespace StardewModdingAPI.Web.Controllers
// strip 'noinclude' blocks from release description
if (release != null)
{
- HtmlDocument doc = new HtmlDocument();
+ HtmlDocument doc = new();
doc.LoadHtml(release.Body);
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//*[@class='noinclude']")?.ToArray() ?? Array.Empty<HtmlNode>())
node.Remove();
diff --git a/src/SMAPI.Web/Controllers/JsonValidatorController.cs b/src/SMAPI.Web/Controllers/JsonValidatorController.cs
index e06c1236..5791d834 100644
--- a/src/SMAPI.Web/Controllers/JsonValidatorController.cs
+++ b/src/SMAPI.Web/Controllers/JsonValidatorController.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -161,7 +163,7 @@ namespace StardewModdingAPI.Web.Controllers
return this.View("Index", this.GetModel(result.ID, schemaName, isEditView: true).SetContent(input, null).SetUploadError(result.UploadError));
// redirect to view
- return this.Redirect(this.Url.PlainAction("Index", "JsonValidator", new { schemaName = schemaName, id = result.ID }));
+ return this.Redirect(this.Url.PlainAction("Index", "JsonValidator", new { schemaName, id = result.ID }));
}
@@ -197,7 +199,7 @@ namespace StardewModdingAPI.Web.Controllers
return null;
// get matching file
- DirectoryInfo schemaDir = new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "schemas"));
+ DirectoryInfo schemaDir = new(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "schemas"));
foreach (FileInfo file in schemaDir.EnumerateFiles("*.json"))
{
if (file.Name.Equals($"{id}.json"))
@@ -317,13 +319,10 @@ namespace StardewModdingAPI.Web.Controllers
/// <param name="key">The case-insensitive field key.</param>
private T GetExtensionField<T>(JSchema schema, string key)
{
- if (schema.ExtensionData != null)
+ foreach ((string curKey, JToken value) in schema.ExtensionData)
{
- foreach ((string curKey, JToken value) in schema.ExtensionData)
- {
- if (curKey.Equals(key, StringComparison.OrdinalIgnoreCase))
- return value.ToObject<T>();
- }
+ if (curKey.Equals(key, StringComparison.OrdinalIgnoreCase))
+ return value.ToObject<T>();
}
return default;
diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs
index db53d942..524cfbcc 100644
--- a/src/SMAPI.Web/Controllers/LogParserController.cs
+++ b/src/SMAPI.Web/Controllers/LogParserController.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Linq;
using System.Text;
diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs
index dfe2504b..3dc1e366 100644
--- a/src/SMAPI.Web/Controllers/ModsApiController.cs
+++ b/src/SMAPI.Web/Controllers/ModsApiController.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -135,7 +137,7 @@ namespace StardewModdingAPI.Web.Controllers
bool isSmapiBeta = apiVersion.IsPrerelease() && apiVersion.PrereleaseTag.StartsWith("beta");
// get latest versions
- ModEntryModel result = new ModEntryModel { ID = search.ID };
+ ModEntryModel result = new() { ID = search.ID };
IList<string> errors = new List<string>();
ModEntryVersionModel main = null;
ModEntryVersionModel optional = null;
diff --git a/src/SMAPI.Web/Controllers/ModsController.cs b/src/SMAPI.Web/Controllers/ModsController.cs
index c62ed605..5292e1ce 100644
--- a/src/SMAPI.Web/Controllers/ModsController.cs
+++ b/src/SMAPI.Web/Controllers/ModsController.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc;
diff --git a/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs b/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs
index 864aa215..108ceff7 100644
--- a/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs
+++ b/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Filters;
diff --git a/src/SMAPI.Web/Framework/Caching/Cached.cs b/src/SMAPI.Web/Framework/Caching/Cached.cs
index 52041a16..aabbf146 100644
--- a/src/SMAPI.Web/Framework/Caching/Cached.cs
+++ b/src/SMAPI.Web/Framework/Caching/Cached.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Web.Framework.Caching
diff --git a/src/SMAPI.Web/Framework/Caching/Mods/IModCacheRepository.cs b/src/SMAPI.Web/Framework/Caching/Mods/IModCacheRepository.cs
index 0d912c7b..2020d747 100644
--- a/src/SMAPI.Web/Framework/Caching/Mods/IModCacheRepository.cs
+++ b/src/SMAPI.Web/Framework/Caching/Mods/IModCacheRepository.cs
@@ -1,6 +1,7 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Toolkit.Framework.UpdateData;
-using StardewModdingAPI.Web.Framework.Clients;
namespace StardewModdingAPI.Web.Framework.Caching.Mods
{
diff --git a/src/SMAPI.Web/Framework/Caching/Mods/ModCacheMemoryRepository.cs b/src/SMAPI.Web/Framework/Caching/Mods/ModCacheMemoryRepository.cs
index 9769793c..338562d8 100644
--- a/src/SMAPI.Web/Framework/Caching/Mods/ModCacheMemoryRepository.cs
+++ b/src/SMAPI.Web/Framework/Caching/Mods/ModCacheMemoryRepository.cs
@@ -1,8 +1,9 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
using StardewModdingAPI.Toolkit.Framework.UpdateData;
-using StardewModdingAPI.Web.Framework.Clients;
namespace StardewModdingAPI.Web.Framework.Caching.Mods
{
diff --git a/src/SMAPI.Web/Framework/Caching/Wiki/IWikiCacheRepository.cs b/src/SMAPI.Web/Framework/Caching/Wiki/IWikiCacheRepository.cs
index 2ab7ea5a..6edafddc 100644
--- a/src/SMAPI.Web/Framework/Caching/Wiki/IWikiCacheRepository.cs
+++ b/src/SMAPI.Web/Framework/Caching/Wiki/IWikiCacheRepository.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
diff --git a/src/SMAPI.Web/Framework/Caching/Wiki/WikiCacheMemoryRepository.cs b/src/SMAPI.Web/Framework/Caching/Wiki/WikiCacheMemoryRepository.cs
index d037a123..d1ccb9c7 100644
--- a/src/SMAPI.Web/Framework/Caching/Wiki/WikiCacheMemoryRepository.cs
+++ b/src/SMAPI.Web/Framework/Caching/Wiki/WikiCacheMemoryRepository.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI.Web/Framework/Caching/Wiki/WikiMetadata.cs b/src/SMAPI.Web/Framework/Caching/Wiki/WikiMetadata.cs
index c04de4a5..6ae42488 100644
--- a/src/SMAPI.Web/Framework/Caching/Wiki/WikiMetadata.cs
+++ b/src/SMAPI.Web/Framework/Caching/Wiki/WikiMetadata.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.Caching.Wiki
{
/// <summary>The model for cached wiki metadata.</summary>
diff --git a/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs b/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs
index b8b05878..4d041c1b 100644
--- a/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/Chucklefish/ChucklefishClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Net;
using System.Threading.Tasks;
@@ -58,7 +60,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Chucklefish
.GetAsync(string.Format(this.ModPageUrlFormat, parsedId))
.AsString();
}
- catch (ApiException ex) when (ex.Status == HttpStatusCode.NotFound || ex.Status == HttpStatusCode.Forbidden)
+ catch (ApiException ex) when (ex.Status is HttpStatusCode.NotFound or HttpStatusCode.Forbidden)
{
return page.SetError(RemoteModStatus.DoesNotExist, "Found no Chucklefish mod with this ID.");
}
@@ -90,7 +92,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Chucklefish
/// <param name="id">The mod ID.</param>
private string GetModUrl(uint id)
{
- UriBuilder builder = new UriBuilder(this.Client.BaseClient.BaseAddress);
+ UriBuilder builder = new(this.Client.BaseClient.BaseAddress);
builder.Path += string.Format(this.ModPageUrlFormat, id);
return builder.Uri.ToString();
}
diff --git a/src/SMAPI.Web/Framework/Clients/CurseForge/CurseForgeClient.cs b/src/SMAPI.Web/Framework/Clients/CurseForge/CurseForgeClient.cs
index d8008721..5ef369d5 100644
--- a/src/SMAPI.Web/Framework/Clients/CurseForge/CurseForgeClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/CurseForge/CurseForgeClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -17,7 +19,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.CurseForge
private readonly IClient Client;
/// <summary>A regex pattern which matches a version number in a CurseForge mod file name.</summary>
- private readonly Regex VersionInNamePattern = new Regex(@"^(?:.+? | *)v?(\d+\.\d+(?:\.\d+)?(?:-.+?)?) *(?:\.(?:zip|rar|7z))?$", RegexOptions.Compiled);
+ private readonly Regex VersionInNamePattern = new(@"^(?:.+? | *)v?(\d+\.\d+(?:\.\d+)?(?:-.+?)?) *(?:\.(?:zip|rar|7z))?$", RegexOptions.Compiled);
/*********
diff --git a/src/SMAPI.Web/Framework/Clients/CurseForge/ResponseModels/ModFileModel.cs b/src/SMAPI.Web/Framework/Clients/CurseForge/ResponseModels/ModFileModel.cs
index 9de74847..eabef9f0 100644
--- a/src/SMAPI.Web/Framework/Clients/CurseForge/ResponseModels/ModFileModel.cs
+++ b/src/SMAPI.Web/Framework/Clients/CurseForge/ResponseModels/ModFileModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.Clients.CurseForge.ResponseModels
{
/// <summary>Metadata from the CurseForge API about a mod file.</summary>
diff --git a/src/SMAPI.Web/Framework/Clients/CurseForge/ResponseModels/ModModel.cs b/src/SMAPI.Web/Framework/Clients/CurseForge/ResponseModels/ModModel.cs
index 48cd185b..a95df7f1 100644
--- a/src/SMAPI.Web/Framework/Clients/CurseForge/ResponseModels/ModModel.cs
+++ b/src/SMAPI.Web/Framework/Clients/CurseForge/ResponseModels/ModModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.Clients.CurseForge.ResponseModels
{
/// <summary>An mod from the CurseForge API.</summary>
diff --git a/src/SMAPI.Web/Framework/Clients/GenericModDownload.cs b/src/SMAPI.Web/Framework/Clients/GenericModDownload.cs
index f08b471c..919072b0 100644
--- a/src/SMAPI.Web/Framework/Clients/GenericModDownload.cs
+++ b/src/SMAPI.Web/Framework/Clients/GenericModDownload.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.Clients
{
/// <summary>Generic metadata about a file download on a mod page.</summary>
diff --git a/src/SMAPI.Web/Framework/Clients/GenericModPage.cs b/src/SMAPI.Web/Framework/Clients/GenericModPage.cs
index a5f7c9b9..4788aa2a 100644
--- a/src/SMAPI.Web/Framework/Clients/GenericModPage.cs
+++ b/src/SMAPI.Web/Framework/Clients/GenericModPage.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitAsset.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitAsset.cs
index 73ce4025..39ebf94e 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitAsset.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitAsset.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Newtonsoft.Json;
namespace StardewModdingAPI.Web.Framework.Clients.GitHub
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
index 671f077c..0e68e2c2 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Linq;
using System.Net;
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitLicense.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitLicense.cs
index 736efbe6..275c775a 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitLicense.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitLicense.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Newtonsoft.Json;
namespace StardewModdingAPI.Web.Framework.Clients.GitHub
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
index d0db5297..383775d2 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Newtonsoft.Json;
namespace StardewModdingAPI.Web.Framework.Clients.GitHub
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitRepo.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitRepo.cs
index 7d80576e..5b5ce6a6 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitRepo.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitRepo.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Newtonsoft.Json;
namespace StardewModdingAPI.Web.Framework.Clients.GitHub
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
index 0d6f4643..e1961416 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Threading.Tasks;
diff --git a/src/SMAPI.Web/Framework/Clients/IModSiteClient.cs b/src/SMAPI.Web/Framework/Clients/IModSiteClient.cs
index 33277711..2cd1f635 100644
--- a/src/SMAPI.Web/Framework/Clients/IModSiteClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/IModSiteClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Threading.Tasks;
using StardewModdingAPI.Toolkit.Framework.UpdateData;
diff --git a/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs b/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs
index 3a1c5b9d..1a11a606 100644
--- a/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/ModDrop/ModDropClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Threading.Tasks;
using Pathoschild.Http.Client;
diff --git a/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/FileDataModel.cs b/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/FileDataModel.cs
index b01196f4..dd6a95e0 100644
--- a/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/FileDataModel.cs
+++ b/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/FileDataModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Newtonsoft.Json;
namespace StardewModdingAPI.Web.Framework.Clients.ModDrop.ResponseModels
diff --git a/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModDataModel.cs b/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModDataModel.cs
index cfdd6a4e..6cae16d9 100644
--- a/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModDataModel.cs
+++ b/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModDataModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.Clients.ModDrop.ResponseModels
{
/// <summary>Metadata about a mod from the ModDrop API.</summary>
diff --git a/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModListModel.cs b/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModListModel.cs
index 7f692ca1..445e25cb 100644
--- a/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModListModel.cs
+++ b/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModListModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
namespace StardewModdingAPI.Web.Framework.Clients.ModDrop.ResponseModels
diff --git a/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModModel.cs b/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModModel.cs
index 9f4b2c6f..8869193e 100644
--- a/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModModel.cs
+++ b/src/SMAPI.Web/Framework/Clients/ModDrop/ResponseModels/ModModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.Clients.ModDrop.ResponseModels
{
/// <summary>An entry in a mod list from the ModDrop API.</summary>
diff --git a/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs b/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs
index 4ba94f81..dd0bb94f 100644
--- a/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/Nexus/NexusClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -75,7 +77,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Nexus
mod = await this.GetModFromApiAsync(parsedId);
// page doesn't exist
- if (mod == null || mod.Status == NexusModStatus.Hidden || mod.Status == NexusModStatus.NotPublished)
+ if (mod == null || mod.Status is NexusModStatus.Hidden or NexusModStatus.NotPublished)
return page.SetError(RemoteModStatus.DoesNotExist, "Found no Nexus mod with this ID.");
// return info
@@ -195,7 +197,7 @@ namespace StardewModdingAPI.Web.Framework.Clients.Nexus
/// <param name="id">The mod ID.</param>
private string GetModUrl(uint id)
{
- UriBuilder builder = new UriBuilder(this.WebClient.BaseClient.BaseAddress);
+ UriBuilder builder = new(this.WebClient.BaseClient.BaseAddress);
builder.Path += string.Format(this.WebModUrlFormat, id);
return builder.Uri.ToString();
}
diff --git a/src/SMAPI.Web/Framework/Clients/Nexus/ResponseModels/NexusMod.cs b/src/SMAPI.Web/Framework/Clients/Nexus/ResponseModels/NexusMod.cs
index aef90ede..358c4633 100644
--- a/src/SMAPI.Web/Framework/Clients/Nexus/ResponseModels/NexusMod.cs
+++ b/src/SMAPI.Web/Framework/Clients/Nexus/ResponseModels/NexusMod.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Newtonsoft.Json;
namespace StardewModdingAPI.Web.Framework.Clients.Nexus.ResponseModels
diff --git a/src/SMAPI.Web/Framework/Clients/Pastebin/IPastebinClient.cs b/src/SMAPI.Web/Framework/Clients/Pastebin/IPastebinClient.cs
index 431fed7b..03c78e01 100644
--- a/src/SMAPI.Web/Framework/Clients/Pastebin/IPastebinClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/Pastebin/IPastebinClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Threading.Tasks;
diff --git a/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs b/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs
index 813ea115..2d48a7ae 100644
--- a/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs
+++ b/src/SMAPI.Web/Framework/Clients/Pastebin/PasteInfo.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.Clients.Pastebin
{
/// <summary>The response for a get-paste request.</summary>
diff --git a/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs b/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs
index 1be00be7..d0cdf374 100644
--- a/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/Pastebin/PastebinClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Net;
using System.Threading.Tasks;
diff --git a/src/SMAPI.Web/Framework/Compression/GzipHelper.cs b/src/SMAPI.Web/Framework/Compression/GzipHelper.cs
index 676d660d..843b7735 100644
--- a/src/SMAPI.Web/Framework/Compression/GzipHelper.cs
+++ b/src/SMAPI.Web/Framework/Compression/GzipHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.IO;
using System.IO.Compression;
@@ -29,9 +31,9 @@ namespace StardewModdingAPI.Web.Framework.Compression
// compressed
byte[] compressedData;
- using (MemoryStream stream = new MemoryStream())
+ using (MemoryStream stream = new())
{
- using (GZipStream zipStream = new GZipStream(stream, CompressionLevel.Optimal, leaveOpen: true))
+ using (GZipStream zipStream = new(stream, CompressionLevel.Optimal, leaveOpen: true))
zipStream.Write(buffer, 0, buffer.Length);
stream.Position = 0;
@@ -69,7 +71,7 @@ namespace StardewModdingAPI.Web.Framework.Compression
return rawText;
// decompress
- using MemoryStream memoryStream = new MemoryStream();
+ using MemoryStream memoryStream = new();
{
// read length prefix
int dataLength = BitConverter.ToInt32(zipBuffer, 0);
@@ -78,7 +80,7 @@ namespace StardewModdingAPI.Web.Framework.Compression
// read data
byte[] buffer = new byte[dataLength];
memoryStream.Position = 0;
- using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
+ using (GZipStream gZipStream = new(memoryStream, CompressionMode.Decompress))
gZipStream.Read(buffer, 0, buffer.Length);
// return original string
diff --git a/src/SMAPI.Web/Framework/Compression/IGzipHelper.cs b/src/SMAPI.Web/Framework/Compression/IGzipHelper.cs
index a000865e..e1ec9b67 100644
--- a/src/SMAPI.Web/Framework/Compression/IGzipHelper.cs
+++ b/src/SMAPI.Web/Framework/Compression/IGzipHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.Compression
{
/// <summary>Handles GZip compression logic.</summary>
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
index 878130bf..3730a9db 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.ConfigModels
{
/// <summary>The config settings for the API clients.</summary>
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModOverrideConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModOverrideConfig.cs
index f382d7b5..682c97e6 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/ModOverrideConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/ModOverrideConfig.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.ConfigModels
{
/// <summary>Override update-check metadata for a mod.</summary>
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
index aea695b8..e525e09a 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.ConfigModels
{
/// <summary>The config settings for mod update checks.</summary>
diff --git a/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs
index 664dbef3..ef6c2659 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.ConfigModels
{
/// <summary>The site config settings.</summary>
diff --git a/src/SMAPI.Web/Framework/ConfigModels/SmapiInfoConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/SmapiInfoConfig.cs
index d69fabb3..dbf58817 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/SmapiInfoConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/SmapiInfoConfig.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.ConfigModels
{
/// <summary>The update-check config for SMAPI's own update checks.</summary>
diff --git a/src/SMAPI.Web/Framework/Extensions.cs b/src/SMAPI.Web/Framework/Extensions.cs
index 5305b142..a72c12c1 100644
--- a/src/SMAPI.Web/Framework/Extensions.cs
+++ b/src/SMAPI.Web/Framework/Extensions.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Html;
@@ -29,7 +31,7 @@ namespace StardewModdingAPI.Web.Framework
public static string PlainAction(this IUrlHelper helper, [AspMvcAction] string action, [AspMvcController] string controller, object values = null, bool absoluteUrl = false)
{
// get route values
- RouteValueDictionary valuesDict = new RouteValueDictionary(values);
+ RouteValueDictionary valuesDict = new(values);
foreach (var value in helper.ActionContext.RouteData.Values)
{
if (!valuesDict.ContainsKey(value.Key))
@@ -45,7 +47,7 @@ namespace StardewModdingAPI.Web.Framework
if (absoluteUrl)
{
HttpRequest request = helper.ActionContext.HttpContext.Request;
- Uri baseUri = new Uri($"{request.Scheme}://{request.Host}");
+ Uri baseUri = new($"{request.Scheme}://{request.Host}");
url = new Uri(baseUri, url).ToString();
}
diff --git a/src/SMAPI.Web/Framework/IModDownload.cs b/src/SMAPI.Web/Framework/IModDownload.cs
index dc058bcb..b8d1f62c 100644
--- a/src/SMAPI.Web/Framework/IModDownload.cs
+++ b/src/SMAPI.Web/Framework/IModDownload.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework
{
/// <summary>Generic metadata about a file download on a mod page.</summary>
diff --git a/src/SMAPI.Web/Framework/IModPage.cs b/src/SMAPI.Web/Framework/IModPage.cs
index e66d401f..68220b49 100644
--- a/src/SMAPI.Web/Framework/IModPage.cs
+++ b/src/SMAPI.Web/Framework/IModPage.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using StardewModdingAPI.Toolkit.Framework.UpdateData;
diff --git a/src/SMAPI.Web/Framework/InternalControllerFeatureProvider.cs b/src/SMAPI.Web/Framework/InternalControllerFeatureProvider.cs
index 2c24c610..98738a82 100644
--- a/src/SMAPI.Web/Framework/InternalControllerFeatureProvider.cs
+++ b/src/SMAPI.Web/Framework/InternalControllerFeatureProvider.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
diff --git a/src/SMAPI.Web/Framework/JobDashboardAuthorizationFilter.cs b/src/SMAPI.Web/Framework/JobDashboardAuthorizationFilter.cs
index 385c0c91..8db43dca 100644
--- a/src/SMAPI.Web/Framework/JobDashboardAuthorizationFilter.cs
+++ b/src/SMAPI.Web/Framework/JobDashboardAuthorizationFilter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Hangfire.Dashboard;
namespace StardewModdingAPI.Web.Framework
@@ -9,7 +11,7 @@ namespace StardewModdingAPI.Web.Framework
** Fields
*********/
/// <summary>An authorization filter that allows local requests.</summary>
- private static readonly LocalRequestsOnlyAuthorizationFilter LocalRequestsOnlyFilter = new LocalRequestsOnlyAuthorizationFilter();
+ private static readonly LocalRequestsOnlyAuthorizationFilter LocalRequestsOnlyFilter = new();
/*********
diff --git a/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs b/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs
index 992876ef..1b692e63 100644
--- a/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs
+++ b/src/SMAPI.Web/Framework/LogParsing/LogMessageBuilder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Text;
using StardewModdingAPI.Web.Framework.LogParsing.Models;
@@ -23,7 +25,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
public string Mod { get; set; }
/// <summary>The text for the next log message.</summary>
- private readonly StringBuilder Text = new StringBuilder();
+ private readonly StringBuilder Text = new();
/*********
diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParseException.cs b/src/SMAPI.Web/Framework/LogParsing/LogParseException.cs
index 5d4c8c08..4ee58433 100644
--- a/src/SMAPI.Web/Framework/LogParsing/LogParseException.cs
+++ b/src/SMAPI.Web/Framework/LogParsing/LogParseException.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Web.Framework.LogParsing
@@ -10,6 +12,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
*********/
/// <summary>Construct an instance.</summary>
/// <param name="message">The user-friendly error message.</param>
- public LogParseException(string message) : base(message) { }
+ public LogParseException(string message)
+ : base(message) { }
}
}
diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs
index 887d0105..4e61ac95 100644
--- a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs
+++ b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -14,38 +16,38 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
** Fields
*********/
/// <summary>A regex pattern matching the start of a SMAPI message.</summary>
- private readonly Regex MessageHeaderPattern = new Regex(@"^\[(?<time>\d\d[:\.]\d\d[:\.]\d\d) (?<level>[a-z]+)(?: +screen_(?<screen>\d+))? +(?<modName>[^\]]+)\] ", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex MessageHeaderPattern = new(@"^\[(?<time>\d\d[:\.]\d\d[:\.]\d\d) (?<level>[a-z]+)(?: +screen_(?<screen>\d+))? +(?<modName>[^\]]+)\] ", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching SMAPI's initial platform info message.</summary>
- private readonly Regex InfoLinePattern = new Regex(@"^SMAPI (?<apiVersion>.+) with Stardew Valley (?<gameVersion>.+) on (?<os>.+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex InfoLinePattern = new(@"^SMAPI (?<apiVersion>.+) with Stardew Valley (?<gameVersion>.+) on (?<os>.+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching SMAPI's mod folder path line.</summary>
- private readonly Regex ModPathPattern = new Regex(@"^Mods go here: (?<path>.+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex ModPathPattern = new(@"^Mods go here: (?<path>.+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching SMAPI's log timestamp line.</summary>
- private readonly Regex LogStartedAtPattern = new Regex(@"^Log started at (?<timestamp>.+) UTC", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex LogStartedAtPattern = new(@"^Log started at (?<timestamp>.+) UTC", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching the start of SMAPI's mod list.</summary>
- private readonly Regex ModListStartPattern = new Regex(@"^Loaded \d+ mods:$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex ModListStartPattern = new(@"^Loaded \d+ mods:$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching an entry in SMAPI's mod list.</summary>
/// <remarks>The author name and description are optional.</remarks>
- private readonly Regex ModListEntryPattern = new Regex(@"^ (?<name>.+?) (?<version>[^\s]+)(?: by (?<author>[^\|]+))?(?: \| (?<description>.+))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex ModListEntryPattern = new(@"^ (?<name>.+?) (?<version>[^\s]+)(?: by (?<author>[^\|]+))?(?: \| (?<description>.+))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching the start of SMAPI's content pack list.</summary>
- private readonly Regex ContentPackListStartPattern = new Regex(@"^Loaded \d+ content packs:$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex ContentPackListStartPattern = new(@"^Loaded \d+ content packs:$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching an entry in SMAPI's content pack list.</summary>
- private readonly Regex ContentPackListEntryPattern = new Regex(@"^ (?<name>.+?) (?<version>[^\s]+)(?: by (?<author>[^\|]+))? \| for (?<for>[^\|]+)(?: \| (?<description>.+))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex ContentPackListEntryPattern = new(@"^ (?<name>.+?) (?<version>[^\s]+)(?: by (?<author>[^\|]+))? \| for (?<for>[^\|]+)(?: \| (?<description>.+))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching the start of SMAPI's mod update list.</summary>
- private readonly Regex ModUpdateListStartPattern = new Regex(@"^You can update \d+ mods?:$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex ModUpdateListStartPattern = new(@"^You can update \d+ mods?:$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching an entry in SMAPI's mod update list.</summary>
- private readonly Regex ModUpdateListEntryPattern = new Regex(@"^ (?<name>.+) (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex ModUpdateListEntryPattern = new(@"^ (?<name>.+) (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching SMAPI's update line.</summary>
- private readonly Regex SmapiUpdatePattern = new Regex(@"^You can update SMAPI to (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex SmapiUpdatePattern = new(@"^You can update SMAPI to (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/*********
@@ -69,7 +71,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
}
// init log
- ParsedLog log = new ParsedLog
+ ParsedLog log = new()
{
IsValid = true,
RawText = logText,
@@ -77,8 +79,8 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
};
// parse log messages
- LogModInfo smapiMod = new LogModInfo { Name = "SMAPI", Author = "Pathoschild", Description = "", Loaded = true };
- LogModInfo gameMod = new LogModInfo { Name = "game", Author = "", Description = "", Loaded = true };
+ LogModInfo smapiMod = new() { Name = "SMAPI", Author = "Pathoschild", Description = "", Loaded = true };
+ LogModInfo gameMod = new() { Name = "game", Author = "", Description = "", Loaded = true };
IDictionary<string, List<LogModInfo>> mods = new Dictionary<string, List<LogModInfo>>();
bool inModList = false;
bool inContentPackList = false;
@@ -211,7 +213,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
{
Match match = this.ModPathPattern.Match(message.Text);
log.ModPath = match.Groups["path"].Value;
- int lastDelimiterPos = log.ModPath.LastIndexOfAny(new char[] { '/', '\\' });
+ int lastDelimiterPos = log.ModPath.LastIndexOfAny(new[] { '/', '\\' });
log.GamePath = lastDelimiterPos >= 0
? log.ModPath.Substring(0, lastDelimiterPos)
: log.ModPath;
@@ -288,8 +290,8 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
/// <exception cref="LogParseException">The log text can't be parsed successfully.</exception>
private IEnumerable<LogMessage> GetMessages(string logText)
{
- LogMessageBuilder builder = new LogMessageBuilder();
- using StringReader reader = new StringReader(logText);
+ LogMessageBuilder builder = new();
+ using StringReader reader = new(logText);
while (true)
{
// read line
diff --git a/src/SMAPI.Web/Framework/LogParsing/Models/LogMessage.cs b/src/SMAPI.Web/Framework/LogParsing/Models/LogMessage.cs
index 1e08be78..57d28755 100644
--- a/src/SMAPI.Web/Framework/LogParsing/Models/LogMessage.cs
+++ b/src/SMAPI.Web/Framework/LogParsing/Models/LogMessage.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.LogParsing.Models
{
/// <summary>A parsed log message.</summary>
diff --git a/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs b/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs
index 92bfe5c7..349312df 100644
--- a/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs
+++ b/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.LogParsing.Models
{
/// <summary>Metadata about a mod or content pack in the log.</summary>
diff --git a/src/SMAPI.Web/Framework/LogParsing/Models/ParsedLog.cs b/src/SMAPI.Web/Framework/LogParsing/Models/ParsedLog.cs
index 693a16ec..dae91d84 100644
--- a/src/SMAPI.Web/Framework/LogParsing/Models/ParsedLog.cs
+++ b/src/SMAPI.Web/Framework/LogParsing/Models/ParsedLog.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Web.Framework.LogParsing.Models
diff --git a/src/SMAPI.Web/Framework/ModInfoModel.cs b/src/SMAPI.Web/Framework/ModInfoModel.cs
index 7845b8c5..021d14fb 100644
--- a/src/SMAPI.Web/Framework/ModInfoModel.cs
+++ b/src/SMAPI.Web/Framework/ModInfoModel.cs
@@ -1,4 +1,4 @@
-using StardewModdingAPI.Web.Framework.Clients;
+#nullable disable
namespace StardewModdingAPI.Web.Framework
{
diff --git a/src/SMAPI.Web/Framework/ModSiteManager.cs b/src/SMAPI.Web/Framework/ModSiteManager.cs
index a2b92aa4..2d6755d8 100644
--- a/src/SMAPI.Web/Framework/ModSiteManager.cs
+++ b/src/SMAPI.Web/Framework/ModSiteManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI.Web/Framework/RedirectRules/RedirectHostsToUrlsRule.cs b/src/SMAPI.Web/Framework/RedirectRules/RedirectHostsToUrlsRule.cs
index d75ee791..fe601524 100644
--- a/src/SMAPI.Web/Framework/RedirectRules/RedirectHostsToUrlsRule.cs
+++ b/src/SMAPI.Web/Framework/RedirectRules/RedirectHostsToUrlsRule.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Net;
using Microsoft.AspNetCore.Rewrite;
@@ -37,8 +39,6 @@ namespace StardewModdingAPI.Web.Framework.RedirectRules
{
// get requested host
string host = context.HttpContext.Request.Host.Host;
- if (host == null)
- return null;
// get new host
host = this.Map(host);
diff --git a/src/SMAPI.Web/Framework/RedirectRules/RedirectMatchRule.cs b/src/SMAPI.Web/Framework/RedirectRules/RedirectMatchRule.cs
index 6e81c4ca..81a265c9 100644
--- a/src/SMAPI.Web/Framework/RedirectRules/RedirectMatchRule.cs
+++ b/src/SMAPI.Web/Framework/RedirectRules/RedirectMatchRule.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Net;
using Microsoft.AspNetCore.Http;
diff --git a/src/SMAPI.Web/Framework/RedirectRules/RedirectPathsToUrlsRule.cs b/src/SMAPI.Web/Framework/RedirectRules/RedirectPathsToUrlsRule.cs
index d9d44641..cb3e53ef 100644
--- a/src/SMAPI.Web/Framework/RedirectRules/RedirectPathsToUrlsRule.cs
+++ b/src/SMAPI.Web/Framework/RedirectRules/RedirectPathsToUrlsRule.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using System.Net;
diff --git a/src/SMAPI.Web/Framework/RedirectRules/RedirectToHttpsRule.cs b/src/SMAPI.Web/Framework/RedirectRules/RedirectToHttpsRule.cs
index 2a503ae3..dd7c836f 100644
--- a/src/SMAPI.Web/Framework/RedirectRules/RedirectToHttpsRule.cs
+++ b/src/SMAPI.Web/Framework/RedirectRules/RedirectToHttpsRule.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Net;
using Microsoft.AspNetCore.Http;
@@ -22,7 +24,7 @@ namespace StardewModdingAPI.Web.Framework.RedirectRules
/// <param name="except">Matches requests which should be ignored.</param>
public RedirectToHttpsRule(Func<HttpRequest, bool> except = null)
{
- this.Except = except ?? (req => false);
+ this.Except = except ?? (_ => false);
this.StatusCode = HttpStatusCode.RedirectKeepVerb;
}
diff --git a/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs b/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs
index dfc1fb47..2eca4845 100644
--- a/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs
+++ b/src/SMAPI.Web/Framework/Storage/IStorageProvider.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Threading.Tasks;
namespace StardewModdingAPI.Web.Framework.Storage
diff --git a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs
index c6f8bac1..0177e602 100644
--- a/src/SMAPI.Web/Framework/Storage/StorageProvider.cs
+++ b/src/SMAPI.Web/Framework/Storage/StorageProvider.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -103,7 +105,7 @@ namespace StardewModdingAPI.Web.Framework.Storage
// fetch file
Response<BlobDownloadInfo> response = await blob.DownloadAsync();
using BlobDownloadInfo result = response.Value;
- using StreamReader reader = new StreamReader(result.Content);
+ using StreamReader reader = new(result.Content);
DateTimeOffset expiry = result.Details.LastModified + TimeSpan.FromDays(this.ExpiryDays);
string content = this.GzipHelper.DecompressString(reader.ReadToEnd());
@@ -130,7 +132,7 @@ namespace StardewModdingAPI.Web.Framework.Storage
else
{
// get file
- FileInfo file = new FileInfo(this.GetDevFilePath(id));
+ FileInfo file = new(this.GetDevFilePath(id));
if (file.Exists && file.LastWriteTimeUtc.AddDays(this.ExpiryDays) < DateTime.UtcNow) // expired
file.Delete();
if (!file.Exists)
diff --git a/src/SMAPI.Web/Framework/Storage/StoredFileInfo.cs b/src/SMAPI.Web/Framework/Storage/StoredFileInfo.cs
index 30676c88..cd941c94 100644
--- a/src/SMAPI.Web/Framework/Storage/StoredFileInfo.cs
+++ b/src/SMAPI.Web/Framework/Storage/StoredFileInfo.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Web.Framework.Storage
diff --git a/src/SMAPI.Web/Framework/Storage/UploadResult.cs b/src/SMAPI.Web/Framework/Storage/UploadResult.cs
index 483c1769..b1eedd59 100644
--- a/src/SMAPI.Web/Framework/Storage/UploadResult.cs
+++ b/src/SMAPI.Web/Framework/Storage/UploadResult.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.Framework.Storage
{
/// <summary>The result of an attempt to upload a file.</summary>
diff --git a/src/SMAPI.Web/Framework/VersionConstraint.cs b/src/SMAPI.Web/Framework/VersionConstraint.cs
index f0c57c41..f230a95b 100644
--- a/src/SMAPI.Web/Framework/VersionConstraint.cs
+++ b/src/SMAPI.Web/Framework/VersionConstraint.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
diff --git a/src/SMAPI.Web/Program.cs b/src/SMAPI.Web/Program.cs
index 1fdd3185..5134791a 100644
--- a/src/SMAPI.Web/Program.cs
+++ b/src/SMAPI.Web/Program.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs
index d8561172..0199938d 100644
--- a/src/SMAPI.Web/Startup.cs
+++ b/src/SMAPI.Web/Startup.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Net;
using Hangfire;
@@ -81,7 +83,7 @@ namespace StardewModdingAPI.Web
// init Hangfire
services
- .AddHangfire((serv, config) =>
+ .AddHangfire((_, config) =>
{
config
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
diff --git a/src/SMAPI.Web/ViewModels/IndexModel.cs b/src/SMAPI.Web/ViewModels/IndexModel.cs
index d8d2d27f..2283acd9 100644
--- a/src/SMAPI.Web/ViewModels/IndexModel.cs
+++ b/src/SMAPI.Web/ViewModels/IndexModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.ViewModels
{
/// <summary>The view model for the index page.</summary>
diff --git a/src/SMAPI.Web/ViewModels/IndexVersionModel.cs b/src/SMAPI.Web/ViewModels/IndexVersionModel.cs
index 4f63b979..1f5d4ec0 100644
--- a/src/SMAPI.Web/ViewModels/IndexVersionModel.cs
+++ b/src/SMAPI.Web/ViewModels/IndexVersionModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.ViewModels
{
/// <summary>The fields for a SMAPI version.</summary>
diff --git a/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorErrorModel.cs b/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorErrorModel.cs
index 62b95501..3c63b730 100644
--- a/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorErrorModel.cs
+++ b/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorErrorModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Newtonsoft.Json.Schema;
namespace StardewModdingAPI.Web.ViewModels.JsonValidator
diff --git a/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorModel.cs b/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorModel.cs
index e659b389..2543807f 100644
--- a/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorModel.cs
+++ b/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorRequestModel.cs b/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorRequestModel.cs
index c8e851bf..43114d94 100644
--- a/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorRequestModel.cs
+++ b/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorRequestModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.ViewModels.JsonValidator
{
/// <summary>The view model for a JSON validation request.</summary>
diff --git a/src/SMAPI.Web/ViewModels/LogParserModel.cs b/src/SMAPI.Web/ViewModels/LogParserModel.cs
index 0b6d7722..c768a08c 100644
--- a/src/SMAPI.Web/ViewModels/LogParserModel.cs
+++ b/src/SMAPI.Web/ViewModels/LogParserModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,7 +16,7 @@ namespace StardewModdingAPI.Web.ViewModels
** Fields
*********/
/// <summary>A regex pattern matching characters to remove from a mod name to create the slug ID.</summary>
- private readonly Regex SlugInvalidCharPattern = new Regex("[^a-z0-9]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex SlugInvalidCharPattern = new("[^a-z0-9]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/*********
diff --git a/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs b/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs
index 85bf1e46..2af30cc3 100644
--- a/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs
+++ b/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
namespace StardewModdingAPI.Web.ViewModels
diff --git a/src/SMAPI.Web/ViewModels/ModLinkModel.cs b/src/SMAPI.Web/ViewModels/ModLinkModel.cs
index 97dd215c..3039702e 100644
--- a/src/SMAPI.Web/ViewModels/ModLinkModel.cs
+++ b/src/SMAPI.Web/ViewModels/ModLinkModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Web.ViewModels
{
/// <summary>Metadata about a link.</summary>
diff --git a/src/SMAPI.Web/ViewModels/ModListModel.cs b/src/SMAPI.Web/ViewModels/ModListModel.cs
index 6b8279c1..f0cf0c3a 100644
--- a/src/SMAPI.Web/ViewModels/ModListModel.cs
+++ b/src/SMAPI.Web/ViewModels/ModListModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI.Web/ViewModels/ModModel.cs b/src/SMAPI.Web/ViewModels/ModModel.cs
index 575d596a..d0d7373b 100644
--- a/src/SMAPI.Web/ViewModels/ModModel.cs
+++ b/src/SMAPI.Web/ViewModels/ModModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
diff --git a/src/SMAPI.Web/Views/Index/Index.cshtml b/src/SMAPI.Web/Views/Index/Index.cshtml
index 669cfd99..9841ca42 100644
--- a/src/SMAPI.Web/Views/Index/Index.cshtml
+++ b/src/SMAPI.Web/Views/Index/Index.cshtml
@@ -1,3 +1,7 @@
+@{
+ #nullable disable
+}
+
@using Microsoft.Extensions.Options
@using StardewModdingAPI.Web.Framework
@using StardewModdingAPI.Web.Framework.ConfigModels
diff --git a/src/SMAPI.Web/Views/Index/Privacy.cshtml b/src/SMAPI.Web/Views/Index/Privacy.cshtml
index fd78f908..1dc327d7 100644
--- a/src/SMAPI.Web/Views/Index/Privacy.cshtml
+++ b/src/SMAPI.Web/Views/Index/Privacy.cshtml
@@ -1,3 +1,7 @@
+@{
+ #nullable disable
+}
+
@using Microsoft.Extensions.Options
@using StardewModdingAPI.Web.Framework
@using StardewModdingAPI.Web.Framework.ConfigModels
diff --git a/src/SMAPI.Web/Views/JsonValidator/Index.cshtml b/src/SMAPI.Web/Views/JsonValidator/Index.cshtml
index 1db79857..5e38e4dc 100644
--- a/src/SMAPI.Web/Views/JsonValidator/Index.cshtml
+++ b/src/SMAPI.Web/Views/JsonValidator/Index.cshtml
@@ -1,3 +1,7 @@
+@{
+ #nullable disable
+}
+
@using Humanizer
@using StardewModdingAPI.Web.Framework
@using StardewModdingAPI.Web.ViewModels.JsonValidator
diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml
index b54867b1..c26ec230 100644
--- a/src/SMAPI.Web/Views/LogParser/Index.cshtml
+++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml
@@ -1,3 +1,7 @@
+@{
+ #nullable disable
+}
+
@using Humanizer
@using StardewModdingAPI.Toolkit.Utilities
@using StardewModdingAPI.Web.Framework
@@ -40,8 +44,8 @@
smapi.logParser({
logStarted: new Date(@this.ForJson(log?.Timestamp)),
showPopup: @this.ForJson(log == null),
- showMods: @this.ForJson(log?.Mods?.Select(p => Model.GetSlug(p.Name)).Distinct().ToDictionary(slug => slug, slug => true)),
- showSections: @this.ForJson(Enum.GetNames(typeof(LogSection)).ToDictionary(section => section, section => false)),
+ showMods: @this.ForJson(log?.Mods?.Select(p => Model.GetSlug(p.Name)).Distinct().ToDictionary(slug => slug, _ => true)),
+ showSections: @this.ForJson(Enum.GetNames(typeof(LogSection)).ToDictionary(section => section, _ => false)),
showLevels: @this.ForJson(defaultFilters),
enableFilters: @this.ForJson(!Model.ShowRaw),
screenIds: @this.ForJson(screenIds)
@@ -207,7 +211,7 @@ else if (log?.IsValid == true)
@if (mod.HasUpdate)
{
<a href="@mod.UpdateLink" target="_blank">
- @(mod.Version == null ? @mod.UpdateVersion : $"{mod.Version} → {mod.UpdateVersion}")
+ @(mod.Version == null ? mod.UpdateVersion : $"{mod.Version} → {mod.UpdateVersion}")
</a>
}
else
diff --git a/src/SMAPI.Web/Views/Mods/Index.cshtml b/src/SMAPI.Web/Views/Mods/Index.cshtml
index 416468e4..4b6400ad 100644
--- a/src/SMAPI.Web/Views/Mods/Index.cshtml
+++ b/src/SMAPI.Web/Views/Mods/Index.cshtml
@@ -1,3 +1,7 @@
+@{
+ #nullable disable
+}
+
@using Humanizer
@using Humanizer.Localisation
@using StardewModdingAPI.Web.Framework
diff --git a/src/SMAPI.Web/Views/Shared/_Layout.cshtml b/src/SMAPI.Web/Views/Shared/_Layout.cshtml
index 67dcd3b3..7c86a68c 100644
--- a/src/SMAPI.Web/Views/Shared/_Layout.cshtml
+++ b/src/SMAPI.Web/Views/Shared/_Layout.cshtml
@@ -1,3 +1,7 @@
+@{
+ #nullable disable
+}
+
@using Microsoft.Extensions.Options
@using StardewModdingAPI.Web.Framework
@using StardewModdingAPI.Web.Framework.ConfigModels
diff --git a/src/SMAPI.Web/Views/_ViewStart.cshtml b/src/SMAPI.Web/Views/_ViewStart.cshtml
index a5f10045..0dbac246 100644
--- a/src/SMAPI.Web/Views/_ViewStart.cshtml
+++ b/src/SMAPI.Web/Views/_ViewStart.cshtml
@@ -1,3 +1,7 @@
-@{
+@{
+ #nullable disable
+}
+
+@{
Layout = "_Layout";
}
diff --git a/src/SMAPI.Web/wwwroot/Content/css/file-upload.css b/src/SMAPI.Web/wwwroot/Content/css/file-upload.css
index ff170691..f29d46aa 100644
--- a/src/SMAPI.Web/wwwroot/Content/css/file-upload.css
+++ b/src/SMAPI.Web/wwwroot/Content/css/file-upload.css
@@ -11,7 +11,7 @@
border-radius: 5px;
border: 1px solid #000088;
outline: none;
- box-shadow: inset 0px 0px 1px 1px rgba(0, 0, 192, .2);
+ box-shadow: inset 0 0 1px 1px rgba(0, 0, 192, .2);
}
#submit {
diff --git a/src/SMAPI.sln.DotSettings b/src/SMAPI.sln.DotSettings
index b85185d5..5b35c615 100644
--- a/src/SMAPI.sln.DotSettings
+++ b/src/SMAPI.sln.DotSettings
@@ -73,5 +73,6 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=tilesheets/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tilesheet_0027s/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unloadable/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=versioning/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=virally/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary> \ No newline at end of file
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index 76f4ef87..2d9ab666 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -170,9 +172,6 @@ namespace StardewModdingAPI
/// <summary>The target game platform as a SMAPI toolkit constant.</summary>
internal static Platform Platform { get; } = (Platform)Constants.TargetPlatform;
- /// <summary>The language code for non-translated mod assets.</summary>
- internal static LocalizedContentManager.LanguageCode DefaultLanguage { get; } = LocalizedContentManager.LanguageCode.en;
-
/*********
** Internal methods
@@ -294,21 +293,6 @@ namespace StardewModdingAPI
return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences.ToArray(), targetAssemblies.ToArray());
}
- /// <summary>Get whether the game assembly was patched by Stardew64Installer.</summary>
- /// <param name="version">The version of Stardew64Installer which was applied to the game assembly, if any.</param>
- internal static bool IsPatchedByStardew64Installer(out ISemanticVersion version)
- {
- PropertyInfo property = typeof(Game1).GetProperty("Stardew64InstallerVersion");
- if (property == null)
- {
- version = null;
- return false;
- }
-
- version = new SemanticVersion((string)property.GetValue(null));
- return true;
- }
-
/*********
** Private methods
diff --git a/src/SMAPI/Context.cs b/src/SMAPI/Context.cs
index a745592c..e906375b 100644
--- a/src/SMAPI/Context.cs
+++ b/src/SMAPI/Context.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using StardewModdingAPI.Enums;
using StardewModdingAPI.Events;
@@ -14,16 +16,16 @@ namespace StardewModdingAPI
** Fields
*********/
/// <summary>Whether the player has loaded a save and the world has finished initializing.</summary>
- private static readonly PerScreen<bool> IsWorldReadyForScreen = new PerScreen<bool>();
+ private static readonly PerScreen<bool> IsWorldReadyForScreen = new();
/// <summary>The current stage in the game's loading process.</summary>
- private static readonly PerScreen<LoadStage> LoadStageForScreen = new PerScreen<LoadStage>();
+ private static readonly PerScreen<LoadStage> LoadStageForScreen = new();
/// <summary>Whether a player save has been loaded.</summary>
- internal static bool IsSaveLoaded => Game1.hasLoadedGame && !(Game1.activeClickableMenu is TitleMenu);
+ internal static bool IsSaveLoaded => Game1.hasLoadedGame && Game1.activeClickableMenu is not TitleMenu;
/// <summary>Whether the game is currently writing to the save file.</summary>
- internal static bool IsSaving => Game1.activeClickableMenu is SaveGameMenu || Game1.activeClickableMenu is ShippingMenu; // saving is performed by SaveGameMenu, but it's wrapped by ShippingMenu on days when the player shipping something
+ internal static bool IsSaving => Game1.activeClickableMenu is SaveGameMenu or ShippingMenu; // saving is performed by SaveGameMenu, but it's wrapped by ShippingMenu on days when the player shipping something
/// <summary>The active split-screen instance IDs.</summary>
internal static readonly ISet<int> ActiveScreenIds = new HashSet<int>();
@@ -39,7 +41,7 @@ namespace StardewModdingAPI
}
/// <summary>Whether the in-game world is completely unloaded and not in the process of being loaded. The world may still exist in memory at this point, but should be ignored.</summary>
- internal static bool IsWorldFullyUnloaded => Context.LoadStage == LoadStage.ReturningToTitle || Context.LoadStage == LoadStage.None;
+ internal static bool IsWorldFullyUnloaded => Context.LoadStage is LoadStage.ReturningToTitle or LoadStage.None;
/*********
@@ -86,7 +88,7 @@ namespace StardewModdingAPI
public static bool HasRemotePlayers => Context.IsMultiplayer && !Game1.hasLocalClientsOnly;
/// <summary>Whether the current player is the main player. This is always true in single-player, and true when hosting in multiplayer.</summary>
- public static bool IsMainPlayer => Game1.IsMasterGame && Context.ScreenId == 0 && !(TitleMenu.subMenu is FarmhandMenu);
+ public static bool IsMainPlayer => Game1.IsMasterGame && Context.ScreenId == 0 && TitleMenu.subMenu is not FarmhandMenu;
/*********
diff --git a/src/SMAPI/Events/AssetReadyEventArgs.cs b/src/SMAPI/Events/AssetReadyEventArgs.cs
index 2c308f18..19e5a9df 100644
--- a/src/SMAPI/Events/AssetReadyEventArgs.cs
+++ b/src/SMAPI/Events/AssetReadyEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/AssetRequestedEventArgs.cs b/src/SMAPI/Events/AssetRequestedEventArgs.cs
index c0cbd8fb..3c51c95d 100644
--- a/src/SMAPI/Events/AssetRequestedEventArgs.cs
+++ b/src/SMAPI/Events/AssetRequestedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
@@ -99,7 +101,7 @@ namespace StardewModdingAPI.Events
mod: this.Mod,
priority: priority,
onBehalfOf: null,
- _ => this.Mod.Mod.Helper.Content.Load<TAsset>(relativePath))
+ _ => this.Mod.Mod.Helper.ModContent.Load<TAsset>(relativePath))
);
}
diff --git a/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs b/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs
index 614cdf49..bd0df598 100644
--- a/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs
+++ b/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
diff --git a/src/SMAPI/Events/BuildingListChangedEventArgs.cs b/src/SMAPI/Events/BuildingListChangedEventArgs.cs
index 74f37710..ba9574cc 100644
--- a/src/SMAPI/Events/BuildingListChangedEventArgs.cs
+++ b/src/SMAPI/Events/BuildingListChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Events/ButtonPressedEventArgs.cs b/src/SMAPI/Events/ButtonPressedEventArgs.cs
index 1b30fd23..94684513 100644
--- a/src/SMAPI/Events/ButtonPressedEventArgs.cs
+++ b/src/SMAPI/Events/ButtonPressedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Framework.Input;
diff --git a/src/SMAPI/Events/ButtonReleasedEventArgs.cs b/src/SMAPI/Events/ButtonReleasedEventArgs.cs
index 40ec1cc1..6ff3727d 100644
--- a/src/SMAPI/Events/ButtonReleasedEventArgs.cs
+++ b/src/SMAPI/Events/ButtonReleasedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Framework.Input;
diff --git a/src/SMAPI/Events/ButtonsChangedEventArgs.cs b/src/SMAPI/Events/ButtonsChangedEventArgs.cs
index a5e87735..c63d34e6 100644
--- a/src/SMAPI/Events/ButtonsChangedEventArgs.cs
+++ b/src/SMAPI/Events/ButtonsChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs b/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs
index 4b4c4210..bc8ac0c0 100644
--- a/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs
+++ b/src/SMAPI/Events/ChestInventoryChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using StardewValley;
diff --git a/src/SMAPI/Events/CursorMovedEventArgs.cs b/src/SMAPI/Events/CursorMovedEventArgs.cs
index 43ff90ce..f3e7513b 100644
--- a/src/SMAPI/Events/CursorMovedEventArgs.cs
+++ b/src/SMAPI/Events/CursorMovedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/DebrisListChangedEventArgs.cs b/src/SMAPI/Events/DebrisListChangedEventArgs.cs
index 61b7590a..56b1f30a 100644
--- a/src/SMAPI/Events/DebrisListChangedEventArgs.cs
+++ b/src/SMAPI/Events/DebrisListChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Events/FurnitureListChangedEventArgs.cs b/src/SMAPI/Events/FurnitureListChangedEventArgs.cs
index 683f4620..cda1b6cc 100644
--- a/src/SMAPI/Events/FurnitureListChangedEventArgs.cs
+++ b/src/SMAPI/Events/FurnitureListChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Events/IContentEvents.cs b/src/SMAPI/Events/IContentEvents.cs
index d537db70..109f9753 100644
--- a/src/SMAPI/Events/IContentEvents.cs
+++ b/src/SMAPI/Events/IContentEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/IDisplayEvents.cs b/src/SMAPI/Events/IDisplayEvents.cs
index dbf8d90f..b8b89120 100644
--- a/src/SMAPI/Events/IDisplayEvents.cs
+++ b/src/SMAPI/Events/IDisplayEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewValley;
diff --git a/src/SMAPI/Events/IGameLoopEvents.cs b/src/SMAPI/Events/IGameLoopEvents.cs
index 6855737b..52bac3f8 100644
--- a/src/SMAPI/Events/IGameLoopEvents.cs
+++ b/src/SMAPI/Events/IGameLoopEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/IInputEvents.cs b/src/SMAPI/Events/IInputEvents.cs
index 081c40c0..01ceb224 100644
--- a/src/SMAPI/Events/IInputEvents.cs
+++ b/src/SMAPI/Events/IInputEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/IModEvents.cs b/src/SMAPI/Events/IModEvents.cs
index 2603961b..a1aacbce 100644
--- a/src/SMAPI/Events/IModEvents.cs
+++ b/src/SMAPI/Events/IModEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Events
{
/// <summary>Manages access to events raised by SMAPI.</summary>
diff --git a/src/SMAPI/Events/IMultiplayerEvents.cs b/src/SMAPI/Events/IMultiplayerEvents.cs
index af9b5f17..c50eaf04 100644
--- a/src/SMAPI/Events/IMultiplayerEvents.cs
+++ b/src/SMAPI/Events/IMultiplayerEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/IPlayerEvents.cs b/src/SMAPI/Events/IPlayerEvents.cs
index 81e17b1a..9d18bfad 100644
--- a/src/SMAPI/Events/IPlayerEvents.cs
+++ b/src/SMAPI/Events/IPlayerEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/ISpecialisedEvents.cs b/src/SMAPI/Events/ISpecialisedEvents.cs
index bf70956d..0ec5bf54 100644
--- a/src/SMAPI/Events/ISpecialisedEvents.cs
+++ b/src/SMAPI/Events/ISpecialisedEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/IWorldEvents.cs b/src/SMAPI/Events/IWorldEvents.cs
index c023e1f0..785dfa8f 100644
--- a/src/SMAPI/Events/IWorldEvents.cs
+++ b/src/SMAPI/Events/IWorldEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/InventoryChangedEventArgs.cs b/src/SMAPI/Events/InventoryChangedEventArgs.cs
index 40cd4128..58c0ff8f 100644
--- a/src/SMAPI/Events/InventoryChangedEventArgs.cs
+++ b/src/SMAPI/Events/InventoryChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using StardewValley;
diff --git a/src/SMAPI/Events/ItemStackSizeChange.cs b/src/SMAPI/Events/ItemStackSizeChange.cs
index 35369be2..5d0986aa 100644
--- a/src/SMAPI/Events/ItemStackSizeChange.cs
+++ b/src/SMAPI/Events/ItemStackSizeChange.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using StardewValley;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/LargeTerrainFeatureListChangedEventArgs.cs b/src/SMAPI/Events/LargeTerrainFeatureListChangedEventArgs.cs
index 59d79f0f..aedb0e46 100644
--- a/src/SMAPI/Events/LargeTerrainFeatureListChangedEventArgs.cs
+++ b/src/SMAPI/Events/LargeTerrainFeatureListChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Events/LevelChangedEventArgs.cs b/src/SMAPI/Events/LevelChangedEventArgs.cs
index c7303603..3beb9fd5 100644
--- a/src/SMAPI/Events/LevelChangedEventArgs.cs
+++ b/src/SMAPI/Events/LevelChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Enums;
using StardewValley;
diff --git a/src/SMAPI/Events/LocaleChangedEventArgs.cs b/src/SMAPI/Events/LocaleChangedEventArgs.cs
index 09d3f6e5..015e7ec8 100644
--- a/src/SMAPI/Events/LocaleChangedEventArgs.cs
+++ b/src/SMAPI/Events/LocaleChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using LanguageCode = StardewValley.LocalizedContentManager.LanguageCode;
diff --git a/src/SMAPI/Events/LocationListChangedEventArgs.cs b/src/SMAPI/Events/LocationListChangedEventArgs.cs
index 1ebb3e2d..055463dd 100644
--- a/src/SMAPI/Events/LocationListChangedEventArgs.cs
+++ b/src/SMAPI/Events/LocationListChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Events/MenuChangedEventArgs.cs b/src/SMAPI/Events/MenuChangedEventArgs.cs
index 977ba38b..362accec 100644
--- a/src/SMAPI/Events/MenuChangedEventArgs.cs
+++ b/src/SMAPI/Events/MenuChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewValley.Menus;
diff --git a/src/SMAPI/Events/ModMessageReceivedEventArgs.cs b/src/SMAPI/Events/ModMessageReceivedEventArgs.cs
index d75a7540..671bdf38 100644
--- a/src/SMAPI/Events/ModMessageReceivedEventArgs.cs
+++ b/src/SMAPI/Events/ModMessageReceivedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Framework.Networking;
using StardewModdingAPI.Toolkit.Serialization;
diff --git a/src/SMAPI/Events/NpcListChangedEventArgs.cs b/src/SMAPI/Events/NpcListChangedEventArgs.cs
index 3a37f1e7..fb6dc1c5 100644
--- a/src/SMAPI/Events/NpcListChangedEventArgs.cs
+++ b/src/SMAPI/Events/NpcListChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Events/ObjectListChangedEventArgs.cs b/src/SMAPI/Events/ObjectListChangedEventArgs.cs
index b21d2867..b1a636aa 100644
--- a/src/SMAPI/Events/ObjectListChangedEventArgs.cs
+++ b/src/SMAPI/Events/ObjectListChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Events/PeerConnectedEventArgs.cs b/src/SMAPI/Events/PeerConnectedEventArgs.cs
index bfaa2bd3..3d11a3b5 100644
--- a/src/SMAPI/Events/PeerConnectedEventArgs.cs
+++ b/src/SMAPI/Events/PeerConnectedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/PeerContextReceivedEventArgs.cs b/src/SMAPI/Events/PeerContextReceivedEventArgs.cs
index 151a295c..35a4b20d 100644
--- a/src/SMAPI/Events/PeerContextReceivedEventArgs.cs
+++ b/src/SMAPI/Events/PeerContextReceivedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/PeerDisconnectedEventArgs.cs b/src/SMAPI/Events/PeerDisconnectedEventArgs.cs
index 8517988a..0675b8fe 100644
--- a/src/SMAPI/Events/PeerDisconnectedEventArgs.cs
+++ b/src/SMAPI/Events/PeerDisconnectedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Events
diff --git a/src/SMAPI/Events/RenderedActiveMenuEventArgs.cs b/src/SMAPI/Events/RenderedActiveMenuEventArgs.cs
index efd4163b..3da0b4b4 100644
--- a/src/SMAPI/Events/RenderedActiveMenuEventArgs.cs
+++ b/src/SMAPI/Events/RenderedActiveMenuEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
diff --git a/src/SMAPI/Events/RenderedEventArgs.cs b/src/SMAPI/Events/RenderedEventArgs.cs
index d6341b19..e8beaaac 100644
--- a/src/SMAPI/Events/RenderedEventArgs.cs
+++ b/src/SMAPI/Events/RenderedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
diff --git a/src/SMAPI/Events/RenderedHudEventArgs.cs b/src/SMAPI/Events/RenderedHudEventArgs.cs
index 46e89013..b25ecd4c 100644
--- a/src/SMAPI/Events/RenderedHudEventArgs.cs
+++ b/src/SMAPI/Events/RenderedHudEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
diff --git a/src/SMAPI/Events/RenderedWorldEventArgs.cs b/src/SMAPI/Events/RenderedWorldEventArgs.cs
index 56145381..a99d6ab3 100644
--- a/src/SMAPI/Events/RenderedWorldEventArgs.cs
+++ b/src/SMAPI/Events/RenderedWorldEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
diff --git a/src/SMAPI/Events/RenderingActiveMenuEventArgs.cs b/src/SMAPI/Events/RenderingActiveMenuEventArgs.cs
index 103f56df..3e3f3258 100644
--- a/src/SMAPI/Events/RenderingActiveMenuEventArgs.cs
+++ b/src/SMAPI/Events/RenderingActiveMenuEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
diff --git a/src/SMAPI/Events/RenderingEventArgs.cs b/src/SMAPI/Events/RenderingEventArgs.cs
index 5acbef09..8f6b3557 100644
--- a/src/SMAPI/Events/RenderingEventArgs.cs
+++ b/src/SMAPI/Events/RenderingEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
diff --git a/src/SMAPI/Events/RenderingHudEventArgs.cs b/src/SMAPI/Events/RenderingHudEventArgs.cs
index 84c96ecd..87269b90 100644
--- a/src/SMAPI/Events/RenderingHudEventArgs.cs
+++ b/src/SMAPI/Events/RenderingHudEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
diff --git a/src/SMAPI/Events/RenderingWorldEventArgs.cs b/src/SMAPI/Events/RenderingWorldEventArgs.cs
index d0d44789..2fc9964f 100644
--- a/src/SMAPI/Events/RenderingWorldEventArgs.cs
+++ b/src/SMAPI/Events/RenderingWorldEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
diff --git a/src/SMAPI/Events/TerrainFeatureListChangedEventArgs.cs b/src/SMAPI/Events/TerrainFeatureListChangedEventArgs.cs
index cdf1e6dc..77a73102 100644
--- a/src/SMAPI/Events/TerrainFeatureListChangedEventArgs.cs
+++ b/src/SMAPI/Events/TerrainFeatureListChangedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Events/WarpedEventArgs.cs b/src/SMAPI/Events/WarpedEventArgs.cs
index 9afe4a4e..92a8ea77 100644
--- a/src/SMAPI/Events/WarpedEventArgs.cs
+++ b/src/SMAPI/Events/WarpedEventArgs.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewValley;
diff --git a/src/SMAPI/Framework/Command.cs b/src/SMAPI/Framework/Command.cs
index 8c9df47d..776ba238 100644
--- a/src/SMAPI/Framework/Command.cs
+++ b/src/SMAPI/Framework/Command.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Framework
diff --git a/src/SMAPI/Framework/CommandManager.cs b/src/SMAPI/Framework/CommandManager.cs
index ff540ad8..df798b0c 100644
--- a/src/SMAPI/Framework/CommandManager.cs
+++ b/src/SMAPI/Framework/CommandManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -65,7 +67,7 @@ namespace StardewModdingAPI.Framework
/// <exception cref="ArgumentException">There's already a command with that name.</exception>
public CommandManager Add(IInternalCommand command, IMonitor monitor)
{
- return this.Add(null, command.Name, command.Description, (name, args) => command.HandleCommand(args, monitor));
+ return this.Add(null, command.Name, command.Description, (_, args) => command.HandleCommand(args, monitor));
}
/// <summary>Get a command by its unique name.</summary>
@@ -166,7 +168,7 @@ namespace StardewModdingAPI.Framework
{
bool inQuotes = false;
IList<string> args = new List<string>();
- StringBuilder currentArg = new StringBuilder();
+ StringBuilder currentArg = new();
foreach (char ch in input)
{
if (ch == '"')
diff --git a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs
index 45b34556..fcfa928e 100644
--- a/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs
+++ b/src/SMAPI/Framework/Commands/HarmonySummaryCommand.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -30,7 +32,7 @@ namespace StardewModdingAPI.Framework.Commands
{
SearchResult[] matches = this.FilterPatches(args).OrderBy(p => p.MethodName).ToArray();
- StringBuilder result = new StringBuilder();
+ StringBuilder result = new();
if (!matches.Any())
result.AppendLine("No current patches match your search.");
diff --git a/src/SMAPI/Framework/Commands/HelpCommand.cs b/src/SMAPI/Framework/Commands/HelpCommand.cs
index baf3116e..eb6c74f5 100644
--- a/src/SMAPI/Framework/Commands/HelpCommand.cs
+++ b/src/SMAPI/Framework/Commands/HelpCommand.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Linq;
namespace StardewModdingAPI.Framework.Commands
diff --git a/src/SMAPI/Framework/Commands/IInternalCommand.cs b/src/SMAPI/Framework/Commands/IInternalCommand.cs
index abf105b6..32e3e9f1 100644
--- a/src/SMAPI/Framework/Commands/IInternalCommand.cs
+++ b/src/SMAPI/Framework/Commands/IInternalCommand.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework.Commands
{
/// <summary>A core SMAPI console command.</summary>
diff --git a/src/SMAPI/Framework/Commands/ReloadI18nCommand.cs b/src/SMAPI/Framework/Commands/ReloadI18nCommand.cs
index 12328bb6..2043b35e 100644
--- a/src/SMAPI/Framework/Commands/ReloadI18nCommand.cs
+++ b/src/SMAPI/Framework/Commands/ReloadI18nCommand.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Framework.Commands
diff --git a/src/SMAPI/Framework/Content/AssetData.cs b/src/SMAPI/Framework/Content/AssetData.cs
index 05be8a3b..be4a7ce6 100644
--- a/src/SMAPI/Framework/Content/AssetData.cs
+++ b/src/SMAPI/Framework/Content/AssetData.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Framework.Content
diff --git a/src/SMAPI/Framework/Content/AssetDataForDictionary.cs b/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
index 735b651c..06dbe259 100644
--- a/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
diff --git a/src/SMAPI/Framework/Content/AssetDataForImage.cs b/src/SMAPI/Framework/Content/AssetDataForImage.cs
index b0f1b5c7..8e59cd27 100644
--- a/src/SMAPI/Framework/Content/AssetDataForImage.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForImage.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
diff --git a/src/SMAPI/Framework/Content/AssetDataForMap.cs b/src/SMAPI/Framework/Content/AssetDataForMap.cs
index 26e4986e..0425e195 100644
--- a/src/SMAPI/Framework/Content/AssetDataForMap.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForMap.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -96,8 +98,8 @@ namespace StardewModdingAPI.Framework.Content
for (int y = 0; y < sourceArea.Value.Height; y++)
{
// calculate tile positions
- Point sourcePos = new Point(sourceArea.Value.X + x, sourceArea.Value.Y + y);
- Point targetPos = new Point(targetArea.Value.X + x, targetArea.Value.Y + y);
+ Point sourcePos = new(sourceArea.Value.X + x, sourceArea.Value.Y + y);
+ Point targetPos = new(targetArea.Value.X + x, targetArea.Value.Y + y);
// replace tiles on target-only layers
if (replaceAll)
@@ -147,7 +149,7 @@ namespace StardewModdingAPI.Framework.Content
{
switch (sourceTile)
{
- case StaticTile _:
+ case StaticTile:
return new StaticTile(targetLayer, targetSheet, sourceTile.BlendMode, sourceTile.TileIndex);
case AnimatedTile animatedTile:
diff --git a/src/SMAPI/Framework/Content/AssetDataForObject.cs b/src/SMAPI/Framework/Content/AssetDataForObject.cs
index d91873ae..4a6df64b 100644
--- a/src/SMAPI/Framework/Content/AssetDataForObject.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForObject.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
@@ -47,9 +49,9 @@ namespace StardewModdingAPI.Framework.Content
/// <inheritdoc />
public TData GetData<TData>()
{
- if (!(this.Data is TData))
+ if (this.Data is not TData data)
throw new InvalidCastException($"The content data of type {this.Data.GetType().FullName} can't be converted to the requested {typeof(TData).FullName}.");
- return (TData)this.Data;
+ return data;
}
}
}
diff --git a/src/SMAPI/Framework/Content/AssetEditOperation.cs b/src/SMAPI/Framework/Content/AssetEditOperation.cs
index 818209fa..1b7d0c93 100644
--- a/src/SMAPI/Framework/Content/AssetEditOperation.cs
+++ b/src/SMAPI/Framework/Content/AssetEditOperation.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/Content/AssetInfo.cs b/src/SMAPI/Framework/Content/AssetInfo.cs
index f5da5d69..51dcc61f 100644
--- a/src/SMAPI/Framework/Content/AssetInfo.cs
+++ b/src/SMAPI/Framework/Content/AssetInfo.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
diff --git a/src/SMAPI/Framework/Content/AssetInterceptorChange.cs b/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
index 981eed40..7f53db9b 100644
--- a/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
+++ b/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Reflection;
using StardewModdingAPI.Internal;
@@ -36,7 +38,7 @@ namespace StardewModdingAPI.Framework.Content
this.Instance = instance ?? throw new ArgumentNullException(nameof(instance));
this.WasAdded = wasAdded;
- if (!(instance is IAssetEditor) && !(instance is IAssetLoader))
+ if (instance is not (IAssetEditor or IAssetLoader))
throw new InvalidCastException($"The provided {nameof(instance)} value must be an {nameof(IAssetEditor)} or {nameof(IAssetLoader)} instance.");
}
diff --git a/src/SMAPI/Framework/Content/AssetLoadOperation.cs b/src/SMAPI/Framework/Content/AssetLoadOperation.cs
index b12958d6..73e60e24 100644
--- a/src/SMAPI/Framework/Content/AssetLoadOperation.cs
+++ b/src/SMAPI/Framework/Content/AssetLoadOperation.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs
index a1d37b0b..4d583d82 100644
--- a/src/SMAPI/Framework/Content/AssetName.cs
+++ b/src/SMAPI/Framework/Content/AssetName.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Toolkit.Utilities;
using StardewValley;
@@ -107,7 +109,7 @@ namespace StardewModdingAPI.Framework.Content
return this.BaseName.Equals(assetName?.BaseName, StringComparison.OrdinalIgnoreCase);
if (assetName is AssetName impl)
- return this.ComparableName == impl?.ComparableName;
+ return this.ComparableName == impl.ComparableName;
return this.Name.Equals(assetName?.Name, StringComparison.OrdinalIgnoreCase);
}
diff --git a/src/SMAPI/Framework/Content/AssetOperationGroup.cs b/src/SMAPI/Framework/Content/AssetOperationGroup.cs
index a2fcb722..e3c3f92c 100644
--- a/src/SMAPI/Framework/Content/AssetOperationGroup.cs
+++ b/src/SMAPI/Framework/Content/AssetOperationGroup.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework.Content
{
/// <summary>A set of operations to apply to an asset for a given <see cref="IAssetEditor"/> or <see cref="IAssetLoader"/> implementation.</summary>
diff --git a/src/SMAPI/Framework/Content/ContentCache.cs b/src/SMAPI/Framework/Content/ContentCache.cs
index 8e0c6228..4e620d28 100644
--- a/src/SMAPI/Framework/Content/ContentCache.cs
+++ b/src/SMAPI/Framework/Content/ContentCache.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
diff --git a/src/SMAPI/Framework/Content/TilesheetReference.cs b/src/SMAPI/Framework/Content/TilesheetReference.cs
index 0919bb44..cdc4bc62 100644
--- a/src/SMAPI/Framework/Content/TilesheetReference.cs
+++ b/src/SMAPI/Framework/Content/TilesheetReference.cs
@@ -1,4 +1,5 @@
-using System.Numerics;
+#nullable disable
+
using xTile.Dimensions;
namespace StardewModdingAPI.Framework.Content
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs
index 108257bf..81820b05 100644
--- a/src/SMAPI/Framework/ContentCoordinator.cs
+++ b/src/SMAPI/Framework/ContentCoordinator.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -15,7 +17,7 @@ using StardewModdingAPI.Framework.Utilities;
using StardewModdingAPI.Internal;
using StardewModdingAPI.Metadata;
using StardewModdingAPI.Toolkit.Serialization;
-using StardewModdingAPI.Toolkit.Utilities;
+using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.GameData;
using xTile;
@@ -80,6 +82,9 @@ namespace StardewModdingAPI.Framework
/// <summary>The cached asset load/edit operations to apply, indexed by asset name.</summary>
private readonly TickCacheDictionary<IAssetName, AssetOperationGroup[]> AssetOperationsByKey = new();
+ /// <summary>The previously created case-insensitive path caches by root path.</summary>
+ private readonly Dictionary<string, CaseInsensitivePathCache> CaseInsensitivePathCaches = new(StringComparer.OrdinalIgnoreCase);
+
/*********
** Accessors
@@ -91,9 +96,11 @@ namespace StardewModdingAPI.Framework
public LocalizedContentManager.LanguageCode Language => this.MainContentManager.Language;
/// <summary>Interceptors which provide the initial versions of matching assets.</summary>
+ [Obsolete]
public IList<ModLinked<IAssetLoader>> Loaders { get; } = new List<ModLinked<IAssetLoader>>();
/// <summary>Interceptors which edit matching assets after they're loaded.</summary>
+ [Obsolete]
public IList<ModLinked<IAssetEditor>> Editors { get; } = new List<ModLinked<IAssetEditor>>();
/// <summary>The absolute path to the <see cref="ContentManager.RootDirectory"/>.</summary>
@@ -156,7 +163,7 @@ namespace StardewModdingAPI.Framework
);
this.ContentManagers.Add(contentManagerForAssetPropagation);
this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
- this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations, this.ParseAssetName);
+ this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations, name => this.ParseAssetName(name, allowLocales: true));
this.LocaleCodes = new Lazy<Dictionary<string, LocalizedContentManager.LanguageCode>>(() => this.GetLocaleCodes(customLanguages: Enumerable.Empty<ModLanguage>()));
}
@@ -205,7 +212,8 @@ namespace StardewModdingAPI.Framework
reflection: this.Reflection,
jsonHelper: this.JsonHelper,
onDisposing: this.OnDisposing,
- aggressiveMemoryOptimizations: this.AggressiveMemoryOptimizations
+ aggressiveMemoryOptimizations: this.AggressiveMemoryOptimizations,
+ relativePathCache: this.GetCaseInsensitivePathCache(rootDirectory)
);
this.ContentManagers.Add(manager);
return manager;
@@ -269,11 +277,17 @@ namespace StardewModdingAPI.Framework
/// <summary>Parse a raw asset name.</summary>
/// <param name="rawName">The raw asset name to parse.</param>
+ /// <param name="allowLocales">Whether to parse locales in the <paramref name="rawName"/>. If this is false, any locale codes in the name are treated as if they were part of the base name (e.g. for mod files).</param>
/// <exception cref="ArgumentException">The <paramref name="rawName"/> is null or empty.</exception>
- public AssetName ParseAssetName(string rawName)
+ public AssetName ParseAssetName(string rawName, bool allowLocales)
{
return !string.IsNullOrWhiteSpace(rawName)
- ? AssetName.Parse(rawName, parseLocale: locale => this.LocaleCodes.Value.TryGetValue(locale, out LocalizedContentManager.LanguageCode langCode) ? langCode : null)
+ ? AssetName.Parse(
+ rawName: rawName,
+ parseLocale: allowLocales
+ ? locale => this.LocaleCodes.Value.TryGetValue(locale, out LocalizedContentManager.LanguageCode langCode) ? langCode : null
+ : _ => null
+ )
: throw new ArgumentException("The asset name can't be null or empty.", nameof(rawName));
}
@@ -303,7 +317,7 @@ namespace StardewModdingAPI.Framework
if (parts.Length != 3) // managed key prefix, mod id, relative path
return false;
contentManagerID = Path.Combine(parts[0], parts[1]);
- relativePath = this.ParseAssetName(parts[2]);
+ relativePath = this.ParseAssetName(parts[2], allowLocales: false);
return true;
}
@@ -357,7 +371,7 @@ namespace StardewModdingAPI.Framework
string locale = this.GetLocale();
return this.InvalidateCache((_, rawName, type) =>
{
- IAssetName assetName = this.ParseAssetName(rawName);
+ IAssetName assetName = this.ParseAssetName(rawName, allowLocales: true);
IAssetInfo info = new AssetInfo(locale, assetName, type, this.MainContentManager.AssertAndNormalizeAssetName);
return predicate(info);
}, dispose);
@@ -378,7 +392,7 @@ namespace StardewModdingAPI.Framework
{
foreach ((string key, object asset) in contentManager.InvalidateCache((key, type) => predicate(contentManager, key, type), dispose))
{
- AssetName assetName = this.ParseAssetName(key);
+ AssetName assetName = this.ParseAssetName(key, allowLocales: true);
if (!invalidatedAssets.ContainsKey(assetName))
invalidatedAssets[assetName] = asset.GetType();
}
@@ -394,7 +408,7 @@ namespace StardewModdingAPI.Framework
continue;
// get map path
- AssetName mapPath = this.ParseAssetName(this.MainContentManager.AssertAndNormalizeAssetName(location.mapPath.Value));
+ AssetName mapPath = this.ParseAssetName(this.MainContentManager.AssertAndNormalizeAssetName(location.mapPath.Value), allowLocales: true);
if (!invalidatedAssets.ContainsKey(mapPath) && predicate(this.MainContentManager, mapPath.Name, typeof(Map)))
invalidatedAssets[mapPath] = typeof(Map);
}
@@ -471,6 +485,18 @@ namespace StardewModdingAPI.Framework
});
}
+ /// <summary>Get a dictionary of relative paths within a root path, for case-insensitive file lookups.</summary>
+ /// <param name="rootPath">The root path to scan.</param>
+ public CaseInsensitivePathCache GetCaseInsensitivePathCache(string rootPath)
+ {
+ rootPath = PathUtilities.NormalizePath(rootPath);
+
+ if (!this.CaseInsensitivePathCaches.TryGetValue(rootPath, out CaseInsensitivePathCache cache))
+ this.CaseInsensitivePathCaches[rootPath] = cache = new CaseInsensitivePathCache(rootPath);
+
+ return cache;
+ }
+
/// <summary>Get the tilesheet ID order used by the unmodified version of a map asset.</summary>
/// <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)
@@ -584,6 +610,7 @@ namespace StardewModdingAPI.Framework
yield return group;
// legacy load operations
+#pragma warning disable CS0612, CS0618 // deprecated code
foreach (ModLinked<IAssetLoader> loader in this.Loaders)
{
// check if loader applies
@@ -631,6 +658,27 @@ namespace StardewModdingAPI.Framework
continue;
}
+ // HACK
+ //
+ // If two editors have the same priority, they're applied in registration order (so
+ // whichever was registered first is applied first). Mods often depend on this
+ // behavior, like Json Assets registering its interceptors before Content Patcher.
+ //
+ // Unfortunately the old & new content APIs have separate lists, so new-API
+ // interceptors always ran before old-API interceptors with the same priority,
+ // regardless of the registration order *between* APIs. Since the new API works in
+ // a fundamentally different way (i.e. loads/edits are defined on asset request
+ // instead of by registering a global 'editor' or 'loader' class), there's no way
+ // to track registration order between them.
+ //
+ // Until we drop the old content API in SMAPI 4.0.0, this sets the priority for
+ // specific legacy editors to maintain compatibility.
+ AssetEditPriority priority = editor.Data.GetType().FullName switch
+ {
+ "JsonAssets.Framework.ContentInjector1" => AssetEditPriority.Default - 1, // must be applied before Content Patcher
+ _ => AssetEditPriority.Default
+ };
+
// add operation
yield return new AssetOperationGroup(
mod: editor.Mod,
@@ -639,7 +687,7 @@ namespace StardewModdingAPI.Framework
{
new AssetEditOperation(
mod: editor.Mod,
- priority: AssetEditPriority.Default,
+ priority: priority,
onBehalfOf: null,
applyEdit: assetData => editor.Data.Edit<T>(
this.GetLegacyAssetData(assetData)
@@ -648,6 +696,7 @@ namespace StardewModdingAPI.Framework
}
);
}
+#pragma warning restore CS0612, CS0618
}
/// <summary>Get an asset info compatible with legacy <see cref="IAssetLoader"/> and <see cref="IAssetEditor"/> instances, which always expect the base name.</summary>
diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
index 2d921cc3..4594d235 100644
--- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -119,7 +121,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
public sealed override T Load<T>(string assetName, LanguageCode language)
{
assetName = this.PrenormalizeRawAssetName(assetName);
- IAssetName parsedName = this.Coordinator.ParseAssetName(assetName);
+ IAssetName parsedName = this.Coordinator.ParseAssetName(assetName, allowLocales: this.TryLocalizeKeys);
return this.LoadLocalized<T>(parsedName, language, useCache: true);
}
@@ -161,7 +163,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
// use cached key
string rawName = LocalizedContentManager.localizedAssetNames[assetName.Name];
if (assetName.Name != rawName)
- assetName = this.Coordinator.ParseAssetName(rawName);
+ assetName = this.Coordinator.ParseAssetName(rawName, allowLocales: this.TryLocalizeKeys);
return this.LoadExact<T>(assetName, useCache: useCache);
}
@@ -213,7 +215,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
IDictionary<string, object> removeAssets = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
this.Cache.Remove((key, asset) =>
{
- string baseAssetName = this.Coordinator.ParseAssetName(key).BaseName;
+ string baseAssetName = this.Coordinator.ParseAssetName(key, allowLocales: this.TryLocalizeKeys).BaseName;
// check if asset should be removed
bool remove = removeAssets.ContainsKey(baseAssetName);
@@ -308,7 +310,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
{
return useCache
? base.LoadBase<T>(assetName.Name)
- : base.ReadAsset<T>(assetName.Name, disposable => this.Disposables.Add(new WeakReference<IDisposable>(disposable)));
+ : this.ReadAsset<T>(assetName.Name, disposable => this.Disposables.Add(new WeakReference<IDisposable>(disposable)));
}
/// <summary>Add tracking data to an asset and add it to the cache.</summary>
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
index 3d37e32a..f4e1bda4 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -235,7 +237,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
mod.LogAsMod($"Mod incorrectly set asset '{info.Name}'{this.GetOnBehalfOfLabel(editor.OnBehalfOf)} to a null value; ignoring override.", LogLevel.Warn);
asset = GetNewData(prevAsset);
}
- else if (!(asset.Data is T))
+ else if (asset.Data is not T)
{
mod.LogAsMod($"Mod incorrectly set asset '{asset.Name}'{this.GetOnBehalfOfLabel(editor.OnBehalfOf)} to incompatible type '{asset.Data.GetType()}', expected '{typeof(T)}'; ignoring override.", LogLevel.Warn);
asset = GetNewData(prevAsset);
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
index 3f7188da..46d5d24e 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Globalization;
using Microsoft.Xna.Framework.Graphics;
diff --git a/src/SMAPI/Framework/ContentManagers/IContentManager.cs b/src/SMAPI/Framework/ContentManagers/IContentManager.cs
index 90095492..c8b2ae64 100644
--- a/src/SMAPI/Framework/ContentManagers/IContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/IContentManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Content;
@@ -36,6 +38,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <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);
diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
index 63b40d66..8051c296 100644
--- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Globalization;
using System.IO;
@@ -9,7 +11,7 @@ using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Framework.Exceptions;
using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Toolkit.Serialization;
-using StardewModdingAPI.Toolkit.Utilities;
+using StardewModdingAPI.Utilities;
using StardewValley;
using xTile;
using xTile.Format;
@@ -32,6 +34,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <summary>The game content manager used for map tilesheets not provided by the mod.</summary>
private readonly IContentManager GameContentManager;
+ /// <summary>A case-insensitive lookup of relative paths within the <see cref="ContentManager.RootDirectory"/>.</summary>
+ private readonly CaseInsensitivePathCache RelativePathCache;
+
/// <summary>If a map tilesheet's image source has no file extensions, the file extensions to check for in the local mod folder.</summary>
private readonly string[] LocalTilesheetExtensions = { ".png", ".xnb" };
@@ -52,10 +57,12 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
- public ModContentManager(string name, IContentManager gameContentManager, IServiceProvider serviceProvider, string modName, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action<BaseContentManager> onDisposing, bool aggressiveMemoryOptimizations)
+ /// <param name="relativePathCache">A case-insensitive lookup of relative paths within the <paramref name="rootDirectory"/>.</param>
+ public ModContentManager(string name, IContentManager gameContentManager, IServiceProvider serviceProvider, string modName, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action<BaseContentManager> onDisposing, bool aggressiveMemoryOptimizations, CaseInsensitivePathCache relativePathCache)
: base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: true, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations)
{
this.GameContentManager = gameContentManager;
+ this.RelativePathCache = relativePathCache;
this.JsonHelper = jsonHelper;
this.ModName = modName;
@@ -88,101 +95,38 @@ namespace StardewModdingAPI.Framework.ContentManagers
if (this.Coordinator.TryParseManagedAssetKey(assetName.Name, out string contentManagerID, out IAssetName relativePath))
{
if (contentManagerID != this.Name)
- throw new SContentLoadException($"Can't load managed asset key '{assetName}' through content manager '{this.Name}' for a different mod.");
+ throw this.GetLoadError(assetName, "can't load a different mod's managed asset key through this mod content manager.");
assetName = relativePath;
}
}
// get local asset
- SContentLoadException GetContentError(string reasonPhrase) => new($"Failed loading asset '{assetName}' from {this.Name}: {reasonPhrase}");
T asset;
try
{
// get file
FileInfo file = this.GetModFile(assetName.Name);
if (!file.Exists)
- throw GetContentError("the specified path doesn't exist.");
+ throw this.GetLoadError(assetName, "the specified path doesn't exist.");
// load content
- switch (file.Extension.ToLower())
+ asset = file.Extension.ToLower() switch
{
- // XNB file
- case ".xnb":
- {
- // the underlying content manager adds a .xnb extension implicitly, so
- // we need to strip it here to avoid trying to load a '.xnb.xnb' file.
- IAssetName loadName = this.Coordinator.ParseAssetName(assetName.Name[..^".xnb".Length]);
-
- // load asset
- asset = this.RawLoad<T>(loadName, useCache: false);
- if (asset is Map map)
- {
- map.assetPath = loadName.Name;
- this.FixTilesheetPaths(map, relativeMapPath: loadName.Name, fixEagerPathPrefixes: true);
- }
- }
- break;
-
- // unpacked Bitmap font
- case ".fnt":
- {
- string source = File.ReadAllText(file.FullName);
- asset = (T)(object)new XmlSource(source);
- }
- break;
-
- // unpacked data
- case ".json":
- {
- if (!this.JsonHelper.ReadJsonFileIfExists(file.FullName, out asset))
- throw GetContentError("the JSON file is invalid."); // should never happen since we check for file existence above
- }
- break;
-
- // unpacked image
- case ".png":
- {
- // validate
- if (typeof(T) != typeof(Texture2D))
- throw GetContentError($"can't read file with extension '{file.Extension}' as type '{typeof(T)}'; must be type '{typeof(Texture2D)}'.");
-
- // fetch & cache
- using FileStream stream = File.OpenRead(file.FullName);
-
- Texture2D texture = Texture2D.FromStream(Game1.graphics.GraphicsDevice, stream);
- texture = this.PremultiplyTransparency(texture);
- asset = (T)(object)texture;
- }
- break;
-
- // unpacked map
- case ".tbin":
- case ".tmx":
- {
- // validate
- if (typeof(T) != typeof(Map))
- throw GetContentError($"can't read file with extension '{file.Extension}' as type '{typeof(T)}'; must be type '{typeof(Map)}'.");
-
- // fetch & cache
- FormatManager formatManager = FormatManager.Instance;
- Map map = formatManager.LoadMap(file.FullName);
- map.assetPath = assetName.Name;
- this.FixTilesheetPaths(map, relativeMapPath: assetName.Name, fixEagerPathPrefixes: false);
- asset = (T)(object)map;
- }
- break;
-
- default:
- throw GetContentError($"unknown file extension '{file.Extension}'; must be one of '.fnt', '.json', '.png', '.tbin', or '.xnb'.");
- }
+ ".fnt" => this.LoadFont<T>(assetName, file),
+ ".json" => this.LoadDataFile<T>(assetName, file),
+ ".png" => this.LoadImageFile<T>(assetName, file),
+ ".tbin" or ".tmx" => this.LoadMapFile<T>(assetName, file),
+ ".xnb" => this.LoadXnbFile<T>(assetName),
+ _ => this.HandleUnknownFileType<T>(assetName, file)
+ };
}
- catch (Exception ex) when (!(ex is SContentLoadException))
+ catch (Exception ex) when (ex is not SContentLoadException)
{
- throw new SContentLoadException($"The content manager failed loading content asset '{assetName}' from {this.Name}.", ex);
+ throw this.GetLoadError(assetName, "an unexpected occurred.", ex);
}
// track & return asset
- this.TrackAsset(assetName, asset, useCache);
+ this.TrackAsset(assetName, asset, useCache: false);
return asset;
}
@@ -198,20 +142,125 @@ namespace StardewModdingAPI.Framework.ContentManagers
public IAssetName GetInternalAssetKey(string key)
{
FileInfo file = this.GetModFile(key);
- string relativePath = PathUtilities.GetRelativePath(this.RootDirectory, file.FullName);
+ string relativePath = Path.GetRelativePath(this.RootDirectory, file.FullName);
string internalKey = Path.Combine(this.Name, relativePath);
- return this.Coordinator.ParseAssetName(internalKey);
+ return this.Coordinator.ParseAssetName(internalKey, allowLocales: false);
}
/*********
** Private methods
*********/
+ /// <summary>Load an unpacked font file (<c>.fnt</c>).</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="file">The file to load.</param>
+ private T LoadFont<T>(IAssetName assetName, FileInfo file)
+ {
+ // validate
+ if (!typeof(T).IsAssignableFrom(typeof(XmlSource)))
+ throw this.GetLoadError(assetName, $"can't read file with extension '{file.Extension}' as type '{typeof(T)}'; must be type '{typeof(XmlSource)}'.");
+
+ // load
+ string source = File.ReadAllText(file.FullName);
+ return (T)(object)new XmlSource(source);
+ }
+
+ /// <summary>Load an unpacked data file (<c>.json</c>).</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="file">The file to load.</param>
+ private T LoadDataFile<T>(IAssetName assetName, FileInfo file)
+ {
+ if (!this.JsonHelper.ReadJsonFileIfExists(file.FullName, out T asset))
+ throw this.GetLoadError(assetName, "the JSON file is invalid."); // should never happen since we check for file existence before calling this method
+
+ return asset;
+ }
+
+ /// <summary>Load an unpacked image file (<c>.json</c>).</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="file">The file to load.</param>
+ private T LoadImageFile<T>(IAssetName assetName, FileInfo file)
+ {
+ // validate
+ if (typeof(T) != typeof(Texture2D))
+ throw this.GetLoadError(assetName, $"can't read file with extension '{file.Extension}' as type '{typeof(T)}'; must be type '{typeof(Texture2D)}'.");
+
+ // load
+ using FileStream stream = File.OpenRead(file.FullName);
+ Texture2D texture = Texture2D.FromStream(Game1.graphics.GraphicsDevice, stream);
+ texture = this.PremultiplyTransparency(texture);
+ return (T)(object)texture;
+ }
+
+ /// <summary>Load an unpacked image file (<c>.tbin</c> or <c>.tmx</c>).</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="file">The file to load.</param>
+ private T LoadMapFile<T>(IAssetName assetName, FileInfo file)
+ {
+ // validate
+ if (typeof(T) != typeof(Map))
+ throw this.GetLoadError(assetName, $"can't read file with extension '{file.Extension}' as type '{typeof(T)}'; must be type '{typeof(Map)}'.");
+
+ // load
+ FormatManager formatManager = FormatManager.Instance;
+ Map map = formatManager.LoadMap(file.FullName);
+ map.assetPath = assetName.Name;
+ this.FixTilesheetPaths(map, relativeMapPath: assetName.Name, fixEagerPathPrefixes: false);
+ return (T)(object)map;
+ }
+
+ /// <summary>Load a packed file (<c>.xnb</c>).</summary>
+ /// <typeparam name="T">The type of asset to load.</typeparam>
+ /// <param name="assetName">The asset name relative to the loader root directory.</param>
+ private T LoadXnbFile<T>(IAssetName assetName)
+ {
+ // the underlying content manager adds a .xnb extension implicitly, so
+ // we need to strip it here to avoid trying to load a '.xnb.xnb' file.
+ IAssetName loadName = assetName.Name.EndsWith(".xnb", StringComparison.OrdinalIgnoreCase)
+ ? this.Coordinator.ParseAssetName(assetName.Name[..^".xnb".Length], allowLocales: false)
+ : assetName;
+
+ // load asset
+ T asset = this.RawLoad<T>(loadName, useCache: false);
+ if (asset is Map map)
+ {
+ map.assetPath = loadName.Name;
+ this.FixTilesheetPaths(map, relativeMapPath: loadName.Name, fixEagerPathPrefixes: true);
+ }
+
+ return asset;
+ }
+
+ /// <summary>Handle a request to load a file type that isn't supported by SMAPI.</summary>
+ /// <typeparam name="T">The expected file type.</typeparam>
+ /// <param name="assetName">The asset name relative to the loader root directory.</param>
+ /// <param name="file">The file to load.</param>
+ private T HandleUnknownFileType<T>(IAssetName assetName, FileInfo file)
+ {
+ throw this.GetLoadError(assetName, $"unknown file extension '{file.Extension}'; must be one of '.fnt', '.json', '.png', '.tbin', '.tmx', or '.xnb'.");
+ }
+
+ /// <summary>Get an error which indicates that an asset couldn't be loaded.</summary>
+ /// <param name="assetName">The asset name that failed to load.</param>
+ /// <param name="reasonPhrase">The reason the file couldn't be loaded.</param>
+ /// <param name="exception">The underlying exception, if applicable.</param>
+ private SContentLoadException GetLoadError(IAssetName assetName, string reasonPhrase, Exception exception = null)
+ {
+ return new($"Failed loading asset '{assetName}' from {this.Name}: {reasonPhrase}", exception);
+ }
+
/// <summary>Get a file from the mod folder.</summary>
/// <param name="path">The asset path relative to the content folder.</param>
private FileInfo GetModFile(string path)
{
+ // map to case-insensitive path if needed
+ path = this.RelativePathCache.GetFilePath(path);
+
// try exact match
FileInfo file = new(Path.Combine(this.FullRootDirectory, path));
@@ -343,7 +392,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
}
// get from game assets
- IAssetName contentKey = this.Coordinator.ParseAssetName(this.GetContentKeyForTilesheetImageSource(relativePath));
+ IAssetName contentKey = this.Coordinator.ParseAssetName(this.GetContentKeyForTilesheetImageSource(relativePath), allowLocales: false);
try
{
this.GameContentManager.LoadLocalized<Texture2D>(contentKey, this.GameContentManager.Language, useCache: true); // no need to bypass cache here, since we're not storing the asset
diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs
index 3920354e..2d33a22e 100644
--- a/src/SMAPI/Framework/ContentPack.cs
+++ b/src/SMAPI/Framework/ContentPack.cs
@@ -1,9 +1,10 @@
+#nullable disable
+
using System;
-using System.Collections.Generic;
using System.IO;
using StardewModdingAPI.Framework.ModHelpers;
using StardewModdingAPI.Toolkit.Serialization;
-using StardewModdingAPI.Toolkit.Utilities;
+using StardewModdingAPI.Utilities;
namespace StardewModdingAPI.Framework
{
@@ -16,8 +17,8 @@ namespace StardewModdingAPI.Framework
/// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
private readonly JsonHelper JsonHelper;
- /// <summary>A cache of case-insensitive => exact relative paths within the content pack, for case-insensitive file lookups on Linux/macOS.</summary>
- private readonly IDictionary<string, string> RelativePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ /// <summary>A case-insensitive lookup of relative paths within the <see cref="DirectoryPath"/>.</summary>
+ private readonly CaseInsensitivePathCache RelativePathCache;
/*********
@@ -48,19 +49,15 @@ namespace StardewModdingAPI.Framework
/// <param name="content">Provides an API for loading content assets from the content pack's folder.</param>
/// <param name="translation">Provides translations stored in the content pack's <c>i18n</c> folder.</param>
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
- public ContentPack(string directoryPath, IManifest manifest, IModContentHelper content, TranslationHelper translation, JsonHelper jsonHelper)
+ /// <param name="relativePathCache">A case-insensitive lookup of relative paths within the <paramref name="directoryPath"/>.</param>
+ public ContentPack(string directoryPath, IManifest manifest, IModContentHelper content, TranslationHelper translation, JsonHelper jsonHelper, CaseInsensitivePathCache relativePathCache)
{
this.DirectoryPath = directoryPath;
this.Manifest = manifest;
this.ModContent = content;
this.TranslationImpl = translation;
this.JsonHelper = jsonHelper;
-
- foreach (string path in Directory.EnumerateFiles(this.DirectoryPath, "*", SearchOption.AllDirectories))
- {
- string relativePath = path.Substring(this.DirectoryPath.Length + 1);
- this.RelativePaths[relativePath] = relativePath;
- }
+ this.RelativePathCache = relativePathCache;
}
/// <inheritdoc />
@@ -90,8 +87,7 @@ namespace StardewModdingAPI.Framework
FileInfo file = this.GetFile(path, out path);
this.JsonHelper.WriteJsonFile(file.FullName, data);
- if (!this.RelativePaths.ContainsKey(path))
- this.RelativePaths[path] = path;
+ this.RelativePathCache.Add(path);
}
/// <inheritdoc />
@@ -112,18 +108,6 @@ namespace StardewModdingAPI.Framework
/*********
** Private methods
*********/
- /// <summary>Get the real relative path from a case-insensitive path.</summary>
- /// <param name="relativePath">The normalized relative path.</param>
- private string GetCaseInsensitiveRelativePath(string relativePath)
- {
- if (!PathUtilities.IsSafeRelativePath(relativePath))
- throw new InvalidOperationException($"You must call {nameof(IContentPack)} methods with a relative path.");
-
- return !string.IsNullOrWhiteSpace(relativePath) && this.RelativePaths.TryGetValue(relativePath, out string caseInsensitivePath)
- ? caseInsensitivePath
- : relativePath;
- }
-
/// <summary>Get the underlying file info.</summary>
/// <param name="relativePath">The normalized file path relative to the content pack directory.</param>
private FileInfo GetFile(string relativePath)
@@ -136,7 +120,11 @@ namespace StardewModdingAPI.Framework
/// <param name="actualRelativePath">The relative path after case-insensitive matching.</param>
private FileInfo GetFile(string relativePath, out string actualRelativePath)
{
- actualRelativePath = this.GetCaseInsensitiveRelativePath(relativePath);
+ if (!PathUtilities.IsSafeRelativePath(relativePath))
+ throw new InvalidOperationException($"You must call {nameof(IContentPack)} methods with a relative path.");
+
+ actualRelativePath = this.RelativePathCache.GetFilePath(relativePath);
+
return new FileInfo(Path.Combine(this.DirectoryPath, actualRelativePath));
}
}
diff --git a/src/SMAPI/Framework/CursorPosition.cs b/src/SMAPI/Framework/CursorPosition.cs
index 107481e7..8f36a554 100644
--- a/src/SMAPI/Framework/CursorPosition.cs
+++ b/src/SMAPI/Framework/CursorPosition.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Microsoft.Xna.Framework;
using StardewValley;
diff --git a/src/SMAPI/Framework/DeprecationManager.cs b/src/SMAPI/Framework/DeprecationManager.cs
index fc1b434b..fe1b623f 100644
--- a/src/SMAPI/Framework/DeprecationManager.cs
+++ b/src/SMAPI/Framework/DeprecationManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -11,7 +13,7 @@ namespace StardewModdingAPI.Framework
** Fields
*********/
/// <summary>The deprecations which have already been logged (as 'mod name::noun phrase::version').</summary>
- private readonly HashSet<string> LoggedDeprecations = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+ private readonly HashSet<string> LoggedDeprecations = new(StringComparer.OrdinalIgnoreCase);
/// <summary>Encapsulates monitoring and logging for a given module.</summary>
private readonly IMonitor Monitor;
diff --git a/src/SMAPI/Framework/DeprecationWarning.cs b/src/SMAPI/Framework/DeprecationWarning.cs
index 5201b06c..f155358b 100644
--- a/src/SMAPI/Framework/DeprecationWarning.cs
+++ b/src/SMAPI/Framework/DeprecationWarning.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework
{
/// <summary>A deprecation warning for a mod.</summary>
diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs
index 41540047..c977e73d 100644
--- a/src/SMAPI/Framework/Events/EventManager.cs
+++ b/src/SMAPI/Framework/Events/EventManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using StardewModdingAPI.Events;
namespace StardewModdingAPI.Framework.Events
diff --git a/src/SMAPI/Framework/Events/IManagedEvent.cs b/src/SMAPI/Framework/Events/IManagedEvent.cs
index e4e3ca08..57277576 100644
--- a/src/SMAPI/Framework/Events/IManagedEvent.cs
+++ b/src/SMAPI/Framework/Events/IManagedEvent.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework.Events
{
/// <summary>Metadata for an event raised by SMAPI.</summary>
diff --git a/src/SMAPI/Framework/Events/ManagedEvent.cs b/src/SMAPI/Framework/Events/ManagedEvent.cs
index 154ef659..8fa31165 100644
--- a/src/SMAPI/Framework/Events/ManagedEvent.cs
+++ b/src/SMAPI/Framework/Events/ManagedEvent.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -18,7 +20,7 @@ namespace StardewModdingAPI.Framework.Events
protected readonly ModRegistry ModRegistry;
/// <summary>The underlying event handlers.</summary>
- private readonly List<ManagedEventHandler<TEventArgs>> Handlers = new List<ManagedEventHandler<TEventArgs>>();
+ 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>>();
diff --git a/src/SMAPI/Framework/Events/ManagedEventHandler.cs b/src/SMAPI/Framework/Events/ManagedEventHandler.cs
index 28e88be0..f31bc04d 100644
--- a/src/SMAPI/Framework/Events/ManagedEventHandler.cs
+++ b/src/SMAPI/Framework/Events/ManagedEventHandler.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
@@ -42,7 +44,7 @@ namespace StardewModdingAPI.Framework.Events
/// <inheritdoc />
public int CompareTo(object obj)
{
- if (!(obj is ManagedEventHandler<TEventArgs> other))
+ if (obj is not ManagedEventHandler<TEventArgs> other)
throw new ArgumentException("Can't compare to an unrelated object type.");
int priorityCompare = -this.Priority.CompareTo(other.Priority); // higher value = sort first
diff --git a/src/SMAPI/Framework/Events/ModContentEvents.cs b/src/SMAPI/Framework/Events/ModContentEvents.cs
index beb96031..f198b793 100644
--- a/src/SMAPI/Framework/Events/ModContentEvents.cs
+++ b/src/SMAPI/Framework/Events/ModContentEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/Events/ModDisplayEvents.cs b/src/SMAPI/Framework/Events/ModDisplayEvents.cs
index 48f55324..b2110cce 100644
--- a/src/SMAPI/Framework/Events/ModDisplayEvents.cs
+++ b/src/SMAPI/Framework/Events/ModDisplayEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/Events/ModEvents.cs b/src/SMAPI/Framework/Events/ModEvents.cs
index 1fb3482c..e8f8885d 100644
--- a/src/SMAPI/Framework/Events/ModEvents.cs
+++ b/src/SMAPI/Framework/Events/ModEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using StardewModdingAPI.Events;
namespace StardewModdingAPI.Framework.Events
diff --git a/src/SMAPI/Framework/Events/ModEventsBase.cs b/src/SMAPI/Framework/Events/ModEventsBase.cs
index 77708fc1..295caa0d 100644
--- a/src/SMAPI/Framework/Events/ModEventsBase.cs
+++ b/src/SMAPI/Framework/Events/ModEventsBase.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework.Events
{
/// <summary>An internal base class for event API classes.</summary>
diff --git a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs
index 5f0db369..51803daf 100644
--- a/src/SMAPI/Framework/Events/ModGameLoopEvents.cs
+++ b/src/SMAPI/Framework/Events/ModGameLoopEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/Events/ModInputEvents.cs b/src/SMAPI/Framework/Events/ModInputEvents.cs
index 40edf806..6af79c59 100644
--- a/src/SMAPI/Framework/Events/ModInputEvents.cs
+++ b/src/SMAPI/Framework/Events/ModInputEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/Events/ModMultiplayerEvents.cs b/src/SMAPI/Framework/Events/ModMultiplayerEvents.cs
index b90f64fa..7d3ce510 100644
--- a/src/SMAPI/Framework/Events/ModMultiplayerEvents.cs
+++ b/src/SMAPI/Framework/Events/ModMultiplayerEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/Events/ModPlayerEvents.cs b/src/SMAPI/Framework/Events/ModPlayerEvents.cs
index b2d89e9a..dac8f05b 100644
--- a/src/SMAPI/Framework/Events/ModPlayerEvents.cs
+++ b/src/SMAPI/Framework/Events/ModPlayerEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs b/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs
index 7980208b..4b438034 100644
--- a/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs
+++ b/src/SMAPI/Framework/Events/ModSpecialisedEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/Events/ModWorldEvents.cs b/src/SMAPI/Framework/Events/ModWorldEvents.cs
index a7b7d799..614945c7 100644
--- a/src/SMAPI/Framework/Events/ModWorldEvents.cs
+++ b/src/SMAPI/Framework/Events/ModWorldEvents.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/Exceptions/SAssemblyLoadFailedException.cs b/src/SMAPI/Framework/Exceptions/SAssemblyLoadFailedException.cs
index ec9279f1..4e03a687 100644
--- a/src/SMAPI/Framework/Exceptions/SAssemblyLoadFailedException.cs
+++ b/src/SMAPI/Framework/Exceptions/SAssemblyLoadFailedException.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Framework.Exceptions
diff --git a/src/SMAPI/Framework/Exceptions/SContentLoadException.cs b/src/SMAPI/Framework/Exceptions/SContentLoadException.cs
index 85d85e3d..c21a6b0e 100644
--- a/src/SMAPI/Framework/Exceptions/SContentLoadException.cs
+++ b/src/SMAPI/Framework/Exceptions/SContentLoadException.cs
@@ -1,4 +1,6 @@
-using System;
+#nullable disable
+
+using System;
using Microsoft.Xna.Framework.Content;
namespace StardewModdingAPI.Framework.Exceptions
diff --git a/src/SMAPI/Framework/GameVersion.cs b/src/SMAPI/Framework/GameVersion.cs
index b69c6757..aa91d8f3 100644
--- a/src/SMAPI/Framework/GameVersion.cs
+++ b/src/SMAPI/Framework/GameVersion.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index cb876ee4..800b198a 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using StardewModdingAPI.Framework.ModHelpers;
diff --git a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs
index ad254828..21168b7a 100644
--- a/src/SMAPI/Framework/Input/GamePadStateBuilder.cs
+++ b/src/SMAPI/Framework/Input/GamePadStateBuilder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
diff --git a/src/SMAPI/Framework/Input/IInputStateBuilder.cs b/src/SMAPI/Framework/Input/IInputStateBuilder.cs
index 28d62439..3fb62686 100644
--- a/src/SMAPI/Framework/Input/IInputStateBuilder.cs
+++ b/src/SMAPI/Framework/Input/IInputStateBuilder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
namespace StardewModdingAPI.Framework.Input
diff --git a/src/SMAPI/Framework/Input/KeyboardStateBuilder.cs b/src/SMAPI/Framework/Input/KeyboardStateBuilder.cs
index eadb7a8a..81ca0ebb 100644
--- a/src/SMAPI/Framework/Input/KeyboardStateBuilder.cs
+++ b/src/SMAPI/Framework/Input/KeyboardStateBuilder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework.Input;
@@ -14,7 +16,7 @@ namespace StardewModdingAPI.Framework.Input
private KeyboardState? State;
/// <summary>The pressed buttons.</summary>
- private readonly HashSet<Keys> PressedButtons = new HashSet<Keys>();
+ private readonly HashSet<Keys> PressedButtons = new();
/*********
diff --git a/src/SMAPI/Framework/Input/MouseStateBuilder.cs b/src/SMAPI/Framework/Input/MouseStateBuilder.cs
index c2a0891b..85b38d32 100644
--- a/src/SMAPI/Framework/Input/MouseStateBuilder.cs
+++ b/src/SMAPI/Framework/Input/MouseStateBuilder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using Microsoft.Xna.Framework.Input;
diff --git a/src/SMAPI/Framework/Input/SInputState.cs b/src/SMAPI/Framework/Input/SInputState.cs
index a8d1f371..37b3c8ef 100644
--- a/src/SMAPI/Framework/Input/SInputState.cs
+++ b/src/SMAPI/Framework/Input/SInputState.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -21,10 +23,10 @@ namespace StardewModdingAPI.Framework.Input
private Vector2? LastPlayerTile;
/// <summary>The buttons to press until the game next handles input.</summary>
- private readonly HashSet<SButton> CustomPressedKeys = new HashSet<SButton>();
+ private readonly HashSet<SButton> CustomPressedKeys = new();
/// <summary>The buttons to consider released until the actual button is released.</summary>
- private readonly HashSet<SButton> CustomReleasedKeys = new HashSet<SButton>();
+ private readonly HashSet<SButton> CustomReleasedKeys = new();
/// <summary>Whether there are new overrides in <see cref="CustomPressedKeys"/> or <see cref="CustomReleasedKeys"/> that haven't been applied to the previous state.</summary>
private bool HasNewOverrides;
@@ -72,8 +74,8 @@ namespace StardewModdingAPI.Framework.Input
var controller = new GamePadStateBuilder(base.GetGamePadState());
var keyboard = new KeyboardStateBuilder(base.GetKeyboardState());
var mouse = new MouseStateBuilder(base.GetMouseState());
- Vector2 cursorAbsolutePos = new Vector2((mouse.X * zoomMultiplier) + Game1.viewport.X, (mouse.Y * zoomMultiplier) + Game1.viewport.Y);
- Vector2? playerTilePos = Context.IsPlayerFree ? Game1.player.getTileLocation() : (Vector2?)null;
+ Vector2 cursorAbsolutePos = new((mouse.X * zoomMultiplier) + Game1.viewport.X, (mouse.Y * zoomMultiplier) + Game1.viewport.Y);
+ Vector2? playerTilePos = Context.IsPlayerFree ? Game1.player.getTileLocation() : null;
HashSet<SButton> reallyDown = new HashSet<SButton>(this.GetPressedButtons(keyboard, mouse, controller));
// apply overrides
@@ -203,8 +205,8 @@ namespace StardewModdingAPI.Framework.Input
/// <param name="zoomMultiplier">The multiplier applied to pixel coordinates to adjust them for pixel zoom.</param>
private CursorPosition GetCursorPosition(MouseState mouseState, Vector2 absolutePixels, float zoomMultiplier)
{
- Vector2 screenPixels = new Vector2(mouseState.X * zoomMultiplier, mouseState.Y * zoomMultiplier);
- Vector2 tile = new Vector2((int)((Game1.viewport.X + screenPixels.X) / Game1.tileSize), (int)((Game1.viewport.Y + screenPixels.Y) / Game1.tileSize));
+ Vector2 screenPixels = new(mouseState.X * zoomMultiplier, mouseState.Y * zoomMultiplier);
+ Vector2 tile = new((int)((Game1.viewport.X + screenPixels.X) / Game1.tileSize), (int)((Game1.viewport.Y + screenPixels.Y) / Game1.tileSize));
Vector2 grabTile = (Game1.mouseCursorTransparency > 0 && Utility.tileWithinRadiusOfPlayer((int)tile.X, (int)tile.Y, 1, Game1.player)) // derived from Game1.pressActionButton
? tile
: Game1.player.GetGrabTile();
@@ -234,7 +236,7 @@ namespace StardewModdingAPI.Framework.Input
isDown: pressed.Contains(button)
);
- if (button == SButton.MouseLeft || button == SButton.MouseMiddle || button == SButton.MouseRight || button == SButton.MouseX1 || button == SButton.MouseX2)
+ if (button is SButton.MouseLeft or SButton.MouseMiddle or SButton.MouseRight or SButton.MouseX1 or SButton.MouseX2)
mouseOverrides[button] = newState;
else if (button.TryGetKeyboard(out Keys _))
keyboardOverrides[button] = newState;
diff --git a/src/SMAPI/Framework/InternalExtensions.cs b/src/SMAPI/Framework/InternalExtensions.cs
index fe10b045..a1d87487 100644
--- a/src/SMAPI/Framework/InternalExtensions.cs
+++ b/src/SMAPI/Framework/InternalExtensions.cs
@@ -1,7 +1,8 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Reflection;
using System.Threading;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Framework.Events;
diff --git a/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs b/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs
index bad69a2a..a0957b90 100644
--- a/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs
+++ b/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.IO;
using System.Text;
diff --git a/src/SMAPI/Framework/Logging/LogFileManager.cs b/src/SMAPI/Framework/Logging/LogFileManager.cs
index 6ab2bdfb..0b6f9ad2 100644
--- a/src/SMAPI/Framework/Logging/LogFileManager.cs
+++ b/src/SMAPI/Framework/Logging/LogFileManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.IO;
diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs
index a8a8b6ee..dab7f554 100644
--- a/src/SMAPI/Framework/Logging/LogManager.cs
+++ b/src/SMAPI/Framework/Logging/LogManager.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/SMAPI/Framework/ModHelpers/BaseHelper.cs b/src/SMAPI/Framework/ModHelpers/BaseHelper.cs
index 5a3d4bed..1cd1a6b3 100644
--- a/src/SMAPI/Framework/ModHelpers/BaseHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/BaseHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework.ModHelpers
{
/// <summary>The common base class for mod helpers.</summary>
diff --git a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
index 69382009..c2b5092e 100644
--- a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Framework.ModHelpers
diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
index b0064532..e72e397e 100644
--- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -107,7 +109,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
public T Load<T>(string key, ContentSource source = ContentSource.ModFolder)
{
- IAssetName assetName = this.ContentCore.ParseAssetName(key);
+ IAssetName assetName = this.ContentCore.ParseAssetName(key, allowLocales: source == ContentSource.GameContent);
try
{
@@ -124,7 +126,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
throw new SContentLoadException($"{this.ModName} failed loading content asset '{key}' from {source}: unknown content source '{source}'.");
}
}
- catch (Exception ex) when (!(ex is SContentLoadException))
+ catch (Exception ex) when (ex is not SContentLoadException)
{
throw new SContentLoadException($"{this.ModName} failed loading content asset '{key}' from {source}.", ex);
}
@@ -157,21 +159,21 @@ namespace StardewModdingAPI.Framework.ModHelpers
public bool InvalidateCache(string key)
{
string actualKey = this.GetActualAssetKey(key, ContentSource.GameContent);
- this.Monitor.Log($"Requested cache invalidation for '{actualKey}'.", LogLevel.Trace);
+ this.Monitor.Log($"Requested cache invalidation for '{actualKey}'.");
return this.ContentCore.InvalidateCache(asset => asset.Name.IsEquivalentTo(actualKey)).Any();
}
/// <inheritdoc />
public bool InvalidateCache<T>()
{
- this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible.", LogLevel.Trace);
- return this.ContentCore.InvalidateCache((contentManager, key, type) => typeof(T).IsAssignableFrom(type)).Any();
+ 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();
}
/// <inheritdoc />
public bool InvalidateCache(Func<IAssetInfo, bool> predicate)
{
- this.Monitor.Log("Requested cache invalidation for all assets matching a predicate.", LogLevel.Trace);
+ this.Monitor.Log("Requested cache invalidation for all assets matching a predicate.");
return this.ContentCore.InvalidateCache(predicate).Any();
}
@@ -183,7 +185,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
assetName ??= $"temp/{Guid.NewGuid():N}";
- return new AssetDataForObject(this.CurrentLocale, this.ContentCore.ParseAssetName(assetName), data, this.NormalizeAssetName);
+ return new AssetDataForObject(this.CurrentLocale, this.ContentCore.ParseAssetName(assetName, allowLocales: true/* no way to know if it's a game or mod asset here*/), data, this.NormalizeAssetName);
}
diff --git a/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs
index d39abc7d..336214e2 100644
--- a/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/src/SMAPI/Framework/ModHelpers/DataHelper.cs b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
index 4cbfd73f..86a34ee8 100644
--- a/src/SMAPI/Framework/ModHelpers/DataHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs
index 42a4de20..956bac7f 100644
--- a/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Linq;
using StardewModdingAPI.Framework.Content;
@@ -58,13 +60,13 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
public IAssetName ParseAssetName(string rawName)
{
- return this.ContentCore.ParseAssetName(rawName);
+ return this.ContentCore.ParseAssetName(rawName, allowLocales: true);
}
/// <inheritdoc />
public T Load<T>(string key)
{
- IAssetName assetName = this.ContentCore.ParseAssetName(key);
+ IAssetName assetName = this.ContentCore.ParseAssetName(key, allowLocales: true);
return this.Load<T>(assetName);
}
@@ -73,7 +75,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
try
{
- return this.GameContentManager.LoadLocalized<T>(assetName, this.CurrentLocaleConstant, useCache: false);
+ return this.GameContentManager.LoadLocalized<T>(assetName, this.CurrentLocaleConstant, useCache: true);
}
catch (Exception ex) when (ex is not SContentLoadException)
{
@@ -117,7 +119,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
assetName ??= $"temp/{Guid.NewGuid():N}";
- return new AssetDataForObject(this.CurrentLocale, this.ContentCore.ParseAssetName(assetName), data, key => this.ParseAssetName(key).Name);
+ return new AssetDataForObject(this.CurrentLocale, this.ContentCore.ParseAssetName(assetName, allowLocales: true), data, key => this.ParseAssetName(key).Name);
}
/// <summary>Get the underlying game content manager.</summary>
diff --git a/src/SMAPI/Framework/ModHelpers/InputHelper.cs b/src/SMAPI/Framework/ModHelpers/InputHelper.cs
index 88caf4c3..29f80d87 100644
--- a/src/SMAPI/Framework/ModHelpers/InputHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/InputHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Framework.Input;
using StardewModdingAPI.Utilities;
diff --git a/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs
index 45899dd7..90064354 100644
--- a/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs
@@ -1,7 +1,11 @@
+#nullable disable
+
using System;
+using Microsoft.Xna.Framework.Content;
using StardewModdingAPI.Framework.Content;
using StardewModdingAPI.Framework.ContentManagers;
using StardewModdingAPI.Framework.Exceptions;
+using StardewModdingAPI.Utilities;
namespace StardewModdingAPI.Framework.ModHelpers
{
@@ -20,6 +24,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <summary>The friendly mod name for use in errors.</summary>
private readonly string ModName;
+ /// <summary>A case-insensitive lookup of relative paths within the <see cref="ContentManager.RootDirectory"/>.</summary>
+ private readonly CaseInsensitivePathCache RelativePathCache;
+
/*********
** Public methods
@@ -30,7 +37,8 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="modID">The unique ID of the relevant mod.</param>
/// <param name="modName">The friendly mod name for use in errors.</param>
/// <param name="gameContentManager">The game content manager used for map tilesheets not provided by the mod.</param>
- public ModContentHelper(ContentCoordinator contentCore, string modFolderPath, string modID, string modName, IContentManager gameContentManager)
+ /// <param name="relativePathCache">A case-insensitive lookup of relative paths within the <paramref name="relativePathCache"/>.</param>
+ public ModContentHelper(ContentCoordinator contentCore, string modFolderPath, string modID, string modName, IContentManager gameContentManager, CaseInsensitivePathCache relativePathCache)
: base(modID)
{
string managedAssetPrefix = contentCore.GetManagedAssetPrefix(modID);
@@ -38,12 +46,15 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.ContentCore = contentCore;
this.ModContentManager = contentCore.CreateModContentManager(managedAssetPrefix, modName, modFolderPath, gameContentManager);
this.ModName = modName;
+ this.RelativePathCache = relativePathCache;
}
/// <inheritdoc />
public T Load<T>(string relativePath)
{
- IAssetName assetName = this.ContentCore.ParseAssetName(relativePath);
+ relativePath = this.RelativePathCache.GetAssetName(relativePath);
+
+ IAssetName assetName = this.ContentCore.ParseAssetName(relativePath, allowLocales: false);
try
{
@@ -58,6 +69,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
public IAssetName GetInternalAssetName(string relativePath)
{
+ relativePath = this.RelativePathCache.GetAssetName(relativePath);
return this.ModContentManager.GetInternalAssetKey(relativePath);
}
@@ -67,9 +79,11 @@ namespace StardewModdingAPI.Framework.ModHelpers
if (data == null)
throw new ArgumentNullException(nameof(data), "Can't get a patch helper for a null value.");
- relativePath ??= $"temp/{Guid.NewGuid():N}";
+ relativePath = relativePath != null
+ ? this.RelativePathCache.GetAssetName(relativePath)
+ : $"temp/{Guid.NewGuid():N}";
- return new AssetDataForObject(this.ContentCore.GetLocale(), this.ContentCore.ParseAssetName(relativePath), data, key => this.ContentCore.ParseAssetName(key).Name);
+ return new AssetDataForObject(this.ContentCore.GetLocale(), this.ContentCore.ParseAssetName(relativePath, allowLocales: false), data, key => this.ContentCore.ParseAssetName(key, allowLocales: false).Name);
}
}
}
diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
index d28faacc..3cfe52bf 100644
--- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.IO;
using StardewModdingAPI.Events;
@@ -13,7 +15,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
*********/
/// <summary>The backing field for <see cref="Content"/>.</summary>
[Obsolete]
- private readonly IContentHelper ContentImpl;
+ private readonly ContentHelper ContentImpl;
/*********
@@ -93,7 +95,13 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="translationHelper">An API for reading translations stored in the mod's <c>i18n</c> folder.</param>
/// <exception cref="ArgumentNullException">An argument is null or empty.</exception>
/// <exception cref="InvalidOperationException">The <paramref name="modDirectory"/> path does not exist on disk.</exception>
- public ModHelper(string modID, string modDirectory, Func<SInputState> currentInputState, IModEvents events, IContentHelper contentHelper, IGameContentHelper gameContentHelper, IModContentHelper modContentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper)
+ public ModHelper(
+ string modID, string modDirectory, Func<SInputState> currentInputState, IModEvents events,
+#pragma warning disable CS0612 // deprecated code
+ ContentHelper contentHelper,
+#pragma warning restore CS0612
+ IGameContentHelper gameContentHelper, IModContentHelper modContentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper
+ )
: base(modID)
{
// validate directory
@@ -104,7 +112,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
// initialize
this.DirectoryPath = modDirectory;
+#pragma warning disable CS0612 // deprecated code
this.ContentImpl = contentHelper ?? throw new ArgumentNullException(nameof(contentHelper));
+#pragma warning restore CS0612
this.GameContent = gameContentHelper ?? throw new ArgumentNullException(nameof(gameContentHelper));
this.ModContent = modContentHelper ?? throw new ArgumentNullException(nameof(modContentHelper));
this.ContentPacks = contentPackHelper ?? throw new ArgumentNullException(nameof(contentPackHelper));
@@ -118,6 +128,13 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.Events = events;
}
+ /// <summary>Get the underlying instance for <see cref="IContentHelper"/>.</summary>
+ [Obsolete]
+ public ContentHelper GetLegacyContentHelper()
+ {
+ return this.ContentImpl;
+ }
+
/****
** Mod config file
****/
diff --git a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
index 95eb03f3..e277e6fa 100644
--- a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using StardewModdingAPI.Framework.Reflection;
@@ -69,7 +71,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
// get raw API
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}.", LogLevel.Trace);
+ this.Monitor.Log($"Accessed mod-provided API for {mod.DisplayName}.");
return mod?.Api;
}
diff --git a/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs b/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
index a7ce8692..96b074e2 100644
--- a/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using StardewModdingAPI.Framework.Networking;
using StardewValley;
diff --git a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
index 5a4ea742..24cbd01c 100644
--- a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Reflection;
using StardewModdingAPI.Framework.Reflection;
diff --git a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs
index 869664fe..37345a76 100644
--- a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using StardewValley;
diff --git a/src/SMAPI/Framework/ModLinked.cs b/src/SMAPI/Framework/ModLinked.cs
index 8cfe6f5f..5a3e38ca 100644
--- a/src/SMAPI/Framework/ModLinked.cs
+++ b/src/SMAPI/Framework/ModLinked.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework
{
/// <summary>A generic tuple which links something to a mod.</summary>
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs b/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
index 8e2f5ef3..1d4ddf72 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using Mono.Cecil;
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoadStatus.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoadStatus.cs
index 11be19fc..d2d5d83b 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyLoadStatus.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyLoadStatus.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework.ModLoading
{
/// <summary>Indicates the result of an assembly load.</summary>
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
index cb5fa2ae..070ee803 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -36,13 +38,13 @@ namespace StardewModdingAPI.Framework.ModLoading
private readonly AssemblyDefinitionResolver AssemblyDefinitionResolver;
/// <summary>Provides assembly symbol readers for Mono.Cecil.</summary>
- private readonly SymbolReaderProvider SymbolReaderProvider = new SymbolReaderProvider();
+ private readonly SymbolReaderProvider SymbolReaderProvider = new();
/// <summary>Provides assembly symbol writers for Mono.Cecil.</summary>
- private readonly SymbolWriterProvider SymbolWriterProvider = new SymbolWriterProvider();
+ private readonly SymbolWriterProvider SymbolWriterProvider = new();
/// <summary>The objects to dispose as part of this instance.</summary>
- private readonly HashSet<IDisposable> Disposables = new HashSet<IDisposable>();
+ private readonly HashSet<IDisposable> Disposables = new();
/// <summary>Whether to rewrite mods for compatibility.</summary>
private readonly bool RewriteMods;
@@ -138,11 +140,11 @@ namespace StardewModdingAPI.Framework.ModLoading
if (changed)
{
if (!oneAssembly)
- this.Monitor.Log($" Loading {assembly.File.Name} (rewritten)...", LogLevel.Trace);
+ this.Monitor.Log($" Loading {assembly.File.Name} (rewritten)...");
// load assembly
- using MemoryStream outAssemblyStream = new MemoryStream();
- using MemoryStream outSymbolStream = new MemoryStream();
+ using MemoryStream outAssemblyStream = new();
+ using MemoryStream outSymbolStream = new();
assembly.Definition.Write(outAssemblyStream, new WriterParameters { WriteSymbols = true, SymbolStream = outSymbolStream, SymbolWriterProvider = this.SymbolWriterProvider });
byte[] bytes = outAssemblyStream.ToArray();
lastAssembly = Assembly.Load(bytes, outSymbolStream.ToArray());
@@ -150,7 +152,7 @@ namespace StardewModdingAPI.Framework.ModLoading
else
{
if (!oneAssembly)
- this.Monitor.Log($" Loading {assembly.File.Name}...", LogLevel.Trace);
+ this.Monitor.Log($" Loading {assembly.File.Name}...");
lastAssembly = Assembly.UnsafeLoadFrom(assembly.File.FullName);
}
@@ -241,7 +243,7 @@ namespace StardewModdingAPI.Framework.ModLoading
try
{
// read assembly with symbols
- FileInfo symbolsFile = new FileInfo(Path.Combine(Path.GetDirectoryName(file.FullName)!, Path.GetFileNameWithoutExtension(file.FullName)) + ".pdb");
+ FileInfo symbolsFile = new(Path.Combine(Path.GetDirectoryName(file.FullName)!, Path.GetFileNameWithoutExtension(file.FullName)) + ".pdb");
if (symbolsFile.Exists)
this.SymbolReaderProvider.TryAddSymbolData(file.Name, () => this.TrackForDisposal(symbolsFile.OpenRead()));
assembly = this.TrackForDisposal(AssemblyDefinition.ReadAssembly(readStream, new ReaderParameters(ReadingMode.Immediate) { AssemblyResolver = assemblyResolver, InMemory = true, ReadSymbols = true, SymbolReaderProvider = this.SymbolReaderProvider }));
@@ -266,7 +268,7 @@ namespace StardewModdingAPI.Framework.ModLoading
// yield referenced assemblies
foreach (AssemblyNameReference dependency in assembly.MainModule.AssemblyReferences)
{
- FileInfo dependencyFile = new FileInfo(Path.Combine(file.Directory.FullName, $"{dependency.Name}.dll"));
+ FileInfo dependencyFile = new(Path.Combine(file.Directory.FullName, $"{dependency.Name}.dll"));
foreach (AssemblyParseResult result in this.GetReferencedLocalAssemblies(dependencyFile, visitedAssemblyNames, assemblyResolver))
yield return result;
}
@@ -333,7 +335,7 @@ namespace StardewModdingAPI.Framework.ModLoading
// find or rewrite code
IInstructionHandler[] handlers = new InstructionMetadata().GetHandlers(this.ParanoidMode, platformChanged, this.RewriteMods).ToArray();
- RecursiveRewriter rewriter = new RecursiveRewriter(
+ RecursiveRewriter rewriter = new(
module: module,
rewriteModule: curModule =>
{
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyParseResult.cs b/src/SMAPI/Framework/ModLoading/AssemblyParseResult.cs
index b56a776c..56bd5a8b 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyParseResult.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyParseResult.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.IO;
using Mono.Cecil;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
index 124951a5..7c94beb7 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
index 68415123..96b4098a 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
index d2340f01..7d3c1fd7 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
index 99344848..b2f2e193 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.Framework.ModLoading.Framework;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
index 8c1cae2b..81f90498 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMemberWithUnexpectedTypeFinder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
index d305daf4..001d1986 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/ReferenceToMissingMemberFinder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
index 24ab2eca..4c589ed8 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/TypeAssemblyFinder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Mono.Cecil;
using StardewModdingAPI.Framework.ModLoading.Framework;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
index 260a8df8..04a5b970 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using Mono.Cecil;
diff --git a/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs b/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs
index d5d1b38e..bea786cd 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/BaseInstructionHandler.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using Mono.Cecil;
diff --git a/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs b/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
index 4f14a579..09ff78f7 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/RecursiveRewriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs b/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
index d7cb2471..8f47fbdd 100644
--- a/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
+++ b/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Linq;
using System.Reflection;
diff --git a/src/SMAPI/Framework/ModLoading/IInstructionHandler.cs b/src/SMAPI/Framework/ModLoading/IInstructionHandler.cs
index d41732f8..126504e3 100644
--- a/src/SMAPI/Framework/ModLoading/IInstructionHandler.cs
+++ b/src/SMAPI/Framework/ModLoading/IInstructionHandler.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using Mono.Cecil;
diff --git a/src/SMAPI/Framework/ModLoading/IncompatibleInstructionException.cs b/src/SMAPI/Framework/ModLoading/IncompatibleInstructionException.cs
index 1f9add30..29406f2a 100644
--- a/src/SMAPI/Framework/ModLoading/IncompatibleInstructionException.cs
+++ b/src/SMAPI/Framework/ModLoading/IncompatibleInstructionException.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Framework.ModLoading
diff --git a/src/SMAPI/Framework/ModLoading/InvalidModStateException.cs b/src/SMAPI/Framework/ModLoading/InvalidModStateException.cs
index 075e237a..9dca9bc4 100644
--- a/src/SMAPI/Framework/ModLoading/InvalidModStateException.cs
+++ b/src/SMAPI/Framework/ModLoading/InvalidModStateException.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Framework.ModLoading
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index 9e6bc61f..0e698bfd 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index 4b05d1e5..2842c11a 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -32,11 +34,8 @@ namespace StardewModdingAPI.Framework.ModLoading
ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest);
// apply defaults
- if (manifest != null && dataRecord != null)
- {
- if (dataRecord.UpdateKey != null)
- manifest.UpdateKeys = new[] { dataRecord.UpdateKey };
- }
+ if (manifest != null && dataRecord?.UpdateKey is not null)
+ manifest.OverrideUpdateKeys(dataRecord.UpdateKey);
// build metadata
bool shouldIgnore = folder.Type == ModType.Ignored;
@@ -240,7 +239,7 @@ namespace StardewModdingAPI.Framework.ModLoading
// initialize metadata
mods = mods.ToArray();
var sortedMods = new Stack<IModMetadata>();
- var states = mods.ToDictionary(mod => mod, mod => ModDependencyStatus.Queued);
+ var states = mods.ToDictionary(mod => mod, _ => ModDependencyStatus.Queued);
// handle failed mods
foreach (IModMetadata mod in mods.Where(m => m.Status == ModMetadataStatus.Failed))
@@ -401,7 +400,7 @@ namespace StardewModdingAPI.Framework.ModLoading
{
foreach (string modRootPath in Directory.GetDirectories(rootPath))
{
- DirectoryInfo directory = new DirectoryInfo(modRootPath);
+ DirectoryInfo directory = new(modRootPath);
// if a folder only contains another folder, check the inner folder instead
while (!directory.GetFiles().Any() && directory.GetDirectories().Length == 1)
diff --git a/src/SMAPI/Framework/ModLoading/PlatformAssemblyMap.cs b/src/SMAPI/Framework/ModLoading/PlatformAssemblyMap.cs
index d4366294..0898f095 100644
--- a/src/SMAPI/Framework/ModLoading/PlatformAssemblyMap.cs
+++ b/src/SMAPI/Framework/ModLoading/PlatformAssemblyMap.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs
index be2a1c58..c05005b8 100644
--- a/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs
+++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/AccessToolsFacade.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs
index 135bd218..fea8c100 100644
--- a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs
+++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyInstanceFacade.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs
index 5162dda4..93124591 100644
--- a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs
+++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs
index 5f68f8d9..20a30f8f 100644
--- a/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs
+++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Diagnostics.CodeAnalysis;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/ArchitectureAssemblyRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/ArchitectureAssemblyRewriter.cs
index cc830216..4985d72a 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/ArchitectureAssemblyRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/ArchitectureAssemblyRewriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Mono.Cecil;
using StardewModdingAPI.Framework.ModLoading.Framework;
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs
index 857a2230..806fca62 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Reflection;
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/HarmonyRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/HarmonyRewriter.cs
index 922d4bc4..92397c58 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/HarmonyRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/HarmonyRewriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using HarmonyLib;
using Mono.Cecil;
@@ -34,7 +36,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
public override bool Handle(ModuleDefinition module, TypeReference type, Action<TypeReference> replaceWith)
{
// detect Harmony
- if (!(type.Scope is AssemblyNameReference scope) || scope.Name != "0Harmony")
+ if (type.Scope is not AssemblyNameReference scope || scope.Name != "0Harmony")
return false;
// rewrite Harmony 1.x type to Harmony 2.0 type
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs
index 57f1dd17..fc06e779 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicFieldRewriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
@@ -37,7 +39,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
// skip if not broken
FieldDefinition fieldDefinition = fieldRef.Resolve();
- if (fieldDefinition != null && !fieldDefinition.HasConstant)
+ if (fieldDefinition?.HasConstant == false)
return false;
// rewrite if possible
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs
index 89de437e..4860072c 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/HeuristicMethodRewriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs
index 9933e2ca..00daf337 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Linq;
using Mono.Cecil;
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs
index ad5cb96f..bdc4c4f3 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Mono.Cecil;
using StardewModdingAPI.Framework.ModLoading.Framework;
diff --git a/src/SMAPI/Framework/ModLoading/Symbols/SymbolReader.cs b/src/SMAPI/Framework/ModLoading/Symbols/SymbolReader.cs
index 2171895d..55b7e0c8 100644
--- a/src/SMAPI/Framework/ModLoading/Symbols/SymbolReader.cs
+++ b/src/SMAPI/Framework/ModLoading/Symbols/SymbolReader.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.IO;
using Mono.Cecil;
using Mono.Cecil.Cil;
diff --git a/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs b/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs
index 44074337..4af7c1e7 100644
--- a/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs
+++ b/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -16,7 +18,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Symbols
private readonly ISymbolReaderProvider BaseProvider = new DefaultSymbolReaderProvider(throwIfNoSymbol: false);
/// <summary>The symbol data loaded by absolute assembly path.</summary>
- private readonly Dictionary<string, Stream> SymbolsByAssemblyPath = new Dictionary<string, Stream>(StringComparer.OrdinalIgnoreCase);
+ private readonly Dictionary<string, Stream> SymbolsByAssemblyPath = new(StringComparer.OrdinalIgnoreCase);
/*********
diff --git a/src/SMAPI/Framework/ModLoading/Symbols/SymbolWriterProvider.cs b/src/SMAPI/Framework/ModLoading/Symbols/SymbolWriterProvider.cs
index 8f7e05d1..c2ac4cd6 100644
--- a/src/SMAPI/Framework/ModLoading/Symbols/SymbolWriterProvider.cs
+++ b/src/SMAPI/Framework/ModLoading/Symbols/SymbolWriterProvider.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.IO;
using Mono.Cecil;
using Mono.Cecil.Cil;
diff --git a/src/SMAPI/Framework/ModLoading/TypeReferenceComparer.cs b/src/SMAPI/Framework/ModLoading/TypeReferenceComparer.cs
index a4ac54e2..248c29fc 100644
--- a/src/SMAPI/Framework/ModLoading/TypeReferenceComparer.cs
+++ b/src/SMAPI/Framework/ModLoading/TypeReferenceComparer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Framework/ModRegistry.cs b/src/SMAPI/Framework/ModRegistry.cs
index ef389337..cae38637 100644
--- a/src/SMAPI/Framework/ModRegistry.cs
+++ b/src/SMAPI/Framework/ModRegistry.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -13,7 +15,7 @@ namespace StardewModdingAPI.Framework
** Fields
*********/
/// <summary>The registered mod data.</summary>
- private readonly List<IModMetadata> Mods = new List<IModMetadata>();
+ private readonly List<IModMetadata> Mods = new();
/// <summary>An assembly full name => mod lookup.</summary>
private readonly IDictionary<string, IModMetadata> ModNamesByAssembly = new Dictionary<string, IModMetadata>();
@@ -94,10 +96,8 @@ namespace StardewModdingAPI.Framework
public IModMetadata GetFromStack()
{
// get stack frames
- StackTrace stack = new StackTrace();
+ StackTrace stack = new();
StackFrame[] frames = stack.GetFrames();
- if (frames == null)
- return null;
// search stack for a source assembly
foreach (StackFrame frame in frames)
diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs
index 9174aea6..e74d73b5 100644
--- a/src/SMAPI/Framework/Models/SConfig.cs
+++ b/src/SMAPI/Framework/Models/SConfig.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -26,7 +28,7 @@ namespace StardewModdingAPI.Framework.Models
};
/// <summary>The default values for <see cref="SuppressUpdateChecks"/>, to log changes if different.</summary>
- private static readonly HashSet<string> DefaultSuppressUpdateChecks = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ private static readonly HashSet<string> DefaultSuppressUpdateChecks = new(StringComparer.OrdinalIgnoreCase)
{
"SMAPI.ConsoleCommands",
"SMAPI.ErrorHandler",
@@ -52,9 +54,6 @@ namespace StardewModdingAPI.Framework.Models
/// <summary>SMAPI's GitHub project name, used to perform update checks.</summary>
public string GitHubProjectName { get; set; }
- /// <summary>Stardew64Installer's GitHub project name, used to perform update checks.</summary>
- public string Stardew64InstallerGitHubProjectName { get; set; }
-
/// <summary>The base URL for SMAPI's web API, used to perform update checks.</summary>
public string WebApiBaseUrl { get; set; }
diff --git a/src/SMAPI/Framework/Monitor.cs b/src/SMAPI/Framework/Monitor.cs
index ab76e7c0..de145d1d 100644
--- a/src/SMAPI/Framework/Monitor.cs
+++ b/src/SMAPI/Framework/Monitor.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -92,7 +94,7 @@ namespace StardewModdingAPI.Framework
public void VerboseLog(string message)
{
if (this.IsVerbose)
- this.Log(message, LogLevel.Trace);
+ this.Log(message);
}
/// <summary>Write a newline to the console and log file.</summary>
diff --git a/src/SMAPI/Framework/Networking/ModMessageModel.cs b/src/SMAPI/Framework/Networking/ModMessageModel.cs
index 4f694f9c..4e7d01eb 100644
--- a/src/SMAPI/Framework/Networking/ModMessageModel.cs
+++ b/src/SMAPI/Framework/Networking/ModMessageModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Linq;
using Newtonsoft.Json.Linq;
diff --git a/src/SMAPI/Framework/Networking/MultiplayerPeer.cs b/src/SMAPI/Framework/Networking/MultiplayerPeer.cs
index 3923700f..8ee5c309 100644
--- a/src/SMAPI/Framework/Networking/MultiplayerPeer.cs
+++ b/src/SMAPI/Framework/Networking/MultiplayerPeer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Framework/Networking/MultiplayerPeerMod.cs b/src/SMAPI/Framework/Networking/MultiplayerPeerMod.cs
index 8087dc7e..6fdb9e54 100644
--- a/src/SMAPI/Framework/Networking/MultiplayerPeerMod.cs
+++ b/src/SMAPI/Framework/Networking/MultiplayerPeerMod.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework.Networking
{
internal class MultiplayerPeerMod : IMultiplayerPeerMod
diff --git a/src/SMAPI/Framework/Networking/RemoteContextModModel.cs b/src/SMAPI/Framework/Networking/RemoteContextModModel.cs
index 9795d971..0383576c 100644
--- a/src/SMAPI/Framework/Networking/RemoteContextModModel.cs
+++ b/src/SMAPI/Framework/Networking/RemoteContextModModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework.Networking
{
/// <summary>Metadata about an installed mod exchanged with connected computers.</summary>
diff --git a/src/SMAPI/Framework/Networking/RemoteContextModel.cs b/src/SMAPI/Framework/Networking/RemoteContextModel.cs
index 7befb151..37fafa67 100644
--- a/src/SMAPI/Framework/Networking/RemoteContextModel.cs
+++ b/src/SMAPI/Framework/Networking/RemoteContextModel.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework.Networking
{
/// <summary>Metadata about the game, SMAPI, and installed mods exchanged with connected computers.</summary>
diff --git a/src/SMAPI/Framework/Networking/SGalaxyNetClient.cs b/src/SMAPI/Framework/Networking/SGalaxyNetClient.cs
index 01095c66..8e19b4a7 100644
--- a/src/SMAPI/Framework/Networking/SGalaxyNetClient.cs
+++ b/src/SMAPI/Framework/Networking/SGalaxyNetClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Galaxy.Api;
using StardewValley.Network;
diff --git a/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs b/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs
index ac9cf313..07a004a2 100644
--- a/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs
+++ b/src/SMAPI/Framework/Networking/SGalaxyNetServer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -45,8 +47,8 @@ namespace StardewModdingAPI.Framework.Networking
[SuppressMessage("ReSharper", "AccessToDisposedClosure", Justification = "The callback is invoked synchronously.")]
protected override void onReceiveMessage(GalaxyID peer, Stream messageStream)
{
- using IncomingMessage message = new IncomingMessage();
- using BinaryReader reader = new BinaryReader(messageStream);
+ using IncomingMessage message = new();
+ using BinaryReader reader = new(messageStream);
message.Read(reader);
ulong peerID = peer.ToUint64(); // note: GalaxyID instances get reused, so need to store the underlying ID instead
@@ -57,7 +59,7 @@ namespace StardewModdingAPI.Framework.Networking
else if (message.MessageType == StardewValley.Multiplayer.playerIntroduction)
{
NetFarmerRoot farmer = this.Multiplayer.readFarmer(message.Reader);
- GalaxyID capturedPeer = new GalaxyID(peerID);
+ GalaxyID capturedPeer = new(peerID);
this.gameServer.checkFarmhandRequest(Convert.ToString(peerID), this.getConnectionId(peer), farmer, msg => this.sendMessage(capturedPeer, msg), () => this.peers[farmer.Value.UniqueMultiplayerID] = capturedPeer.ToUint64());
}
});
diff --git a/src/SMAPI/Framework/Networking/SLidgrenClient.cs b/src/SMAPI/Framework/Networking/SLidgrenClient.cs
index 39876744..ecf18cbd 100644
--- a/src/SMAPI/Framework/Networking/SLidgrenClient.cs
+++ b/src/SMAPI/Framework/Networking/SLidgrenClient.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewValley.Network;
diff --git a/src/SMAPI/Framework/Networking/SLidgrenServer.cs b/src/SMAPI/Framework/Networking/SLidgrenServer.cs
index 05c8b872..c0b247c8 100644
--- a/src/SMAPI/Framework/Networking/SLidgrenServer.cs
+++ b/src/SMAPI/Framework/Networking/SLidgrenServer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -44,9 +46,9 @@ namespace StardewModdingAPI.Framework.Networking
{
// add hook to call multiplayer core
NetConnection peer = rawMessage.SenderConnection;
- using IncomingMessage message = new IncomingMessage();
+ using IncomingMessage message = new();
using Stream readStream = new NetBufferReadStream(rawMessage);
- using BinaryReader reader = new BinaryReader(readStream);
+ using BinaryReader reader = new(readStream);
while (rawMessage.LengthBits - rawMessage.Position >= 8)
{
diff --git a/src/SMAPI/Framework/Reflection/CacheEntry.cs b/src/SMAPI/Framework/Reflection/CacheEntry.cs
index 912662e3..6b18d204 100644
--- a/src/SMAPI/Framework/Reflection/CacheEntry.cs
+++ b/src/SMAPI/Framework/Reflection/CacheEntry.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Reflection;
namespace StardewModdingAPI.Framework.Reflection
diff --git a/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs b/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs
index 40adde8e..4c49e219 100644
--- a/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs
+++ b/src/SMAPI/Framework/Reflection/InterfaceProxyFactory.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Reflection;
using System.Reflection.Emit;
using Nanoray.Pintail;
diff --git a/src/SMAPI/Framework/Reflection/ReflectedField.cs b/src/SMAPI/Framework/Reflection/ReflectedField.cs
index 3c4da4fc..921876b9 100644
--- a/src/SMAPI/Framework/Reflection/ReflectedField.cs
+++ b/src/SMAPI/Framework/Reflection/ReflectedField.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Reflection;
diff --git a/src/SMAPI/Framework/Reflection/ReflectedMethod.cs b/src/SMAPI/Framework/Reflection/ReflectedMethod.cs
index 26112806..50f89b40 100644
--- a/src/SMAPI/Framework/Reflection/ReflectedMethod.cs
+++ b/src/SMAPI/Framework/Reflection/ReflectedMethod.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Reflection;
diff --git a/src/SMAPI/Framework/Reflection/ReflectedProperty.cs b/src/SMAPI/Framework/Reflection/ReflectedProperty.cs
index 42d7bb59..a6d8c75c 100644
--- a/src/SMAPI/Framework/Reflection/ReflectedProperty.cs
+++ b/src/SMAPI/Framework/Reflection/ReflectedProperty.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Reflection;
diff --git a/src/SMAPI/Framework/Reflection/Reflector.cs b/src/SMAPI/Framework/Reflection/Reflector.cs
index 889c7ed6..d5938c3f 100644
--- a/src/SMAPI/Framework/Reflection/Reflector.cs
+++ b/src/SMAPI/Framework/Reflection/Reflector.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Linq;
using System.Reflection;
@@ -13,7 +15,7 @@ namespace StardewModdingAPI.Framework.Reflection
** Fields
*********/
/// <summary>The cached fields and methods found via reflection.</summary>
- private readonly MemoryCache Cache = new MemoryCache(typeof(Reflector).FullName);
+ private readonly MemoryCache Cache = new(typeof(Reflector).FullName);
/// <summary>The sliding cache expiration time.</summary>
private readonly TimeSpan SlidingCacheExpiry = TimeSpan.FromMinutes(5);
@@ -268,7 +270,7 @@ namespace StardewModdingAPI.Framework.Reflection
// fetch & cache new value
TMemberInfo result = fetch();
- CacheEntry cacheEntry = new CacheEntry(result != null, result);
+ CacheEntry cacheEntry = new(result != null, result);
this.Cache.Add(key, cacheEntry, new CacheItemPolicy { SlidingExpiration = this.SlidingCacheExpiry });
return result;
}
diff --git a/src/SMAPI/Framework/Rendering/SDisplayDevice.cs b/src/SMAPI/Framework/Rendering/SDisplayDevice.cs
index 85e69ae6..8718bcb1 100644
--- a/src/SMAPI/Framework/Rendering/SDisplayDevice.cs
+++ b/src/SMAPI/Framework/Rendering/SDisplayDevice.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
diff --git a/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs b/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs
index cb499c6b..21edaedd 100644
--- a/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs
+++ b/src/SMAPI/Framework/Rendering/SXnaDisplayDevice.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/SMAPI/Framework/RequestExitDelegate.cs b/src/SMAPI/Framework/RequestExitDelegate.cs
index 810c399b..93ef1cf9 100644
--- a/src/SMAPI/Framework/RequestExitDelegate.cs
+++ b/src/SMAPI/Framework/RequestExitDelegate.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework
{
/// <summary>A delegate which requests that SMAPI immediately exit the game. This should only be invoked when an irrecoverable fatal error happens that risks save corruption or game-breaking bugs.</summary>
diff --git a/src/SMAPI/Framework/SChatBox.cs b/src/SMAPI/Framework/SChatBox.cs
index e000d1cd..d6286c12 100644
--- a/src/SMAPI/Framework/SChatBox.cs
+++ b/src/SMAPI/Framework/SChatBox.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using StardewValley;
using StardewValley.Menus;
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 44f46179..1a58d84b 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -158,9 +160,12 @@ namespace StardewModdingAPI.Framework
/// <remarks>This is only intended for use by external code like the Error Handler mod.</remarks>
internal static SCore Instance { get; private set; }
- /// <summary>The number of update ticks which have already executed. This is similar to <see cref="Game1.ticks"/>, but incremented more consistently for every tick.</summary>
+ /// <summary>The number of game update ticks which have already executed. This is similar to <see cref="Game1.ticks"/>, but incremented more consistently for every tick.</summary>
internal static uint TicksElapsed { get; private set; }
+ /// <summary>A specialized form of <see cref="TicksElapsed"/> which is incremented each time SMAPI performs a processing tick (whether that's a game update, one wait cycle while synchronizing code, etc).</summary>
+ internal static uint ProcessTicksElapsed { get; private set; }
+
/*********
** Public methods
@@ -231,13 +236,13 @@ namespace StardewModdingAPI.Framework
this.Toolkit.JsonHelper.JsonSettings.Converters.Add(converter);
// add error handlers
- AppDomain.CurrentDomain.UnhandledException += (sender, e) => this.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error);
+ AppDomain.CurrentDomain.UnhandledException += (_, e) => this.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error);
// add more lenient assembly resolver
- AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => AssemblyLoader.ResolveAssembly(e.Name);
+ AppDomain.CurrentDomain.AssemblyResolve += (_, e) => AssemblyLoader.ResolveAssembly(e.Name);
// hook locale event
- LocalizedContentManager.OnLanguageChange += locale => this.OnLocaleChanged();
+ LocalizedContentManager.OnLanguageChange += _ => this.OnLocaleChanged();
// override game
this.Multiplayer = new SMultiplayer(this.Monitor, this.EventManager, this.Toolkit.JsonHelper, this.ModRegistry, this.Reflection, this.OnModMessageReceived, this.Settings.LogNetworkTraffic);
@@ -560,6 +565,7 @@ namespace StardewModdingAPI.Framework
finally
{
SCore.TicksElapsed++;
+ SCore.ProcessTicksElapsed++;
}
}
@@ -578,7 +584,7 @@ namespace StardewModdingAPI.Framework
*********/
if (this.JustReturnedToTitle)
{
- if (!(Game1.mapDisplayDevice is SDisplayDevice))
+ if (Game1.mapDisplayDevice is not SDisplayDevice)
Game1.mapDisplayDevice = this.GetMapDisplayDevice();
this.JustReturnedToTitle = false;
@@ -633,6 +639,8 @@ namespace StardewModdingAPI.Framework
this.Reflection.GetMethod(Game1.game1, "UpdateTitleScreen").Invoke(Game1.currentGameTime); // run game logic to change music on load, etc
while (Game1.currentLoader?.MoveNext() == true)
{
+ SCore.ProcessTicksElapsed++;
+
// raise load stage changed
switch (Game1.currentLoader.Current)
{
@@ -1446,41 +1454,6 @@ namespace StardewModdingAPI.Framework
this.LogManager.WriteUpdateMarker(updateFound.ToString(), updateUrl);
}
- // check Stardew64Installer version
- if (Constants.IsPatchedByStardew64Installer(out ISemanticVersion patchedByVersion))
- {
- ISemanticVersion updateFound = null;
- string updateUrl = null;
- try
- {
- // fetch update check
- ModEntryModel response = client.GetModInfo(new[] { new ModSearchEntryModel("Steviegt6.Stardew64Installer", patchedByVersion, new[] { $"GitHub:{this.Settings.Stardew64InstallerGitHubProjectName}" }) }, apiVersion: Constants.ApiVersion, gameVersion: Constants.GameVersion, platform: Constants.Platform).Single().Value;
- updateFound = response.SuggestedUpdate?.Version;
- updateUrl = response.SuggestedUpdate?.Url ?? Constants.HomePageUrl;
-
- // log message
- if (updateFound != null)
- this.Monitor.Log($"You can update Stardew64Installer to {updateFound}: {updateUrl}", LogLevel.Alert);
- else
- this.Monitor.Log(" Stardew64Installer okay.");
-
- // show errors
- if (response.Errors.Any())
- {
- this.Monitor.Log("Couldn't check for a new version of Stardew64Installer. This won't affect your game, but you may not be notified of new versions if this keeps happening.", LogLevel.Warn);
- this.Monitor.Log($"Error: {string.Join("\n", response.Errors)}");
- }
- }
- catch (Exception ex)
- {
- this.Monitor.Log("Couldn't check for a new version of Stardew64Installer. This won't affect your game, but you won't be notified of new versions if this keeps happening.", LogLevel.Warn);
- this.Monitor.Log(ex is WebException && ex.InnerException == null
- ? $"Error: {ex.Message}"
- : $"Error: {ex.GetLogSummary()}"
- );
- }
- }
-
// check mod versions
if (mods.Any())
{
@@ -1622,10 +1595,11 @@ namespace StardewModdingAPI.Framework
// initialize loaded non-content-pack mods
this.Monitor.Log("Launching mods...", LogLevel.Debug);
+#pragma warning disable CS0612, CS0618 // deprecated code
foreach (IModMetadata metadata in loadedMods)
{
// add interceptors
- if (metadata.Mod.Helper.Content is ContentHelper helper)
+ if (metadata.Mod.Helper is ModHelper helper)
{
// ReSharper disable SuspiciousTypeConversion.Global
if (metadata.Mod is IAssetEditor editor)
@@ -1653,9 +1627,11 @@ namespace StardewModdingAPI.Framework
}
// ReSharper restore SuspiciousTypeConversion.Global
- helper.ObservableAssetEditors.CollectionChanged += (sender, e) => this.OnAssetInterceptorsChanged(metadata, e.NewItems?.Cast<IAssetEditor>(), e.OldItems?.Cast<IAssetEditor>(), this.ContentCore.Editors);
- helper.ObservableAssetLoaders.CollectionChanged += (sender, e) => this.OnAssetInterceptorsChanged(metadata, e.NewItems?.Cast<IAssetLoader>(), e.OldItems?.Cast<IAssetLoader>(), this.ContentCore.Loaders);
+ ContentHelper content = helper.GetLegacyContentHelper();
+ content.ObservableAssetEditors.CollectionChanged += (_, e) => this.OnAssetInterceptorsChanged(metadata, e.NewItems?.Cast<IAssetEditor>(), e.OldItems?.Cast<IAssetEditor>(), this.ContentCore.Editors);
+ content.ObservableAssetLoaders.CollectionChanged += (_, e) => this.OnAssetInterceptorsChanged(metadata, e.NewItems?.Cast<IAssetLoader>(), e.OldItems?.Cast<IAssetLoader>(), this.ContentCore.Loaders);
}
+#pragma warning restore CS0612, CS0618
// call entry method
try
@@ -1777,10 +1753,11 @@ namespace StardewModdingAPI.Framework
{
IManifest manifest = mod.Manifest;
IMonitor monitor = this.LogManager.GetMonitor(mod.DisplayName);
+ CaseInsensitivePathCache relativePathCache = this.ContentCore.GetCaseInsensitivePathCache(mod.DirectoryPath);
GameContentHelper gameContentHelper = new(this.ContentCore, manifest.UniqueID, mod.DisplayName, monitor);
- IModContentHelper modContentHelper = new ModContentHelper(this.ContentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, gameContentHelper.GetUnderlyingContentManager());
+ IModContentHelper modContentHelper = new ModContentHelper(this.ContentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, gameContentHelper.GetUnderlyingContentManager(), relativePathCache);
TranslationHelper translationHelper = new(manifest.UniqueID, contentCore.GetLocale(), contentCore.Language);
- IContentPack contentPack = new ContentPack(mod.DirectoryPath, manifest, modContentHelper, translationHelper, jsonHelper);
+ IContentPack contentPack = new ContentPack(mod.DirectoryPath, manifest, modContentHelper, translationHelper, jsonHelper, relativePathCache);
mod.SetMod(contentPack, monitor, translationHelper);
this.ModRegistry.Add(mod);
@@ -1858,11 +1835,14 @@ namespace StardewModdingAPI.Framework
IContentPack CreateFakeContentPack(string packDirPath, IManifest packManifest)
{
IMonitor packMonitor = this.LogManager.GetMonitor(packManifest.Name);
+
+ CaseInsensitivePathCache relativePathCache = this.ContentCore.GetCaseInsensitivePathCache(packDirPath);
+
GameContentHelper gameContentHelper = new(contentCore, packManifest.UniqueID, packManifest.Name, packMonitor);
- IModContentHelper packContentHelper = new ModContentHelper(contentCore, packDirPath, packManifest.UniqueID, packManifest.Name, gameContentHelper.GetUnderlyingContentManager());
+ IModContentHelper packContentHelper = new ModContentHelper(contentCore, packDirPath, packManifest.UniqueID, packManifest.Name, gameContentHelper.GetUnderlyingContentManager(), relativePathCache);
TranslationHelper packTranslationHelper = new(packManifest.UniqueID, contentCore.GetLocale(), contentCore.Language);
- ContentPack contentPack = new(packDirPath, packManifest, packContentHelper, packTranslationHelper, this.Toolkit.JsonHelper);
+ ContentPack contentPack = new(packDirPath, packManifest, packContentHelper, packTranslationHelper, this.Toolkit.JsonHelper, relativePathCache);
this.ReloadTranslationsForTemporaryContentPack(mod, contentPack);
mod.FakeContentPacks.Add(new WeakReference<ContentPack>(contentPack));
return contentPack;
@@ -1870,9 +1850,12 @@ namespace StardewModdingAPI.Framework
IModEvents events = new ModEvents(mod, this.EventManager);
ICommandHelper commandHelper = new CommandHelper(mod, this.CommandManager);
- IContentHelper contentHelper = new ContentHelper(contentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, monitor);
+ CaseInsensitivePathCache relativePathCache = this.ContentCore.GetCaseInsensitivePathCache(mod.DirectoryPath);
+#pragma warning disable CS0612 // deprecated code
+ ContentHelper contentHelper = new(contentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, monitor);
+#pragma warning restore CS0612
GameContentHelper gameContentHelper = new(contentCore, manifest.UniqueID, mod.DisplayName, monitor);
- IModContentHelper modContentHelper = new ModContentHelper(contentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, gameContentHelper.GetUnderlyingContentManager());
+ IModContentHelper modContentHelper = new ModContentHelper(contentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, gameContentHelper.GetUnderlyingContentManager(), relativePathCache);
IContentPackHelper contentPackHelper = new ContentPackHelper(manifest.UniqueID, new Lazy<IContentPack[]>(GetContentPacks), CreateFakeContentPack);
IDataHelper dataHelper = new DataHelper(manifest.UniqueID, mod.DirectoryPath, jsonHelper);
IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, mod.DisplayName, this.Reflection);
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index 104cf330..7ca89eec 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
@@ -37,7 +39,7 @@ namespace StardewModdingAPI.Framework
private readonly EventManager Events;
/// <summary>The maximum number of consecutive attempts SMAPI should make to recover from a draw error.</summary>
- private readonly Countdown DrawCrashTimer = new Countdown(60); // 60 ticks = roughly one second
+ private readonly Countdown DrawCrashTimer = new(60); // 60 ticks = roughly one second
/// <summary>Simplifies access to private game code.</summary>
private readonly Reflector Reflection;
@@ -71,14 +73,14 @@ namespace StardewModdingAPI.Framework
public WatcherCore Watchers { get; private set; }
/// <summary>A snapshot of the current <see cref="Watchers"/> state.</summary>
- public WatcherSnapshot WatcherSnapshot { get; } = new WatcherSnapshot();
+ public WatcherSnapshot WatcherSnapshot { get; } = new();
/// <summary>Whether the current update tick is the first one for this instance.</summary>
public bool IsFirstTick = true;
/// <summary>The number of ticks until SMAPI should notify mods that the game has loaded.</summary>
/// <remarks>Skipping a few frames ensures the game finishes initializing the world before mods try to change it.</remarks>
- public Countdown AfterLoadTimer { get; } = new Countdown(5);
+ public Countdown AfterLoadTimer { get; } = new(5);
/// <summary>Whether the game is saving and SMAPI has already raised <see cref="IGameLoopEvents.Saving"/>.</summary>
public bool IsBetweenSaveEvents { get; set; }
@@ -624,7 +626,7 @@ namespace StardewModdingAPI.Framework
if (tile != null)
{
Vector2 vector_draw_position = Game1.GlobalToLocal(Game1.viewport, tile_position * 64f);
- Location draw_location = new Location((int)vector_draw_position.X, (int)vector_draw_position.Y);
+ Location draw_location = new((int)vector_draw_position.X, (int)vector_draw_position.Y);
Game1.mapDisplayDevice.DrawTile(tile, draw_location, (tile_position.Y * 64f - 1f) / 10000f);
}
}
diff --git a/src/SMAPI/Framework/SGameRunner.cs b/src/SMAPI/Framework/SGameRunner.cs
index b816bb7c..dae314af 100644
--- a/src/SMAPI/Framework/SGameRunner.cs
+++ b/src/SMAPI/Framework/SGameRunner.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -93,7 +95,7 @@ namespace StardewModdingAPI.Framework
/// <param name="instanceIndex">The instance index.</param>
public override Game1 CreateGameInstance(PlayerIndex playerIndex = PlayerIndex.One, int instanceIndex = 0)
{
- SInputState inputState = new SInputState();
+ SInputState inputState = new();
return new SGame(playerIndex, instanceIndex, this.Monitor, this.Reflection, this.Events, inputState, this.ModHooks, this.Multiplayer, this.ExitGameImmediately, this.OnPlayerInstanceUpdating, this.OnGameContentLoaded);
}
diff --git a/src/SMAPI/Framework/SModHooks.cs b/src/SMAPI/Framework/SModHooks.cs
index 101e022a..7941e102 100644
--- a/src/SMAPI/Framework/SModHooks.cs
+++ b/src/SMAPI/Framework/SModHooks.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Threading.Tasks;
using StardewValley;
diff --git a/src/SMAPI/Framework/SMultiplayer.cs b/src/SMAPI/Framework/SMultiplayer.cs
index 5956b63f..de3c25a5 100644
--- a/src/SMAPI/Framework/SMultiplayer.cs
+++ b/src/SMAPI/Framework/SMultiplayer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -56,10 +58,10 @@ namespace StardewModdingAPI.Framework
private readonly bool LogNetworkTraffic;
/// <summary>The backing field for <see cref="Peers"/>.</summary>
- private readonly PerScreen<IDictionary<long, MultiplayerPeer>> PeersImpl = new PerScreen<IDictionary<long, MultiplayerPeer>>(() => new Dictionary<long, MultiplayerPeer>());
+ private readonly PerScreen<IDictionary<long, MultiplayerPeer>> PeersImpl = new(() => new Dictionary<long, MultiplayerPeer>());
/// <summary>The backing field for <see cref="HostPeer"/>.</summary>
- private readonly PerScreen<MultiplayerPeer> HostPeerImpl = new PerScreen<MultiplayerPeer>();
+ private readonly PerScreen<MultiplayerPeer> HostPeerImpl = new();
/*********
@@ -111,20 +113,20 @@ namespace StardewModdingAPI.Framework
{
switch (client)
{
- case LidgrenClient _:
+ case LidgrenClient:
{
string address = this.Reflection.GetField<string>(client, "address").GetValue();
return new SLidgrenClient(address, this.OnClientProcessingMessage, this.OnClientSendingMessage);
}
- case GalaxyNetClient _:
+ case GalaxyNetClient:
{
GalaxyID address = this.Reflection.GetField<GalaxyID>(client, "lobbyId").GetValue();
return new SGalaxyNetClient(address, this.OnClientProcessingMessage, this.OnClientSendingMessage);
}
default:
- this.Monitor.Log($"Unknown multiplayer client type: {client.GetType().AssemblyQualifiedName}", LogLevel.Trace);
+ this.Monitor.Log($"Unknown multiplayer client type: {client.GetType().AssemblyQualifiedName}");
return client;
}
}
@@ -135,20 +137,20 @@ namespace StardewModdingAPI.Framework
{
switch (server)
{
- case LidgrenServer _:
+ case LidgrenServer:
{
IGameServer gameServer = this.Reflection.GetField<IGameServer>(server, "gameServer").GetValue();
return new SLidgrenServer(gameServer, this, this.OnServerProcessingMessage);
}
- case GalaxyNetServer _:
+ case GalaxyNetServer:
{
IGameServer gameServer = this.Reflection.GetField<IGameServer>(server, "gameServer").GetValue();
return new SGalaxyNetServer(gameServer, this, this.OnServerProcessingMessage);
}
default:
- this.Monitor.Log($"Unknown multiplayer server type: {server.GetType().AssemblyQualifiedName}", LogLevel.Trace);
+ this.Monitor.Log($"Unknown multiplayer server type: {server.GetType().AssemblyQualifiedName}");
return server;
}
}
@@ -160,7 +162,7 @@ namespace StardewModdingAPI.Framework
protected void OnClientSendingMessage(OutgoingMessage message, Action<OutgoingMessage> sendMessage, Action resume)
{
if (this.LogNetworkTraffic)
- this.Monitor.Log($"CLIENT SEND {(MessageType)message.MessageType} {message.FarmerID}", LogLevel.Trace);
+ this.Monitor.Log($"CLIENT SEND {(MessageType)message.MessageType} {message.FarmerID}");
switch (message.MessageType)
{
@@ -184,7 +186,7 @@ namespace StardewModdingAPI.Framework
public void OnServerProcessingMessage(IncomingMessage message, Action<OutgoingMessage> sendMessage, Action resume)
{
if (this.LogNetworkTraffic)
- this.Monitor.Log($"SERVER RECV {(MessageType)message.MessageType} {message.FarmerID}", LogLevel.Trace);
+ this.Monitor.Log($"SERVER RECV {(MessageType)message.MessageType} {message.FarmerID}");
switch (message.MessageType)
{
@@ -193,10 +195,10 @@ namespace StardewModdingAPI.Framework
{
// parse message
RemoteContextModel model = this.ReadContext(message.Reader);
- this.Monitor.Log($"Received context for farmhand {message.FarmerID} running {(model != null ? $"SMAPI {model.ApiVersion} with {model.Mods.Length} mods" : "vanilla")}.", LogLevel.Trace);
+ this.Monitor.Log($"Received context for farmhand {message.FarmerID} running {(model != null ? $"SMAPI {model.ApiVersion} with {model.Mods.Length} mods" : "vanilla")}.");
// store peer
- MultiplayerPeer newPeer = new MultiplayerPeer(
+ MultiplayerPeer newPeer = new(
playerID: message.FarmerID,
screenID: this.GetScreenId(message.FarmerID),
model: model,
@@ -243,8 +245,8 @@ namespace StardewModdingAPI.Framework
// store peer if new
if (!this.Peers.ContainsKey(message.FarmerID))
{
- this.Monitor.Log($"Received connection for vanilla player {message.FarmerID}.", LogLevel.Trace);
- MultiplayerPeer peer = new MultiplayerPeer(
+ this.Monitor.Log($"Received connection for vanilla player {message.FarmerID}.");
+ MultiplayerPeer peer = new(
playerID: message.FarmerID,
screenID: this.GetScreenId(message.FarmerID),
model: null,
@@ -280,7 +282,7 @@ namespace StardewModdingAPI.Framework
public void OnClientProcessingMessage(IncomingMessage message, Action<OutgoingMessage> sendMessage, Action resume)
{
if (this.LogNetworkTraffic)
- this.Monitor.Log($"CLIENT RECV {(MessageType)message.MessageType} {message.FarmerID}", LogLevel.Trace);
+ this.Monitor.Log($"CLIENT RECV {(MessageType)message.MessageType} {message.FarmerID}");
switch (message.MessageType)
{
@@ -289,10 +291,10 @@ namespace StardewModdingAPI.Framework
{
// parse message
RemoteContextModel model = this.ReadContext(message.Reader);
- this.Monitor.Log($"Received context for {(model?.IsHost == true ? "host" : "farmhand")} {message.FarmerID} running {(model != null ? $"SMAPI {model.ApiVersion} with {model.Mods.Length} mods" : "vanilla")}.", LogLevel.Trace);
+ this.Monitor.Log($"Received context for {(model?.IsHost == true ? "host" : "farmhand")} {message.FarmerID} running {(model != null ? $"SMAPI {model.ApiVersion} with {model.Mods.Length} mods" : "vanilla")}.");
// store peer
- MultiplayerPeer peer = new MultiplayerPeer(
+ MultiplayerPeer peer = new(
playerID: message.FarmerID,
screenID: this.GetScreenId(message.FarmerID),
model: model,
@@ -314,7 +316,7 @@ namespace StardewModdingAPI.Framework
// store peer
if (!this.Peers.ContainsKey(message.FarmerID) && this.HostPeer == null)
{
- this.Monitor.Log($"Received connection for vanilla host {message.FarmerID}.", LogLevel.Trace);
+ this.Monitor.Log($"Received connection for vanilla host {message.FarmerID}.");
var peer = new MultiplayerPeer(
playerID: message.FarmerID,
screenID: this.GetScreenId(message.FarmerID),
@@ -341,7 +343,7 @@ namespace StardewModdingAPI.Framework
sendMessage: sendMessage,
isHost: this.HostPeer == null
);
- this.Monitor.Log($"Received connection for vanilla {(peer.IsHost ? "host" : "farmhand")} {message.FarmerID}.", LogLevel.Trace);
+ this.Monitor.Log($"Received connection for vanilla {(peer.IsHost ? "host" : "farmhand")} {message.FarmerID}.");
this.AddPeer(peer, canBeHost: true);
}
@@ -367,7 +369,7 @@ namespace StardewModdingAPI.Framework
{
if (this.Peers.TryGetValue(playerID, out MultiplayerPeer peer))
{
- this.Monitor.Log($"Player quit: {playerID}", LogLevel.Trace);
+ this.Monitor.Log($"Player quit: {playerID}");
this.Peers.Remove(playerID);
this.EventManager.PeerDisconnected.Raise(new PeerDisconnectedEventArgs(peer));
}
@@ -420,7 +422,7 @@ namespace StardewModdingAPI.Framework
}
// get data to send
- ModMessageModel model = new ModMessageModel(
+ ModMessageModel model = new(
fromPlayerID: Game1.player.UniqueMultiplayerID,
fromModID: fromModID,
toModIDs: toModIDs,
@@ -434,7 +436,7 @@ namespace StardewModdingAPI.Framework
if (sendToSelf)
{
if (this.LogNetworkTraffic)
- this.Monitor.Log($"Broadcasting '{messageType}' message to self: {data}.", LogLevel.Trace);
+ this.Monitor.Log($"Broadcasting '{messageType}' message to self: {data}.");
this.OnModMessageReceived(model);
}
@@ -447,7 +449,7 @@ namespace StardewModdingAPI.Framework
foreach (MultiplayerPeer peer in sendToPeers)
{
if (this.LogNetworkTraffic)
- this.Monitor.Log($"Broadcasting '{messageType}' message to farmhand {peer.PlayerID}: {data}.", LogLevel.Trace);
+ this.Monitor.Log($"Broadcasting '{messageType}' message to farmhand {peer.PlayerID}: {data}.");
peer.SendMessage(new OutgoingMessage((byte)MessageType.ModMessage, peer.PlayerID, data));
}
@@ -455,7 +457,7 @@ namespace StardewModdingAPI.Framework
else if (this.HostPeer?.HasSmapi == true)
{
if (this.LogNetworkTraffic)
- this.Monitor.Log($"Broadcasting '{messageType}' message to host {this.HostPeer.PlayerID}: {data}.", LogLevel.Trace);
+ this.Monitor.Log($"Broadcasting '{messageType}' message to host {this.HostPeer.PlayerID}: {data}.");
this.HostPeer.SendMessage(new OutgoingMessage((byte)MessageType.ModMessage, this.HostPeer.PlayerID, data));
}
@@ -504,7 +506,7 @@ namespace StardewModdingAPI.Framework
ModMessageModel model = this.JsonHelper.Deserialize<ModMessageModel>(json);
HashSet<long> playerIDs = new HashSet<long>(model.ToPlayerIDs ?? this.GetKnownPlayerIDs());
if (this.LogNetworkTraffic)
- this.Monitor.Log($"Received message: {json}.", LogLevel.Trace);
+ this.Monitor.Log($"Received message: {json}.");
// notify local mods
if (playerIDs.Contains(Game1.player.UniqueMultiplayerID))
@@ -513,7 +515,7 @@ namespace StardewModdingAPI.Framework
// forward to other players
if (Context.IsMainPlayer && playerIDs.Any(p => p != Game1.player.UniqueMultiplayerID))
{
- ModMessageModel newModel = new ModMessageModel(model);
+ ModMessageModel newModel = new(model);
foreach (long playerID in playerIDs)
{
if (playerID != Game1.player.UniqueMultiplayerID && playerID != model.FromPlayerID && this.Peers.TryGetValue(playerID, out MultiplayerPeer peer))
@@ -544,7 +546,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Get the fields to include in a context sync message sent to other players.</summary>
private object[] GetContextSyncMessageFields()
{
- RemoteContextModel model = new RemoteContextModel
+ RemoteContextModel model = new()
{
IsHost = Context.IsWorldReady && Context.IsMainPlayer,
Platform = Constants.TargetPlatform,
@@ -571,7 +573,7 @@ namespace StardewModdingAPI.Framework
if (!peer.HasSmapi)
return new object[] { "{}" };
- RemoteContextModel model = new RemoteContextModel
+ RemoteContextModel model = new()
{
IsHost = peer.IsHost,
Platform = peer.Platform.Value,
diff --git a/src/SMAPI/Framework/Serialization/KeybindConverter.cs b/src/SMAPI/Framework/Serialization/KeybindConverter.cs
index 93a274a8..f3bab20d 100644
--- a/src/SMAPI/Framework/Serialization/KeybindConverter.cs
+++ b/src/SMAPI/Framework/Serialization/KeybindConverter.cs
@@ -36,7 +36,7 @@ namespace StardewModdingAPI.Framework.Serialization
/// <param name="objectType">The object type.</param>
/// <param name="existingValue">The object being read.</param>
/// <param name="serializer">The calling serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
string path = reader.Path;
@@ -44,7 +44,7 @@ namespace StardewModdingAPI.Framework.Serialization
{
case JsonToken.Null:
return objectType == typeof(Keybind)
- ? (object)new Keybind()
+ ? new Keybind()
: new KeybindList();
case JsonToken.String:
@@ -74,7 +74,7 @@ namespace StardewModdingAPI.Framework.Serialization
/// <param name="writer">The JSON writer.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
writer.WriteValue(value?.ToString());
}
diff --git a/src/SMAPI/Framework/Singleton.cs b/src/SMAPI/Framework/Singleton.cs
index 399a8bf0..da16c48e 100644
--- a/src/SMAPI/Framework/Singleton.cs
+++ b/src/SMAPI/Framework/Singleton.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework
{
/// <summary>Provides singleton instances of a given type.</summary>
@@ -5,6 +7,6 @@ namespace StardewModdingAPI.Framework
internal static class Singleton<T> where T : new()
{
/// <summary>The singleton instance.</summary>
- public static T Instance { get; } = new T();
+ public static T Instance { get; } = new();
}
}
diff --git a/src/SMAPI/Framework/SnapshotDiff.cs b/src/SMAPI/Framework/SnapshotDiff.cs
index 5b6288ff..eb2aebe1 100644
--- a/src/SMAPI/Framework/SnapshotDiff.cs
+++ b/src/SMAPI/Framework/SnapshotDiff.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using StardewModdingAPI.Framework.StateTracking;
namespace StardewModdingAPI.Framework
diff --git a/src/SMAPI/Framework/SnapshotItemListDiff.cs b/src/SMAPI/Framework/SnapshotItemListDiff.cs
index e8ab1b1e..97942783 100644
--- a/src/SMAPI/Framework/SnapshotItemListDiff.cs
+++ b/src/SMAPI/Framework/SnapshotItemListDiff.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Framework/SnapshotListDiff.cs b/src/SMAPI/Framework/SnapshotListDiff.cs
index 2d0efa0d..1d585c15 100644
--- a/src/SMAPI/Framework/SnapshotListDiff.cs
+++ b/src/SMAPI/Framework/SnapshotListDiff.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using StardewModdingAPI.Framework.StateTracking;
@@ -11,10 +13,10 @@ namespace StardewModdingAPI.Framework
** Fields
*********/
/// <summary>The removed values.</summary>
- private readonly List<T> RemovedImpl = new List<T>();
+ private readonly List<T> RemovedImpl = new();
/// <summary>The added values.</summary>
- private readonly List<T> AddedImpl = new List<T>();
+ private readonly List<T> AddedImpl = new();
/*********
diff --git a/src/SMAPI/Framework/StateTracking/ChestTracker.cs b/src/SMAPI/Framework/StateTracking/ChestTracker.cs
index 65f58ee7..28335200 100644
--- a/src/SMAPI/Framework/StateTracking/ChestTracker.cs
+++ b/src/SMAPI/Framework/StateTracking/ChestTracker.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -18,10 +20,10 @@ namespace StardewModdingAPI.Framework.StateTracking
private readonly IDictionary<Item, int> StackSizes;
/// <summary>Items added since the last update.</summary>
- private readonly HashSet<Item> Added = new HashSet<Item>(new ObjectReferenceComparer<Item>());
+ private readonly HashSet<Item> Added = new(new ObjectReferenceComparer<Item>());
/// <summary>Items removed since the last update.</summary>
- private readonly HashSet<Item> Removed = new HashSet<Item>(new ObjectReferenceComparer<Item>());
+ private readonly HashSet<Item> Removed = new(new ObjectReferenceComparer<Item>());
/// <summary>The underlying inventory watcher.</summary>
private readonly ICollectionWatcher<Item> InventoryWatcher;
diff --git a/src/SMAPI/Framework/StateTracking/Comparers/EquatableComparer.cs b/src/SMAPI/Framework/StateTracking/Comparers/EquatableComparer.cs
index a96ffdb6..987e1820 100644
--- a/src/SMAPI/Framework/StateTracking/Comparers/EquatableComparer.cs
+++ b/src/SMAPI/Framework/StateTracking/Comparers/EquatableComparer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
diff --git a/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs b/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs
index cc1d6553..f6b04583 100644
--- a/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs
+++ b/src/SMAPI/Framework/StateTracking/Comparers/GenericEqualsComparer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Runtime.CompilerServices;
diff --git a/src/SMAPI/Framework/StateTracking/Comparers/ObjectReferenceComparer.cs b/src/SMAPI/Framework/StateTracking/Comparers/ObjectReferenceComparer.cs
index ef9adafb..8d3a7eb9 100644
--- a/src/SMAPI/Framework/StateTracking/Comparers/ObjectReferenceComparer.cs
+++ b/src/SMAPI/Framework/StateTracking/Comparers/ObjectReferenceComparer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Runtime.CompilerServices;
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/BaseDisposableWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/BaseDisposableWatcher.cs
index 60006c51..03bf84d9 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/BaseDisposableWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/BaseDisposableWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs
index 32ec8c7e..52e1dbad 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableListWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
@@ -17,10 +19,10 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
private HashSet<TValue> LastValues;
/// <summary>The pairs added since the last reset.</summary>
- private readonly List<TValue> AddedImpl = new List<TValue>();
+ private readonly List<TValue> AddedImpl = new();
/// <summary>The pairs removed since the last reset.</summary>
- private readonly List<TValue> RemovedImpl = new List<TValue>();
+ private readonly List<TValue> RemovedImpl = new();
/*********
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs
index 5ca4b9f4..4f94294c 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ComparableWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ImmutableCollectionWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ImmutableCollectionWatcher.cs
index 009e0282..94ce0c8e 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ImmutableCollectionWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ImmutableCollectionWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
@@ -11,7 +13,7 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
** Accessors
*********/
/// <summary>A singleton collection watcher instance.</summary>
- public static ImmutableCollectionWatcher<TValue> Instance { get; } = new ImmutableCollectionWatcher<TValue>();
+ public static ImmutableCollectionWatcher<TValue> Instance { get; } = new();
/// <summary>Whether the collection changed since the last reset.</summary>
public bool IsChanged { get; } = false;
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs
index 21e84c47..e662c433 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetCollectionWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using Netcode;
@@ -15,10 +17,10 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
private readonly NetCollection<TValue> Field;
/// <summary>The pairs added since the last reset.</summary>
- private readonly List<TValue> AddedImpl = new List<TValue>();
+ private readonly List<TValue> AddedImpl = new();
/// <summary>The pairs removed since the last reset.</summary>
- private readonly List<TValue> RemovedImpl = new List<TValue>();
+ private readonly List<TValue> RemovedImpl = new();
/*********
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs
index e6882f7e..0d7f2ad2 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetDictionaryWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using Netcode;
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetListWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetListWatcher.cs
index 0b4d3030..a97e754c 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetListWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetListWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using Netcode;
using StardewModdingAPI.Framework.StateTracking.Comparers;
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs
index 48d5d681..26641750 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/NetValueWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Netcode;
namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs
index c29d2783..82e5387e 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/ObservableCollectionWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
@@ -16,13 +18,13 @@ namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers
private readonly ObservableCollection<TValue> Field;
/// <summary>The pairs added since the last reset.</summary>
- private readonly List<TValue> AddedImpl = new List<TValue>();
+ private readonly List<TValue> AddedImpl = new();
/// <summary>The pairs removed since the last reset.</summary>
- private readonly List<TValue> RemovedImpl = new List<TValue>();
+ private readonly List<TValue> RemovedImpl = new();
/// <summary>The previous values as of the last update.</summary>
- private readonly List<TValue> PreviousValues = new List<TValue>();
+ private readonly List<TValue> PreviousValues = new();
/*********
diff --git a/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs b/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs
index bde43486..0b99914c 100644
--- a/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs
+++ b/src/SMAPI/Framework/StateTracking/FieldWatchers/WatcherFactory.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
diff --git a/src/SMAPI/Framework/StateTracking/ICollectionWatcher.cs b/src/SMAPI/Framework/StateTracking/ICollectionWatcher.cs
index 7a7759e3..74c9313b 100644
--- a/src/SMAPI/Framework/StateTracking/ICollectionWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/ICollectionWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
namespace StardewModdingAPI.Framework.StateTracking
diff --git a/src/SMAPI/Framework/StateTracking/IDictionaryWatcher.cs b/src/SMAPI/Framework/StateTracking/IDictionaryWatcher.cs
index 691ed377..81fb7460 100644
--- a/src/SMAPI/Framework/StateTracking/IDictionaryWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/IDictionaryWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
namespace StardewModdingAPI.Framework.StateTracking
diff --git a/src/SMAPI/Framework/StateTracking/IValueWatcher.cs b/src/SMAPI/Framework/StateTracking/IValueWatcher.cs
index 4afca972..7d46053c 100644
--- a/src/SMAPI/Framework/StateTracking/IValueWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/IValueWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI.Framework.StateTracking
{
/// <summary>A watcher which tracks changes to a value.</summary>
diff --git a/src/SMAPI/Framework/StateTracking/IWatcher.cs b/src/SMAPI/Framework/StateTracking/IWatcher.cs
index 8c7fa51c..3603b6f8 100644
--- a/src/SMAPI/Framework/StateTracking/IWatcher.cs
+++ b/src/SMAPI/Framework/StateTracking/IWatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI.Framework.StateTracking
diff --git a/src/SMAPI/Framework/StateTracking/LocationTracker.cs b/src/SMAPI/Framework/StateTracking/LocationTracker.cs
index 748e4ecc..9c2ff7f0 100644
--- a/src/SMAPI/Framework/StateTracking/LocationTracker.cs
+++ b/src/SMAPI/Framework/StateTracking/LocationTracker.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -19,7 +21,7 @@ namespace StardewModdingAPI.Framework.StateTracking
** Fields
*********/
/// <summary>The underlying watchers.</summary>
- private readonly List<IWatcher> Watchers = new List<IWatcher>();
+ private readonly List<IWatcher> Watchers = new();
/*********
diff --git a/src/SMAPI/Framework/StateTracking/PlayerTracker.cs b/src/SMAPI/Framework/StateTracking/PlayerTracker.cs
index cf49a7c1..367eafea 100644
--- a/src/SMAPI/Framework/StateTracking/PlayerTracker.cs
+++ b/src/SMAPI/Framework/StateTracking/PlayerTracker.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -24,7 +26,7 @@ namespace StardewModdingAPI.Framework.StateTracking
private GameLocation LastValidLocation;
/// <summary>The underlying watchers.</summary>
- private readonly List<IWatcher> Watchers = new List<IWatcher>();
+ private readonly List<IWatcher> Watchers = new();
/*********
diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs
index 6c9cc4f5..3d13f92b 100644
--- a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs
+++ b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using StardewValley;
@@ -17,25 +19,25 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots
public GameLocation Location { get; }
/// <summary>Tracks added or removed buildings.</summary>
- public SnapshotListDiff<Building> Buildings { get; } = new SnapshotListDiff<Building>();
+ public SnapshotListDiff<Building> Buildings { get; } = new();
/// <summary>Tracks added or removed debris.</summary>
- public SnapshotListDiff<Debris> Debris { get; } = new SnapshotListDiff<Debris>();
+ public SnapshotListDiff<Debris> Debris { get; } = new();
/// <summary>Tracks added or removed large terrain features.</summary>
- public SnapshotListDiff<LargeTerrainFeature> LargeTerrainFeatures { get; } = new SnapshotListDiff<LargeTerrainFeature>();
+ public SnapshotListDiff<LargeTerrainFeature> LargeTerrainFeatures { get; } = new();
/// <summary>Tracks added or removed NPCs.</summary>
- public SnapshotListDiff<NPC> Npcs { get; } = new SnapshotListDiff<NPC>();
+ public SnapshotListDiff<NPC> Npcs { get; } = new();
/// <summary>Tracks added or removed objects.</summary>
- public SnapshotListDiff<KeyValuePair<Vector2, Object>> Objects { get; } = new SnapshotListDiff<KeyValuePair<Vector2, Object>>();
+ public SnapshotListDiff<KeyValuePair<Vector2, Object>> Objects { get; } = new();
/// <summary>Tracks added or removed terrain features.</summary>
- public SnapshotListDiff<KeyValuePair<Vector2, TerrainFeature>> TerrainFeatures { get; } = new SnapshotListDiff<KeyValuePair<Vector2, TerrainFeature>>();
+ public SnapshotListDiff<KeyValuePair<Vector2, TerrainFeature>> TerrainFeatures { get; } = new();
/// <summary>Tracks added or removed furniture.</summary>
- public SnapshotListDiff<Furniture> Furniture { get; } = new SnapshotListDiff<Furniture>();
+ public SnapshotListDiff<Furniture> Furniture { get; } = new();
/// <summary>Tracks changed chest inventories.</summary>
public IDictionary<Chest, SnapshotItemListDiff> ChestItems { get; } = new Dictionary<Chest, SnapshotItemListDiff>();
diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/PlayerSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/PlayerSnapshot.cs
index 72f45a87..bf81a35e 100644
--- a/src/SMAPI/Framework/StateTracking/Snapshots/PlayerSnapshot.cs
+++ b/src/SMAPI/Framework/StateTracking/Snapshots/PlayerSnapshot.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,7 +16,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots
** Fields
*********/
/// <summary>An empty item list diff.</summary>
- private readonly SnapshotItemListDiff EmptyItemListDiff = new SnapshotItemListDiff(Array.Empty<Item>(), Array.Empty<Item>(), Array.Empty<ItemStackSizeChange>());
+ private readonly SnapshotItemListDiff EmptyItemListDiff = new(Array.Empty<Item>(), Array.Empty<Item>(), Array.Empty<ItemStackSizeChange>());
/*********
@@ -24,14 +26,14 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots
public Farmer Player { get; }
/// <summary>The player's current location.</summary>
- public SnapshotDiff<GameLocation> Location { get; } = new SnapshotDiff<GameLocation>();
+ public SnapshotDiff<GameLocation> Location { get; } = new();
/// <summary>Tracks changes to the player's skill levels.</summary>
public IDictionary<SkillType, SnapshotDiff<int>> Skills { get; } =
Enum
.GetValues(typeof(SkillType))
.Cast<SkillType>()
- .ToDictionary(skill => skill, skill => new SnapshotDiff<int>());
+ .ToDictionary(skill => skill, _ => new SnapshotDiff<int>());
/// <summary>Get a list of inventory changes.</summary>
public SnapshotItemListDiff Inventory { get; private set; }
diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/WatcherSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/WatcherSnapshot.cs
index cf51e040..1d43ef26 100644
--- a/src/SMAPI/Framework/StateTracking/Snapshots/WatcherSnapshot.cs
+++ b/src/SMAPI/Framework/StateTracking/Snapshots/WatcherSnapshot.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Microsoft.Xna.Framework;
using StardewValley;
using StardewValley.Menus;
@@ -11,31 +13,31 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots
** Accessors
*********/
/// <summary>Tracks changes to the window size.</summary>
- public SnapshotDiff<Point> WindowSize { get; } = new SnapshotDiff<Point>();
+ public SnapshotDiff<Point> WindowSize { get; } = new();
/// <summary>Tracks changes to the current player.</summary>
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 SnapshotDiff<int>();
+ public SnapshotDiff<int> Time { get; } = new();
/// <summary>Tracks changes to the save ID.</summary>
- public SnapshotDiff<ulong> SaveID { get; } = new SnapshotDiff<ulong>();
+ public SnapshotDiff<ulong> SaveID { get; } = new();
/// <summary>Tracks changes to the game's locations.</summary>
- public WorldLocationsSnapshot Locations { get; } = new WorldLocationsSnapshot();
+ public WorldLocationsSnapshot Locations { get; } = new();
/// <summary>Tracks changes to <see cref="Game1.activeClickableMenu"/>.</summary>
- public SnapshotDiff<IClickableMenu> ActiveMenu { get; } = new SnapshotDiff<IClickableMenu>();
+ public SnapshotDiff<IClickableMenu> ActiveMenu { get; } = new();
/// <summary>Tracks changes to the cursor position.</summary>
- public SnapshotDiff<ICursorPosition> Cursor { get; } = new SnapshotDiff<ICursorPosition>();
+ public SnapshotDiff<ICursorPosition> Cursor { get; } = new();
/// <summary>Tracks changes to the mouse wheel scroll.</summary>
- public SnapshotDiff<int> MouseWheelScroll { get; } = new SnapshotDiff<int>();
+ public SnapshotDiff<int> MouseWheelScroll { get; } = new();
/// <summary>Tracks changes to the content locale.</summary>
- public SnapshotDiff<LocalizedContentManager.LanguageCode> Locale { get; } = new SnapshotDiff<LocalizedContentManager.LanguageCode>();
+ public SnapshotDiff<LocalizedContentManager.LanguageCode> Locale { get; } = new();
/*********
diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs
index 73ed2d8f..88aac0df 100644
--- a/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs
+++ b/src/SMAPI/Framework/StateTracking/Snapshots/WorldLocationsSnapshot.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Linq;
using StardewModdingAPI.Framework.StateTracking.Comparers;
@@ -12,14 +14,14 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots
** Fields
*********/
/// <summary>A map of tracked locations.</summary>
- private readonly Dictionary<GameLocation, LocationSnapshot> LocationsDict = new Dictionary<GameLocation, LocationSnapshot>(new ObjectReferenceComparer<GameLocation>());
+ private readonly Dictionary<GameLocation, LocationSnapshot> LocationsDict = new(new ObjectReferenceComparer<GameLocation>());
/*********
** Accessors
*********/
/// <summary>Tracks changes to the location list.</summary>
- public SnapshotListDiff<GameLocation> LocationList { get; } = new SnapshotListDiff<GameLocation>();
+ public SnapshotListDiff<GameLocation> LocationList { get; } = new();
/// <summary>The tracked locations.</summary>
public IEnumerable<LocationSnapshot> Locations => this.LocationsDict.Values;
diff --git a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs
index e968d79c..ab02d7d5 100644
--- a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs
+++ b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
diff --git a/src/SMAPI/Framework/TemporaryHacks/MiniMonoModHotfix.cs b/src/SMAPI/Framework/TemporaryHacks/MiniMonoModHotfix.cs
index 173438f1..5f0ecfa0 100644
--- a/src/SMAPI/Framework/TemporaryHacks/MiniMonoModHotfix.cs
+++ b/src/SMAPI/Framework/TemporaryHacks/MiniMonoModHotfix.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
// This temporary utility fixes an esoteric issue in XNA Framework where deserialization depends on
// the order of fields returned by Type.GetFields, but that order changes after Harmony/MonoMod use
// reflection to access the fields due to an issue in .NET Framework.
@@ -55,7 +57,7 @@ namespace MonoMod.Utils
.GetType("System.RuntimeType+RuntimeTypeCache")
?.GetMethod("GetPropertyList", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
- private static readonly ConditionalWeakTable<Type, CacheFixEntry> _CacheFixed = new ConditionalWeakTable<Type, CacheFixEntry>();
+ private static readonly ConditionalWeakTable<Type, CacheFixEntry> _CacheFixed = new();
public static void Apply()
{
@@ -126,7 +128,7 @@ namespace MonoMod.Utils
}
public static Type GetRealDeclaringType(this MemberInfo member)
- => member.DeclaringType ?? member.Module?.GetModuleType();
+ => member.DeclaringType ?? member.Module.GetModuleType();
public static void FixReflectionCache(this Type type)
{
@@ -144,7 +146,7 @@ namespace MonoMod.Utils
continue;
CacheFixEntry entry = _CacheFixed.GetValue(type, rt => {
- CacheFixEntry entryNew = new CacheFixEntry();
+ CacheFixEntry entryNew = new();
object cache;
Array properties, fields;
diff --git a/src/SMAPI/Framework/Translator.cs b/src/SMAPI/Framework/Translator.cs
index 4492b17f..144b043c 100644
--- a/src/SMAPI/Framework/Translator.cs
+++ b/src/SMAPI/Framework/Translator.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/SMAPI/Framework/Utilities/ContextHash.cs b/src/SMAPI/Framework/Utilities/ContextHash.cs
index 6c0fdc90..46b9099e 100644
--- a/src/SMAPI/Framework/Utilities/ContextHash.cs
+++ b/src/SMAPI/Framework/Utilities/ContextHash.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
diff --git a/src/SMAPI/Framework/Utilities/Countdown.cs b/src/SMAPI/Framework/Utilities/Countdown.cs
index 342b4258..94c69e73 100644
--- a/src/SMAPI/Framework/Utilities/Countdown.cs
+++ b/src/SMAPI/Framework/Utilities/Countdown.cs
@@ -1,4 +1,4 @@
-namespace StardewModdingAPI.Framework.Utilities
+namespace StardewModdingAPI.Framework.Utilities
{
/// <summary>Counts down from a baseline value.</summary>
internal class Countdown
diff --git a/src/SMAPI/Framework/Utilities/TickCacheDictionary.cs b/src/SMAPI/Framework/Utilities/TickCacheDictionary.cs
index 1613a480..94ce0069 100644
--- a/src/SMAPI/Framework/Utilities/TickCacheDictionary.cs
+++ b/src/SMAPI/Framework/Utilities/TickCacheDictionary.cs
@@ -1,6 +1,7 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
-using StardewValley;
namespace StardewModdingAPI.Framework.Utilities
{
@@ -13,7 +14,7 @@ namespace StardewModdingAPI.Framework.Utilities
** Fields
*********/
/// <summary>The last game tick for which data was cached.</summary>
- private int LastGameTick = -1;
+ private uint? LastGameTick;
/// <summary>The underlying cached data.</summary>
private readonly Dictionary<TKey, TValue> Cache = new();
@@ -28,10 +29,10 @@ namespace StardewModdingAPI.Framework.Utilities
public TValue GetOrSet(TKey cacheKey, Func<TValue> get)
{
// clear cache on new tick
- if (Game1.ticks != this.LastGameTick)
+ if (SCore.ProcessTicksElapsed != this.LastGameTick)
{
this.Cache.Clear();
- this.LastGameTick = Game1.ticks;
+ this.LastGameTick = SCore.ProcessTicksElapsed;
}
// fetch value
diff --git a/src/SMAPI/Framework/WatcherCore.cs b/src/SMAPI/Framework/WatcherCore.cs
index 62a0c3b8..bd8d3367 100644
--- a/src/SMAPI/Framework/WatcherCore.cs
+++ b/src/SMAPI/Framework/WatcherCore.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
@@ -17,7 +19,7 @@ namespace StardewModdingAPI.Framework
** Fields
*********/
/// <summary>The underlying watchers for convenience. These are accessible individually as separate properties.</summary>
- private readonly List<IWatcher> Watchers = new List<IWatcher>();
+ private readonly List<IWatcher> Watchers = new();
/*********
diff --git a/src/SMAPI/GamePlatform.cs b/src/SMAPI/GamePlatform.cs
index cce5ed8d..8013faa9 100644
--- a/src/SMAPI/GamePlatform.cs
+++ b/src/SMAPI/GamePlatform.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using StardewModdingAPI.Toolkit.Utilities;
namespace StardewModdingAPI
diff --git a/src/SMAPI/IAssetData.cs b/src/SMAPI/IAssetData.cs
index 8df59e53..f07340e4 100644
--- a/src/SMAPI/IAssetData.cs
+++ b/src/SMAPI/IAssetData.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI
diff --git a/src/SMAPI/IAssetDataForDictionary.cs b/src/SMAPI/IAssetDataForDictionary.cs
index 1136316f..82ba25cb 100644
--- a/src/SMAPI/IAssetDataForDictionary.cs
+++ b/src/SMAPI/IAssetDataForDictionary.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
namespace StardewModdingAPI
diff --git a/src/SMAPI/IAssetDataForImage.cs b/src/SMAPI/IAssetDataForImage.cs
index 27ed9267..388caa68 100644
--- a/src/SMAPI/IAssetDataForImage.cs
+++ b/src/SMAPI/IAssetDataForImage.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
diff --git a/src/SMAPI/IAssetDataForMap.cs b/src/SMAPI/IAssetDataForMap.cs
index 47a33de8..89ee28f2 100644
--- a/src/SMAPI/IAssetDataForMap.cs
+++ b/src/SMAPI/IAssetDataForMap.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using Microsoft.Xna.Framework;
using xTile;
diff --git a/src/SMAPI/IAssetEditor.cs b/src/SMAPI/IAssetEditor.cs
index 9f22ed83..f3d91bd0 100644
--- a/src/SMAPI/IAssetEditor.cs
+++ b/src/SMAPI/IAssetEditor.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/IAssetInfo.cs b/src/SMAPI/IAssetInfo.cs
index 64d10b35..5b4ac479 100644
--- a/src/SMAPI/IAssetInfo.cs
+++ b/src/SMAPI/IAssetInfo.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI
diff --git a/src/SMAPI/IAssetLoader.cs b/src/SMAPI/IAssetLoader.cs
index 96b98793..0d52a481 100644
--- a/src/SMAPI/IAssetLoader.cs
+++ b/src/SMAPI/IAssetLoader.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/IAssetName.cs b/src/SMAPI/IAssetName.cs
index c91da266..22f5c6b7 100644
--- a/src/SMAPI/IAssetName.cs
+++ b/src/SMAPI/IAssetName.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewValley;
diff --git a/src/SMAPI/ICommandHelper.cs b/src/SMAPI/ICommandHelper.cs
index 9f1c345c..a0c524d6 100644
--- a/src/SMAPI/ICommandHelper.cs
+++ b/src/SMAPI/ICommandHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI
diff --git a/src/SMAPI/IContentHelper.cs b/src/SMAPI/IContentHelper.cs
index 48f6bfd8..0ad209ab 100644
--- a/src/SMAPI/IContentHelper.cs
+++ b/src/SMAPI/IContentHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
diff --git a/src/SMAPI/IContentPack.cs b/src/SMAPI/IContentPack.cs
index 3c66faff..f853e2b4 100644
--- a/src/SMAPI/IContentPack.cs
+++ b/src/SMAPI/IContentPack.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
diff --git a/src/SMAPI/IContentPackHelper.cs b/src/SMAPI/IContentPackHelper.cs
index c48a4f86..5464df22 100644
--- a/src/SMAPI/IContentPackHelper.cs
+++ b/src/SMAPI/IContentPackHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
namespace StardewModdingAPI
diff --git a/src/SMAPI/ICursorPosition.cs b/src/SMAPI/ICursorPosition.cs
index 99c1b84d..da6cbb62 100644
--- a/src/SMAPI/ICursorPosition.cs
+++ b/src/SMAPI/ICursorPosition.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework;
using StardewValley;
diff --git a/src/SMAPI/IDataHelper.cs b/src/SMAPI/IDataHelper.cs
index 901266d7..4c96367b 100644
--- a/src/SMAPI/IDataHelper.cs
+++ b/src/SMAPI/IDataHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI
diff --git a/src/SMAPI/IGameContentHelper.cs b/src/SMAPI/IGameContentHelper.cs
index 86bc3e0e..4b967993 100644
--- a/src/SMAPI/IGameContentHelper.cs
+++ b/src/SMAPI/IGameContentHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
diff --git a/src/SMAPI/IInputHelper.cs b/src/SMAPI/IInputHelper.cs
index 2b907b0d..b7ed0838 100644
--- a/src/SMAPI/IInputHelper.cs
+++ b/src/SMAPI/IInputHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using StardewModdingAPI.Utilities;
namespace StardewModdingAPI
diff --git a/src/SMAPI/IMod.cs b/src/SMAPI/IMod.cs
index 44ef32c9..0de4961e 100644
--- a/src/SMAPI/IMod.cs
+++ b/src/SMAPI/IMod.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI
{
/// <summary>The implementation for a Stardew Valley mod.</summary>
diff --git a/src/SMAPI/IModContentHelper.cs b/src/SMAPI/IModContentHelper.cs
index e3431365..815d6848 100644
--- a/src/SMAPI/IModContentHelper.cs
+++ b/src/SMAPI/IModContentHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
diff --git a/src/SMAPI/IModHelper.cs b/src/SMAPI/IModHelper.cs
index 15e4ed8d..5e4246aa 100644
--- a/src/SMAPI/IModHelper.cs
+++ b/src/SMAPI/IModHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/IModInfo.cs b/src/SMAPI/IModInfo.cs
index 3c85d454..2788e4fc 100644
--- a/src/SMAPI/IModInfo.cs
+++ b/src/SMAPI/IModInfo.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI
{
/// <summary>Metadata for a loaded mod.</summary>
diff --git a/src/SMAPI/IModLinked.cs b/src/SMAPI/IModLinked.cs
index 172ee30c..cf08c9c5 100644
--- a/src/SMAPI/IModLinked.cs
+++ b/src/SMAPI/IModLinked.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI
{
/// <summary>An instance linked to a mod.</summary>
diff --git a/src/SMAPI/IModRegistry.cs b/src/SMAPI/IModRegistry.cs
index 10b3121e..9cab08a1 100644
--- a/src/SMAPI/IModRegistry.cs
+++ b/src/SMAPI/IModRegistry.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
namespace StardewModdingAPI
diff --git a/src/SMAPI/IMonitor.cs b/src/SMAPI/IMonitor.cs
index c400a211..535f56e3 100644
--- a/src/SMAPI/IMonitor.cs
+++ b/src/SMAPI/IMonitor.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI
{
/// <summary>Encapsulates monitoring and logging for a given module.</summary>
diff --git a/src/SMAPI/IMultiplayerHelper.cs b/src/SMAPI/IMultiplayerHelper.cs
index 4067a676..77a0f3f4 100644
--- a/src/SMAPI/IMultiplayerHelper.cs
+++ b/src/SMAPI/IMultiplayerHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using StardewValley;
diff --git a/src/SMAPI/IMultiplayerPeer.cs b/src/SMAPI/IMultiplayerPeer.cs
index 47084174..e487f100 100644
--- a/src/SMAPI/IMultiplayerPeer.cs
+++ b/src/SMAPI/IMultiplayerPeer.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
namespace StardewModdingAPI
diff --git a/src/SMAPI/IMultiplayerPeerMod.cs b/src/SMAPI/IMultiplayerPeerMod.cs
index 005408b1..81978bef 100644
--- a/src/SMAPI/IMultiplayerPeerMod.cs
+++ b/src/SMAPI/IMultiplayerPeerMod.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
namespace StardewModdingAPI
{
/// <summary>Metadata about a mod installed by a connected player.</summary>
diff --git a/src/SMAPI/IReflectedField.cs b/src/SMAPI/IReflectedField.cs
index 7ff61f29..94dbe6a3 100644
--- a/src/SMAPI/IReflectedField.cs
+++ b/src/SMAPI/IReflectedField.cs
@@ -1,4 +1,6 @@
-using System.Reflection;
+#nullable disable
+
+using System.Reflection;
namespace StardewModdingAPI
{
diff --git a/src/SMAPI/IReflectedMethod.cs b/src/SMAPI/IReflectedMethod.cs
index 646e7301..78e66cb1 100644
--- a/src/SMAPI/IReflectedMethod.cs
+++ b/src/SMAPI/IReflectedMethod.cs
@@ -1,4 +1,6 @@
-using System.Reflection;
+#nullable disable
+
+using System.Reflection;
namespace StardewModdingAPI
{
diff --git a/src/SMAPI/IReflectedProperty.cs b/src/SMAPI/IReflectedProperty.cs
index 73ad9f30..edbf0b21 100644
--- a/src/SMAPI/IReflectedProperty.cs
+++ b/src/SMAPI/IReflectedProperty.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Reflection;
namespace StardewModdingAPI
diff --git a/src/SMAPI/IReflectionHelper.cs b/src/SMAPI/IReflectionHelper.cs
index a2b9eb32..bf7270cf 100644
--- a/src/SMAPI/IReflectionHelper.cs
+++ b/src/SMAPI/IReflectionHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI
diff --git a/src/SMAPI/ITranslationHelper.cs b/src/SMAPI/ITranslationHelper.cs
index b30d9b14..3c297731 100644
--- a/src/SMAPI/ITranslationHelper.cs
+++ b/src/SMAPI/ITranslationHelper.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using StardewValley;
diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs
index 424abc18..b7cec72c 100644
--- a/src/SMAPI/Metadata/CoreAssetPropagator.cs
+++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -1238,6 +1240,9 @@ namespace StardewModdingAPI.Metadata
/// <param name="right">The second value to compare.</param>
private bool IsSameBaseName(IAssetName left, string right)
{
+ if (left is null || right is null)
+ return false;
+
IAssetName parsedB = this.ParseAssetNameOrNull(right);
return this.IsSameBaseName(left, parsedB);
}
@@ -1247,6 +1252,9 @@ namespace StardewModdingAPI.Metadata
/// <param name="right">The second value to compare.</param>
private bool IsSameBaseName(IAssetName left, IAssetName right)
{
+ if (left is null || right is null)
+ return false;
+
return left.IsEquivalentTo(right.BaseName, useBaseName: true);
}
diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs
index 367372b2..5617fd13 100644
--- a/src/SMAPI/Metadata/InstructionMetadata.cs
+++ b/src/SMAPI/Metadata/InstructionMetadata.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Events;
diff --git a/src/SMAPI/Mod.cs b/src/SMAPI/Mod.cs
index 9af55cd4..2b3750d5 100644
--- a/src/SMAPI/Mod.cs
+++ b/src/SMAPI/Mod.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
namespace StardewModdingAPI
diff --git a/src/SMAPI/Patches/Game1Patcher.cs b/src/SMAPI/Patches/Game1Patcher.cs
index 173a2055..c5d98e9e 100644
--- a/src/SMAPI/Patches/Game1Patcher.cs
+++ b/src/SMAPI/Patches/Game1Patcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
diff --git a/src/SMAPI/Patches/TitleMenuPatcher.cs b/src/SMAPI/Patches/TitleMenuPatcher.cs
index b4320ce0..56e5597c 100644
--- a/src/SMAPI/Patches/TitleMenuPatcher.cs
+++ b/src/SMAPI/Patches/TitleMenuPatcher.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index 1e3b2000..0c9c2d87 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.IO;
@@ -78,7 +80,7 @@ namespace StardewModdingAPI
}
catch
{
- continue;
+ // ignore invalid DLL
}
}
}
@@ -206,7 +208,7 @@ namespace StardewModdingAPI
}
// load SMAPI
- using SCore core = new SCore(modsPath, writeToConsole, developerMode);
+ using SCore core = new(modsPath, writeToConsole, developerMode);
core.RunInteractively();
}
diff --git a/src/SMAPI/SButton.cs b/src/SMAPI/SButton.cs
index ae825696..133ea901 100644
--- a/src/SMAPI/SButton.cs
+++ b/src/SMAPI/SButton.cs
@@ -674,7 +674,7 @@ namespace StardewModdingAPI
}
// mouse
- if (input == SButton.MouseLeft || input == SButton.MouseRight)
+ if (input is SButton.MouseLeft or SButton.MouseRight)
{
button = new InputButton(mouseLeft: input == SButton.MouseLeft);
return true;
diff --git a/src/SMAPI/SButtonState.cs b/src/SMAPI/SButtonState.cs
index 5f3e8d3c..ca2dd83d 100644
--- a/src/SMAPI/SButtonState.cs
+++ b/src/SMAPI/SButtonState.cs
@@ -23,7 +23,7 @@ namespace StardewModdingAPI
/// <param name="state">The button state.</param>
public static bool IsDown(this SButtonState state)
{
- return state == SButtonState.Held || state == SButtonState.Pressed;
+ return state is SButtonState.Held or SButtonState.Pressed;
}
}
}
diff --git a/src/SMAPI/SMAPI.config.json b/src/SMAPI/SMAPI.config.json
index e62c8880..49056e83 100644
--- a/src/SMAPI/SMAPI.config.json
+++ b/src/SMAPI/SMAPI.config.json
@@ -70,11 +70,6 @@ copy all the settings, or you may cause bugs due to overridden changes in future
"GitHubProjectName": "Pathoschild/SMAPI",
/**
- * Stardew64Installer's GitHub project name, used to perform update checks.
- */
- "Stardew64InstallerGitHubProjectName": "Steviegt6/Stardew64Installer",
-
- /**
* The base URL for SMAPI's web API, used to perform update checks.
*/
"WebApiBaseUrl": "https://smapi.io/api/",
diff --git a/src/SMAPI/SemanticVersion.cs b/src/SMAPI/SemanticVersion.cs
index ae616419..7a6cdcdd 100644
--- a/src/SMAPI/SemanticVersion.cs
+++ b/src/SMAPI/SemanticVersion.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace StardewModdingAPI
@@ -26,10 +27,10 @@ namespace StardewModdingAPI
public int PatchVersion => this.Version.PatchVersion;
/// <inheritdoc />
- public string PrereleaseTag => this.Version.PrereleaseTag;
+ public string? PrereleaseTag => this.Version.PrereleaseTag;
/// <inheritdoc />
- public string BuildMetadata => this.Version.BuildMetadata;
+ public string? BuildMetadata => this.Version.BuildMetadata;
/*********
@@ -41,7 +42,7 @@ namespace StardewModdingAPI
/// <param name="patchVersion">The patch version for backwards-compatible bug fixes.</param>
/// <param name="prereleaseTag">An optional prerelease tag.</param>
/// <param name="buildMetadata">Optional build metadata. This is ignored when determining version precedence.</param>
- public SemanticVersion(int majorVersion, int minorVersion, int patchVersion, string prereleaseTag = null, string buildMetadata = null)
+ public SemanticVersion(int majorVersion, int minorVersion, int patchVersion, string? prereleaseTag = null, string? buildMetadata = null)
: this(majorVersion, minorVersion, patchVersion, 0, prereleaseTag, buildMetadata) { }
/// <summary>Construct an instance.</summary>
@@ -52,7 +53,7 @@ namespace StardewModdingAPI
/// <param name="platformRelease">The platform-specific version (if applicable).</param>
/// <param name="buildMetadata">Optional build metadata. This is ignored when determining version precedence.</param>
[JsonConstructor]
- internal SemanticVersion(int majorVersion, int minorVersion, int patchVersion, int platformRelease, string prereleaseTag = null, string buildMetadata = null)
+ internal SemanticVersion(int majorVersion, int minorVersion, int patchVersion, int platformRelease, string? prereleaseTag = null, string? buildMetadata = null)
: this(new Toolkit.SemanticVersion(majorVersion, minorVersion, patchVersion, platformRelease, prereleaseTag, buildMetadata)) { }
/// <summary>Construct an instance.</summary>
@@ -91,49 +92,49 @@ namespace StardewModdingAPI
/// <inheritdoc />
/// <remarks>The implementation is defined by Semantic Version 2.0 (https://semver.org/).</remarks>
- public int CompareTo(ISemanticVersion other)
+ public int CompareTo(ISemanticVersion? other)
{
return this.Version.CompareTo(other);
}
/// <inheritdoc />
- public bool IsOlderThan(ISemanticVersion other)
+ public bool IsOlderThan(ISemanticVersion? other)
{
return this.Version.IsOlderThan(other);
}
/// <inheritdoc />
- public bool IsOlderThan(string other)
+ public bool IsOlderThan(string? other)
{
return this.Version.IsOlderThan(other);
}
/// <inheritdoc />
- public bool IsNewerThan(ISemanticVersion other)
+ public bool IsNewerThan(ISemanticVersion? other)
{
return this.Version.IsNewerThan(other);
}
/// <inheritdoc />
- public bool IsNewerThan(string other)
+ public bool IsNewerThan(string? other)
{
return this.Version.IsNewerThan(other);
}
/// <inheritdoc />
- public bool IsBetween(ISemanticVersion min, ISemanticVersion max)
+ public bool IsBetween(ISemanticVersion? min, ISemanticVersion? max)
{
return this.Version.IsBetween(min, max);
}
/// <inheritdoc />
- public bool IsBetween(string min, string max)
+ public bool IsBetween(string? min, string? max)
{
return this.Version.IsBetween(min, max);
}
/// <inheritdoc />
- public bool Equals(ISemanticVersion other)
+ public bool Equals(ISemanticVersion? other)
{
return other != null && this.CompareTo(other) == 0;
}
@@ -154,9 +155,9 @@ namespace StardewModdingAPI
/// <param name="version">The version string.</param>
/// <param name="parsed">The parsed representation.</param>
/// <returns>Returns whether parsing the version succeeded.</returns>
- public static bool TryParse(string version, out ISemanticVersion parsed)
+ public static bool TryParse(string version, [NotNullWhen(true)] out ISemanticVersion? parsed)
{
- if (Toolkit.SemanticVersion.TryParse(version, out ISemanticVersion versionImpl))
+ if (Toolkit.SemanticVersion.TryParse(version, out ISemanticVersion? versionImpl))
{
parsed = new SemanticVersion(versionImpl);
return true;
diff --git a/src/SMAPI/Translation.cs b/src/SMAPI/Translation.cs
index 149f6728..ef98a00f 100644
--- a/src/SMAPI/Translation.cs
+++ b/src/SMAPI/Translation.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections;
using System.Collections.Generic;
@@ -74,7 +76,7 @@ namespace StardewModdingAPI
{
foreach (DictionaryEntry entry in inputLookup)
{
- string key = entry.Key?.ToString().Trim();
+ string key = entry.Key.ToString()?.Trim();
if (key != null)
tokenLookup[key] = entry.Value?.ToString();
}
diff --git a/src/SMAPI/Utilities/CaseInsensitivePathCache.cs b/src/SMAPI/Utilities/CaseInsensitivePathCache.cs
new file mode 100644
index 00000000..4596fdce
--- /dev/null
+++ b/src/SMAPI/Utilities/CaseInsensitivePathCache.cs
@@ -0,0 +1,126 @@
+#nullable disable
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace StardewModdingAPI.Utilities
+{
+ /// <summary>Provides an API for case-insensitive relative path lookups within a root directory.</summary>
+ internal class CaseInsensitivePathCache
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>The root directory path for relative paths.</summary>
+ private readonly string RootPath;
+
+ /// <summary>A case-insensitive lookup of file paths within the <see cref="RootPath"/>. Each path is listed in both file path and asset name format, so it's usable in both contexts without needing to re-parse paths.</summary>
+ private readonly Lazy<Dictionary<string, string>> RelativePathCache;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="rootPath">The root directory path for relative paths.</param>
+ public CaseInsensitivePathCache(string rootPath)
+ {
+ this.RootPath = rootPath;
+ this.RelativePathCache = new(this.GetRelativePathCache);
+ }
+
+ /// <summary>Get the exact capitalization for a given relative file path.</summary>
+ /// <param name="relativePath">The relative path.</param>
+ /// <remarks>Returns the resolved path in file path format, else the normalized <paramref name="relativePath"/>.</remarks>
+ public string GetFilePath(string relativePath)
+ {
+ return this.GetImpl(PathUtilities.NormalizePath(relativePath));
+ }
+
+ /// <summary>Get the exact capitalization for a given asset name.</summary>
+ /// <param name="relativePath">The relative path.</param>
+ /// <remarks>Returns the resolved path in asset name format, else the normalized <paramref name="relativePath"/>.</remarks>
+ public string GetAssetName(string relativePath)
+ {
+ return this.GetImpl(PathUtilities.NormalizeAssetName(relativePath));
+ }
+
+ /// <summary>Add a relative path that was just created by a SMAPI API.</summary>
+ /// <param name="relativePath">The relative path. This must already be normalized in asset name or file path format.</param>
+ public void Add(string relativePath)
+ {
+ // skip if cache isn't created yet (no need to add files manually in that case)
+ if (!this.RelativePathCache.IsValueCreated)
+ return;
+
+ // skip if already cached
+ if (this.RelativePathCache.Value.ContainsKey(relativePath))
+ return;
+
+ // make sure path exists
+ relativePath = PathUtilities.NormalizePath(relativePath);
+ if (!File.Exists(Path.Combine(this.RootPath, relativePath)))
+ throw new InvalidOperationException($"Can't add relative path '{relativePath}' to the case-insensitive cache for '{this.RootPath}' because that file doesn't exist.");
+
+ // cache path
+ this.CacheRawPath(this.RelativePathCache.Value, relativePath);
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Get the exact capitalization for a given relative path.</summary>
+ /// <param name="relativePath">The relative path. This must already be normalized into asset name or file path format (i.e. using <see cref="PathUtilities.NormalizeAssetName"/> or <see cref="PathUtilities.NormalizePath"/> respectively).</param>
+ /// <remarks>Returns the resolved path in the same format if found, else returns the path as-is.</remarks>
+ private string GetImpl(string relativePath)
+ {
+ // invalid path
+ if (string.IsNullOrWhiteSpace(relativePath))
+ return relativePath;
+
+ // already cached
+ if (this.RelativePathCache.Value.TryGetValue(relativePath, out string resolved))
+ return resolved;
+
+ // file exists but isn't cached for some reason
+ // cache it now so any later references to it are case-insensitive
+ if (File.Exists(Path.Combine(this.RootPath, relativePath)))
+ {
+ this.CacheRawPath(this.RelativePathCache.Value, relativePath);
+ return relativePath;
+ }
+
+ // no such file, keep capitalization as-is
+ return relativePath;
+ }
+
+ /// <summary>Get a case-insensitive lookup of file paths (see <see cref="RelativePathCache"/>).</summary>
+ private Dictionary<string, string> GetRelativePathCache()
+ {
+ Dictionary<string, string> cache = new(StringComparer.OrdinalIgnoreCase);
+
+ foreach (string path in Directory.EnumerateFiles(this.RootPath, "*", SearchOption.AllDirectories))
+ {
+ string relativePath = path.Substring(this.RootPath.Length + 1);
+
+ this.CacheRawPath(cache, relativePath);
+ }
+
+ return cache;
+ }
+
+ /// <summary>Add a raw relative path to the cache.</summary>
+ /// <param name="cache">The cache to update.</param>
+ /// <param name="relativePath">The relative path to cache, with its exact filesystem capitalization.</param>
+ private void CacheRawPath(IDictionary<string, string> cache, string relativePath)
+ {
+ string filePath = PathUtilities.NormalizePath(relativePath);
+ string assetName = PathUtilities.NormalizeAssetName(relativePath);
+
+ cache[filePath] = filePath;
+ cache[assetName] = assetName;
+ }
+ }
+}
diff --git a/src/SMAPI/Utilities/Keybind.cs b/src/SMAPI/Utilities/Keybind.cs
index 403ecf4a..7b1acf1d 100644
--- a/src/SMAPI/Utilities/Keybind.cs
+++ b/src/SMAPI/Utilities/Keybind.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -118,11 +120,11 @@ namespace StardewModdingAPI.Utilities
return SButtonState.None;
// mix of held + pressed => pressed
- if (states.All(p => p == SButtonState.Pressed || p == SButtonState.Held))
+ if (states.All(p => p is SButtonState.Pressed or SButtonState.Held))
return SButtonState.Pressed;
// mix of held + released => released
- if (states.All(p => p == SButtonState.Held || p == SButtonState.Released))
+ if (states.All(p => p is SButtonState.Held or SButtonState.Released))
return SButtonState.Released;
// not down last tick or now
diff --git a/src/SMAPI/Utilities/KeybindList.cs b/src/SMAPI/Utilities/KeybindList.cs
index f8f569af..7b2c396b 100644
--- a/src/SMAPI/Utilities/KeybindList.cs
+++ b/src/SMAPI/Utilities/KeybindList.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -139,7 +141,7 @@ namespace StardewModdingAPI.Utilities
public bool IsDown()
{
SButtonState state = this.GetState();
- return state == SButtonState.Pressed || state == SButtonState.Held;
+ return state is SButtonState.Pressed or SButtonState.Held;
}
/// <summary>Get whether the input binding was just pressed this tick.</summary>
diff --git a/src/SMAPI/Utilities/PathUtilities.cs b/src/SMAPI/Utilities/PathUtilities.cs
index 541b163c..4350f441 100644
--- a/src/SMAPI/Utilities/PathUtilities.cs
+++ b/src/SMAPI/Utilities/PathUtilities.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using ToolkitPathUtilities = StardewModdingAPI.Toolkit.Utilities.PathUtilities;
@@ -20,14 +21,16 @@ namespace StardewModdingAPI.Utilities
/// <param name="path">The path to split.</param>
/// <param name="limit">The number of segments to match. Any additional segments will be merged into the last returned part.</param>
[Pure]
- public static string[] GetSegments(string path, int? limit = null)
+ public static string[] GetSegments(string? path, int? limit = null)
{
return ToolkitPathUtilities.GetSegments(path, limit);
}
/// <summary>Normalize an asset name to match how MonoGame's content APIs would normalize and cache it.</summary>
/// <param name="assetName">The asset name to normalize.</param>
- public static string NormalizeAssetName(string assetName)
+ [Pure]
+ [return: NotNullIfNotNull("assetName")]
+ public static string? NormalizeAssetName(string? assetName)
{
return ToolkitPathUtilities.NormalizeAssetName(assetName);
}
@@ -36,7 +39,8 @@ namespace StardewModdingAPI.Utilities
/// <param name="path">The file path to normalize.</param>
/// <remarks>This should only be used for file paths. For asset names, use <see cref="NormalizeAssetName"/> instead.</remarks>
[Pure]
- public static string NormalizePath(string path)
+ [return: NotNullIfNotNull("path")]
+ public static string? NormalizePath(string? path)
{
return ToolkitPathUtilities.NormalizePath(path);
}
@@ -44,7 +48,7 @@ namespace StardewModdingAPI.Utilities
/// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary>
/// <param name="path">The path to check.</param>
[Pure]
- public static bool IsSafeRelativePath(string path)
+ public static bool IsSafeRelativePath(string? path)
{
return ToolkitPathUtilities.IsSafeRelativePath(path);
}
@@ -52,7 +56,7 @@ namespace StardewModdingAPI.Utilities
/// <summary>Get whether a string is a valid 'slug', containing only basic characters that are safe in all contexts (e.g. filenames, URLs, etc).</summary>
/// <param name="str">The string to check.</param>
[Pure]
- public static bool IsSlug(string str)
+ public static bool IsSlug(string? str)
{
return ToolkitPathUtilities.IsSlug(str);
}
diff --git a/src/SMAPI/Utilities/PerScreen.cs b/src/SMAPI/Utilities/PerScreen.cs
index 20b8fbce..afe3ba91 100644
--- a/src/SMAPI/Utilities/PerScreen.cs
+++ b/src/SMAPI/Utilities/PerScreen.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -76,7 +78,7 @@ namespace StardewModdingAPI.Utilities
/// <summary>Remove all active values.</summary>
public void ResetAllScreens()
{
- this.RemoveScreens(p => true);
+ this.RemoveScreens(_ => true);
}
diff --git a/src/SMAPI/Utilities/SDate.cs b/src/SMAPI/Utilities/SDate.cs
index e10a59f8..b10bc3da 100644
--- a/src/SMAPI/Utilities/SDate.cs
+++ b/src/SMAPI/Utilities/SDate.cs
@@ -1,3 +1,5 @@
+#nullable disable
+
using System;
using System.Linq;
using Newtonsoft.Json;