add flag to disable deprecated code

This commit is contained in:
Jesse Plamondon-Willard 2022-06-20 18:01:48 -04:00
parent e0ef8a20a5
commit dab1ef6acc
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
26 changed files with 173 additions and 55 deletions

View File

@ -5,6 +5,7 @@
<Product>SMAPI</Product>
<LangVersion>latest</LangVersion>
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
<DefineConstants>$(DefineConstants);SMAPI_DEPRECATED</DefineConstants>
<!--enable nullable annotations, except in .NET Standard 2.0 where they aren't supported-->
<Nullable Condition="'$(TargetFramework)' != 'netstandard2.0'">enable</Nullable>
@ -21,12 +22,15 @@
suppress warnings that don't apply, so it's easier to spot actual issues.
warning | builds | summary | rationale
┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ | ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
CS0436 | all | local type conflicts with imported type | SMAPI needs to use certain low-level code during very early compatibility checks, before it's safe to load any other DLLs.
CS0612 | deprecated | member is obsolete | internal references to deprecated code when deprecated code is enabled.
CS0618 | deprecated | member is obsolete (with message) | internal references to deprecated code when deprecated code is enabled.
CA1416 | all | platform code available on all platforms | Compiler doesn't recognize the #if constants used by SMAPI.
CS0809 | all | obsolete overload for non-onsolete member | This is deliberate to signal to mods that certain APIs are only implemented for the game and shouldn't be called by mods.
CS0809 | all | obsolete overload for non-obsolete member | This is deliberate to signal to mods that certain APIs are only implemented for the game and shouldn't be called by mods.
NU1701 | all | NuGet package targets older .NET version | All such packages are carefully tested to make sure they do work.
-->
<NoWarn Condition="$(DefineConstants.Contains(SMAPI_DEPRECATED))">$(NoWarn);CS0612;CS0618</NoWarn>
<NoWarn>$(NoWarn);CS0436;CA1416;CS0809;NU1701</NoWarn>
</PropertyGroup>

View File

@ -62,6 +62,7 @@ SMAPI uses a small number of conditional compilation constants, which you can se
flag | purpose
---- | -------
`SMAPI_FOR_WINDOWS` | Whether SMAPI is being compiled for Windows; if not set, the code assumes Linux/macOS. Set automatically in `common.targets`.
`SMAPI_DEPRECATED` | Whether to include deprecated code in the build.
## Compile from source code
### Main project

View File

@ -18,9 +18,11 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// <summary>The mod patches the game in a way that may impact stability.</summary>
PatchesGame = 4,
#if SMAPI_FOR_WINDOWS
/// <summary>The mod uses the <c>dynamic</c> keyword which won't work on Linux/macOS.</summary>
[Obsolete("This value is no longer used by SMAPI and will be removed in the upcoming SMAPI 4.0.0.")]
UsesDynamic = 8,
#endif
/// <summary>The mod references specialized 'unvalidated update tick' events which may impact stability.</summary>
UsesUnvalidatedUpdateTick = 16,
@ -37,6 +39,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// <summary>Uses .NET APIs for shell or process access.</summary>
AccessesShell = 256,
#if SMAPI_DEPRECATED
/// <summary>References the legacy <c>System.Configuration.ConfigurationManager</c> assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0.</summary>
DetectedLegacyConfigurationDll = 512,
@ -45,5 +48,6 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// <summary>References the legacy <c>System.Security.Permissions</c> assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0.</summary>
DetectedLegacyPermissionsDll = 2048
#endif
}
}

View File

@ -6,7 +6,9 @@ using System.Reflection;
using Mono.Cecil;
using StardewModdingAPI.Enums;
using StardewModdingAPI.Framework;
#if SMAPI_DEPRECATED
using StardewModdingAPI.Framework.Deprecations;
#endif
using StardewModdingAPI.Framework.ModLoading;
using StardewModdingAPI.Toolkit.Framework;
using StardewModdingAPI.Toolkit.Utilities;
@ -77,6 +79,7 @@ namespace StardewModdingAPI
/// <summary>The game framework running the game.</summary>
public static GameFramework GameFramework { get; } = EarlyConstants.GameFramework;
#if SMAPI_DEPRECATED
/// <summary>The path to the game folder.</summary>
[Obsolete($"Use {nameof(Constants)}.{nameof(GamePath)} instead. This property will be removed in SMAPI 4.0.0.")]
public static string ExecutionPath
@ -93,6 +96,7 @@ namespace StardewModdingAPI
return Constants.GamePath;
}
}
#endif
/// <summary>The path to the game folder.</summary>
public static string GamePath { get; } = EarlyConstants.GamePath;

View File

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
#if SMAPI_DEPRECATED
using StardewModdingAPI.Framework.Deprecations;
#endif
namespace StardewModdingAPI.Framework.Content
{
@ -29,6 +31,10 @@ namespace StardewModdingAPI.Framework.Content
/// <inheritdoc />
public IAssetName NameWithoutLocale => this.NameWithoutLocaleImpl ??= this.Name.GetBaseAssetName();
/// <inheritdoc />
public Type DataType { get; }
#if SMAPI_DEPRECATED
/// <inheritdoc />
[Obsolete($"Use {nameof(AssetInfo.Name)} or {nameof(AssetInfo.NameWithoutLocale)} instead. This property will be removed in SMAPI 4.0.0.")]
public string AssetName
@ -50,9 +56,7 @@ namespace StardewModdingAPI.Framework.Content
return this.NameWithoutLocale.Name;
}
}
/// <inheritdoc />
public Type DataType { get; }
#endif
/*********
@ -71,6 +75,7 @@ namespace StardewModdingAPI.Framework.Content
this.GetNormalizedPath = getNormalizedPath;
}
#if SMAPI_DEPRECATED
/// <inheritdoc />
[Obsolete($"Use {nameof(Name)}.{nameof(IAssetName.IsEquivalentTo)} or {nameof(AssetInfo.NameWithoutLocale)}.{nameof(IAssetName.IsEquivalentTo)} instead. This method will be removed in SMAPI 4.0.0.")]
public bool AssetNameEquals(string path)
@ -90,6 +95,7 @@ namespace StardewModdingAPI.Framework.Content
return this.NameWithoutLocale.IsEquivalentTo(path);
}
#endif
/*********

View File

@ -1,8 +1,8 @@
#if SMAPI_DEPRECATED
using System;
using System.Reflection;
using StardewModdingAPI.Internal;
#pragma warning disable CS0618 // obsolete asset interceptors deliberately supported here
namespace StardewModdingAPI.Framework.Content
{
/// <summary>A wrapper for <see cref="IAssetEditor"/> and <see cref="IAssetLoader"/> for internal cache invalidation.</summary>
@ -103,3 +103,4 @@ namespace StardewModdingAPI.Framework.Content
}
}
}
#endif

View File

@ -12,7 +12,9 @@ using StardewModdingAPI.Framework.Content;
using StardewModdingAPI.Framework.ContentManagers;
using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Framework.Utilities;
#if SMAPI_DEPRECATED
using StardewModdingAPI.Internal;
#endif
using StardewModdingAPI.Metadata;
using StardewModdingAPI.Toolkit.Serialization;
using StardewModdingAPI.Toolkit.Utilities.PathLookups;
@ -84,6 +86,7 @@ namespace StardewModdingAPI.Framework
/// <summary>The cached asset load/edit operations to apply, indexed by asset name.</summary>
private readonly TickCacheDictionary<IAssetName, AssetOperationGroup?> AssetOperationsByKey = new();
#if SMAPI_DEPRECATED
/// <summary>A cache of asset operation groups created for legacy <see cref="IAssetLoader"/> implementations.</summary>
[Obsolete("This only exists to support legacy code and will be removed in SMAPI 4.0.0.")]
private readonly Dictionary<IAssetLoader, Dictionary<Type, AssetLoadOperation>> LegacyLoaderCache = new(ReferenceEqualityComparer.Instance);
@ -91,6 +94,7 @@ namespace StardewModdingAPI.Framework
/// <summary>A cache of asset operation groups created for legacy <see cref="IAssetEditor"/> implementations.</summary>
[Obsolete("This only exists to support legacy code and will be removed in SMAPI 4.0.0.")]
private readonly Dictionary<IAssetEditor, Dictionary<Type, AssetEditOperation>> LegacyEditorCache = new(ReferenceEqualityComparer.Instance);
#endif
/*********
@ -102,6 +106,7 @@ namespace StardewModdingAPI.Framework
/// <summary>The current language as a constant.</summary>
public LocalizedContentManager.LanguageCode Language => this.MainContentManager.Language;
#if SMAPI_DEPRECATED
/// <summary>Interceptors which provide the initial versions of matching assets.</summary>
[Obsolete("This only exists to support legacy code and will be removed in SMAPI 4.0.0.")]
public IList<ModLinked<IAssetLoader>> Loaders { get; } = new List<ModLinked<IAssetLoader>>();
@ -109,6 +114,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Interceptors which edit matching assets after they're loaded.</summary>
[Obsolete("This only exists to support legacy code and will be removed in SMAPI 4.0.0.")]
public IList<ModLinked<IAssetEditor>> Editors { get; } = new List<ModLinked<IAssetEditor>>();
#endif
/// <summary>The absolute path to the <see cref="ContentManager.RootDirectory"/>.</summary>
public string FullRootDirectory { get; }
@ -498,15 +504,25 @@ namespace StardewModdingAPI.Framework
return invalidatedAssets.Keys;
}
#if SMAPI_DEPRECATED
/// <summary>Get the asset load and edit operations to apply to a given asset if it's (re)loaded now.</summary>
/// <typeparam name="T">The asset type.</typeparam>
/// <param name="info">The asset info to load or edit.</param>
public AssetOperationGroup? GetAssetOperations<T>(IAssetInfo info)
where T : notnull
#else
/// <summary>Get the asset load and edit operations to apply to a given asset if it's (re)loaded now.</summary>
/// <param name="info">The asset info to load or edit.</param>
public AssetOperationGroup? GetAssetOperations(IAssetInfo info)
#endif
{
return this.AssetOperationsByKey.GetOrSet(
info.Name,
#if SMAPI_DEPRECATED
() => this.GetAssetOperationsWithoutCache<T>(info)
#else
() => this.RequestAssetOperations(info)
#endif
);
}
@ -629,6 +645,7 @@ namespace StardewModdingAPI.Framework
return map;
}
#if SMAPI_DEPRECATED
/// <summary>Get the asset load and edit operations to apply to a given asset if it's (re)loaded now, ignoring the <see cref="AssetOperationsByKey"/> cache.</summary>
/// <typeparam name="T">The asset type.</typeparam>
/// <param name="info">The asset info to load or edit.</param>
@ -639,7 +656,6 @@ namespace StardewModdingAPI.Framework
AssetOperationGroup? group = this.RequestAssetOperations(info);
// legacy load operations
#pragma warning disable CS0612, CS0618 // deprecated code
if (this.Editors.Count > 0 || this.Loaders.Count > 0)
{
IAssetInfo legacyInfo = this.GetLegacyAssetInfo(info);
@ -738,7 +754,6 @@ namespace StardewModdingAPI.Framework
);
}
}
#pragma warning restore CS0612, CS0618
return group;
}
@ -818,5 +833,6 @@ namespace StardewModdingAPI.Framework
// else no change needed
return asset;
}
#endif
}
}

View File

@ -76,7 +76,11 @@ namespace StardewModdingAPI.Framework.ContentManagers
// custom asset from a loader
string locale = this.GetLocale();
IAssetInfo info = new AssetInfo(locale, assetName, typeof(T), this.AssertAndNormalizeAssetName);
AssetOperationGroup? operations = this.Coordinator.GetAssetOperations<T>(info);
AssetOperationGroup? operations = this.Coordinator.GetAssetOperations
#if SMAPI_DEPRECATED
<T>
#endif
(info);
if (operations?.LoadOperations.Count > 0)
{
if (!this.AssertMaxOneRequiredLoader(info, operations.LoadOperations, out string? error))
@ -129,7 +133,11 @@ namespace StardewModdingAPI.Framework.ContentManagers
data = this.AssetsBeingLoaded.Track(assetName.Name, () =>
{
IAssetInfo info = new AssetInfo(assetName.LocaleCode, assetName, typeof(T), this.AssertAndNormalizeAssetName);
AssetOperationGroup? operations = this.Coordinator.GetAssetOperations<T>(info);
AssetOperationGroup? operations = this.Coordinator.GetAssetOperations
#if SMAPI_DEPRECATED
<T>
#endif
(info);
IAssetData asset =
this.ApplyLoader<T>(info, operations?.LoadOperations)
?? new AssetDataForObject(info, this.RawLoad<T>(assetName, useCache), this.AssertAndNormalizeAssetName, this.Reflection);
@ -294,7 +302,11 @@ namespace StardewModdingAPI.Framework.ContentManagers
? $"Multiple mods want to provide the '{info.Name}' asset: {string.Join(", ", loaderNames)}"
: $"The '{loaderNames[0]}' mod wants to provide the '{info.Name}' asset multiple times";
error = $"{errorPhrase}. An asset can't be loaded multiple times, so SMAPI will use the default asset instead. Uninstall one of the mods to fix this. (Message for modders: you should avoid {nameof(AssetLoadPriority)}.{nameof(AssetLoadPriority.Exclusive)} and {nameof(IAssetLoader)} if possible to avoid conflicts.)";
error = $"{errorPhrase}. An asset can't be loaded multiple times, so SMAPI will use the default asset instead. Uninstall one of the mods to fix this. (Message for modders: you should avoid {nameof(AssetLoadPriority)}.{nameof(AssetLoadPriority.Exclusive)}"
#if SMAPI_DEPRECATED
+ " and {nameof(IAssetLoader)}"
#endif
+ " if possible to avoid conflicts.)";
return false;
}
@ -349,6 +361,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
// handle mismatch
if (loadedMap.TileSheets.Count <= vanillaSheet.Index || loadedMap.TileSheets[vanillaSheet.Index].Id != vanillaSheet.Id)
{
#if SMAPI_DEPRECATED
// only show warning if not farm map
// This is temporary: mods shouldn't do this for any vanilla map, but these are the ones we know will crash. Showing a warning for others instead gives modders time to update their mods, while still simplifying troubleshooting.
bool isFarmMap = info.Name.IsEquivalentTo("Maps/Farm") || info.Name.IsEquivalentTo("Maps/Farm_Combat") || info.Name.IsEquivalentTo("Maps/Farm_Fishing") || info.Name.IsEquivalentTo("Maps/Farm_Foraging") || info.Name.IsEquivalentTo("Maps/Farm_FourCorners") || info.Name.IsEquivalentTo("Maps/Farm_Island") || info.Name.IsEquivalentTo("Maps/Farm_Mining");
@ -361,7 +374,12 @@ namespace StardewModdingAPI.Framework.ContentManagers
mod.LogAsMod($"SMAPI blocked a '{info.Name}' map load: {reason}", LogLevel.Error);
return false;
}
mod.LogAsMod($"SMAPI found an issue with a '{info.Name}' map load: {reason}", LogLevel.Warn);
#else
mod.LogAsMod($"SMAPI found an issue with a '{info.Name}' map load: {this.GetOnBehalfOfLabel(loader.OnBehalfOf, parenthetical: false) ?? "mod"} reordered the original tilesheets, which often causes crashes.\nTechnical details for mod author: Expected order: {string.Join(", ", vanillaTilesheetRefs.Select(p => p.Id))}. See https://stardewvalleywiki.com/Modding:Maps#Tilesheet_order for help.", LogLevel.Error);
return false;
#endif
}
}
}

View File

@ -96,6 +96,7 @@ namespace StardewModdingAPI.Framework
}
}
#if SMAPI_DEPRECATED
/// <inheritdoc />
[Obsolete($"Use {nameof(IContentPack.ModContent)}.{nameof(IModContentHelper.Load)} instead. This method will be removed in SMAPI 4.0.0.")]
public T LoadAsset<T>(string key)
@ -110,6 +111,7 @@ namespace StardewModdingAPI.Framework
{
return this.ModContent.GetInternalAssetName(key).Name;
}
#endif
/*********

View File

@ -1,5 +1,7 @@
using System;
#if SMAPI_DEPRECATED
using StardewModdingAPI.Framework.Deprecations;
#endif
namespace StardewModdingAPI.Framework.ModHelpers
{
@ -32,6 +34,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
return this;
}
#if SMAPI_DEPRECATED
/// <inheritdoc />
[Obsolete("Use mod-provided APIs to integrate with mods instead. This method will be removed in SMAPI 4.0.0.")]
public bool Trigger(string name, string[] arguments)
@ -45,5 +48,6 @@ namespace StardewModdingAPI.Framework.ModHelpers
return this.CommandManager.Trigger(name, arguments);
}
#endif
}
}

View File

@ -1,3 +1,4 @@
#if SMAPI_DEPRECATED
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@ -249,3 +250,4 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
}
}
#endif

View File

@ -1,7 +1,9 @@
using System;
using System.IO;
using StardewModdingAPI.Events;
#if SMAPI_DEPRECATED
using StardewModdingAPI.Framework.Deprecations;
#endif
using StardewModdingAPI.Framework.Input;
namespace StardewModdingAPI.Framework.ModHelpers
@ -9,12 +11,14 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <summary>Provides simplified APIs for writing mods.</summary>
internal class ModHelper : BaseHelper, IModHelper, IDisposable
{
#if SMAPI_DEPRECATED
/*********
** Fields
*********/
/// <summary>The backing field for <see cref="Content"/>.</summary>
[Obsolete("This only exists to support legacy code and will be removed in SMAPI 4.0.0.")]
private readonly ContentHelper ContentImpl;
#endif
/*********
@ -26,6 +30,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <inheritdoc />
public IModEvents Events { get; }
#if SMAPI_DEPRECATED
/// <inheritdoc />
[Obsolete($"Use {nameof(IGameContentHelper)} or {nameof(IModContentHelper)} instead.")]
public IContentHelper Content
@ -42,6 +47,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
return this.ContentImpl;
}
}
#endif
/// <inheritdoc />
public IGameContentHelper GameContent { get; }
@ -82,7 +88,6 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="modDirectory">The full path to the mod's folder.</param>
/// <param name="currentInputState">Manages the game's input state for the current player instance. That may not be the main player in split-screen mode.</param>
/// <param name="events">Manages access to events raised by SMAPI.</param>
/// <param name="contentHelper">An API for loading content assets.</param>
/// <param name="gameContentHelper">An API for loading content assets from the game's <c>Content</c> folder or via <see cref="IModEvents.Content"/>.</param>
/// <param name="modContentHelper">An API for loading content assets from your mod's files.</param>
/// <param name="contentPackHelper">An API for managing content packs.</param>
@ -96,9 +101,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <exception cref="InvalidOperationException">The <paramref name="modDirectory"/> path does not exist on disk.</exception>
public ModHelper(
IModMetadata mod, string modDirectory, Func<SInputState> currentInputState, IModEvents events,
#pragma warning disable CS0612 // deprecated code
#if SMAPI_DEPRECATED
ContentHelper contentHelper,
#pragma warning restore CS0612
#endif
IGameContentHelper gameContentHelper, IModContentHelper modContentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper
)
: base(mod)
@ -111,9 +116,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
// initialize
this.DirectoryPath = modDirectory;
#pragma warning disable CS0612 // deprecated code
#if SMAPI_DEPRECATED
this.ContentImpl = contentHelper ?? throw new ArgumentNullException(nameof(contentHelper));
#pragma warning restore CS0612
#endif
this.GameContent = gameContentHelper ?? throw new ArgumentNullException(nameof(gameContentHelper));
this.ModContent = modContentHelper ?? throw new ArgumentNullException(nameof(modContentHelper));
this.ContentPacks = contentPackHelper ?? throw new ArgumentNullException(nameof(contentPackHelper));
@ -127,12 +132,14 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.Events = events;
}
#if SMAPI_DEPRECATED
/// <summary>Get the underlying instance for <see cref="IContentHelper"/>.</summary>
[Obsolete("This only exists to support legacy code and will be removed in SMAPI 4.0.0.")]
public ContentHelper GetLegacyContentHelper()
{
return this.ContentImpl;
}
#endif
/****
** Mod config file

View File

@ -163,6 +163,7 @@ namespace StardewModdingAPI.Framework.ModLoading
this.AssemblyDefinitionResolver.Add(assembly.Definition);
}
#if SMAPI_DEPRECATED
// special case: clear legacy-DLL warnings if the mod bundles a copy
if (mod.Warnings.HasFlag(ModWarning.DetectedLegacyCachingDll))
{
@ -185,6 +186,7 @@ namespace StardewModdingAPI.Framework.ModLoading
if (File.Exists(Path.Combine(mod.DirectoryPath, "System.Security.Permissions.dll")))
mod.RemoveWarning(ModWarning.DetectedLegacyPermissionsDll);
}
#endif
// throw if incompatibilities detected
if (!assumeCompatible && mod.Warnings.HasFlag(ModWarning.BrokenCodeLoaded))
@ -452,6 +454,7 @@ namespace StardewModdingAPI.Framework.ModLoading
mod.SetWarning(ModWarning.AccessesShell);
break;
#if SMAPI_DEPRECATED
case InstructionHandleResult.DetectedLegacyCachingDll:
template = $"{logPrefix}Detected reference to System.Runtime.Caching.dll, which will be removed in SMAPI 4.0.0.";
mod.SetWarning(ModWarning.DetectedLegacyCachingDll);
@ -466,6 +469,7 @@ namespace StardewModdingAPI.Framework.ModLoading
template = $"{logPrefix}Detected reference to System.Security.Permissions.dll, which will be removed in SMAPI 4.0.0.";
mod.SetWarning(ModWarning.DetectedLegacyPermissionsDll);
break;
#endif
case InstructionHandleResult.None:
break;

View File

@ -1,3 +1,4 @@
#if SMAPI_DEPRECATED
using Mono.Cecil;
using StardewModdingAPI.Framework.ModLoading.Framework;
@ -47,3 +48,4 @@ namespace StardewModdingAPI.Framework.ModLoading.Finders
}
}
}
#endif

View File

@ -32,6 +32,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>The instruction accesses the OS shell or processes directly.</summary>
DetectedShellAccess,
#if SMAPI_DEPRECATED
/// <summary>The module references the legacy <c>System.Configuration.ConfigurationManager</c> assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0.</summary>
DetectedLegacyConfigurationDll,
@ -40,5 +41,6 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>The module references the legacy <c>System.Security.Permissions</c> assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0.</summary>
DetectedLegacyPermissionsDll
#endif
}
}

View File

@ -32,7 +32,9 @@ using StardewModdingAPI.Framework.Networking;
using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Framework.Rendering;
using StardewModdingAPI.Framework.Serialization;
#if SMAPI_DEPRECATED
using StardewModdingAPI.Framework.StateTracking.Comparers;
#endif
using StardewModdingAPI.Framework.StateTracking.Snapshots;
using StardewModdingAPI.Framework.Utilities;
using StardewModdingAPI.Internal;
@ -139,8 +141,10 @@ namespace StardewModdingAPI.Framework
/// <summary>The maximum number of consecutive attempts SMAPI should make to recover from an update error.</summary>
private readonly Countdown UpdateCrashTimer = new(60); // 60 ticks = roughly one second
#if SMAPI_DEPRECATED
/// <summary>Asset interceptors added or removed since the last tick.</summary>
private readonly List<AssetInterceptorChange> ReloadAssetInterceptorsQueue = new();
#endif
/// <summary>A list of queued commands to parse and execute.</summary>
/// <remarks>This property must be thread-safe, since it's accessed from a separate console input thread.</remarks>
@ -483,6 +487,7 @@ namespace StardewModdingAPI.Framework
return;
}
#if SMAPI_DEPRECATED
/*********
** Reload assets when interceptors are added/removed
*********/
@ -515,6 +520,7 @@ namespace StardewModdingAPI.Framework
// reload affected assets
this.ContentCore.InvalidateCache(asset => interceptors.Any(p => p.CanIntercept(asset)));
}
#endif
/*********
** Parse commands
@ -1646,9 +1652,9 @@ 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)
{
#if SMAPI_DEPRECATED
// add interceptors
if (metadata.Mod?.Helper is ModHelper helper)
{
@ -1684,7 +1690,6 @@ namespace StardewModdingAPI.Framework
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
// log deprecation warnings
if (metadata.HasWarnings(ModWarning.DetectedLegacyCachingDll, ModWarning.DetectedLegacyConfigurationDll, ModWarning.DetectedLegacyPermissionsDll))
@ -1710,6 +1715,7 @@ namespace StardewModdingAPI.Framework
);
}
}
#endif
// call entry method
Context.HeuristicModsRunningCode.Push(metadata);
@ -1750,6 +1756,7 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log("Mods loaded and ready!", LogLevel.Debug);
}
#if SMAPI_DEPRECATED
/// <summary>Raised after a mod adds or removes asset interceptors.</summary>
/// <typeparam name="T">The asset interceptor type (one of <see cref="IAssetEditor"/> or <see cref="IAssetLoader"/>).</typeparam>
/// <param name="mod">The mod metadata.</param>
@ -1772,6 +1779,7 @@ namespace StardewModdingAPI.Framework
list.Remove(entry);
}
}
#endif
/// <summary>Load a given mod.</summary>
/// <param name="mod">The mod to load.</param>
@ -1915,9 +1923,9 @@ namespace StardewModdingAPI.Framework
{
IModEvents events = new ModEvents(mod, this.EventManager);
ICommandHelper commandHelper = new CommandHelper(mod, this.CommandManager);
#pragma warning disable CS0612 // deprecated code
#if SMAPI_DEPRECATED
ContentHelper contentHelper = new(contentCore, mod.DirectoryPath, mod, monitor, this.Reflection);
#pragma warning restore CS0612
#endif
GameContentHelper gameContentHelper = new(contentCore, mod, mod.DisplayName, monitor, this.Reflection);
IModContentHelper modContentHelper = new ModContentHelper(contentCore, mod.DirectoryPath, mod, mod.DisplayName, gameContentHelper.GetUnderlyingContentManager(), this.Reflection);
IContentPackHelper contentPackHelper = new ContentPackHelper(
@ -1930,7 +1938,11 @@ namespace StardewModdingAPI.Framework
IModRegistry modRegistryHelper = new ModRegistryHelper(mod, this.ModRegistry, proxyFactory, monitor);
IMultiplayerHelper multiplayerHelper = new MultiplayerHelper(mod, this.Multiplayer);
modHelper = new ModHelper(mod, mod.DirectoryPath, () => this.GetCurrentGameInstance().Input, events, contentHelper, gameContentHelper, modContentHelper, contentPackHelper, commandHelper, dataHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper);
modHelper = new ModHelper(mod, mod.DirectoryPath, () => this.GetCurrentGameInstance().Input, events,
#if SMAPI_DEPRECATED
contentHelper,
#endif
gameContentHelper, modContentHelper, contentPackHelper, commandHelper, dataHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper);
}
// init mod

View File

@ -1,13 +1,17 @@
#if SMAPI_DEPRECATED
using System;
#endif
namespace StardewModdingAPI
{
/// <summary>The game framework running the game.</summary>
public enum GameFramework
{
#if SMAPI_DEPRECATED
/// <summary>The XNA Framework, previously used on Windows.</summary>
[Obsolete("Stardew Valley no longer uses XNA Framework on any supported platform. This value will be removed in SMAPI 4.0.0.")]
Xna,
#endif
/// <summary>The MonoGame framework.</summary>
MonoGame

View File

@ -1,3 +1,4 @@
#if SMAPI_DEPRECATED
using System;
using StardewModdingAPI.Events;
@ -19,3 +20,4 @@ namespace StardewModdingAPI
void Edit<T>(IAssetData asset);
}
}
#endif

View File

@ -8,25 +8,34 @@ namespace StardewModdingAPI
/*********
** Accessors
*********/
#if SMAPI_DEPRECATED
/// <summary>The content's locale code, if the content is localized.</summary>
/// <remarks>LEGACY NOTE: when reading this field from an <see cref="IAssetLoader"/> or <see cref="IAssetEditor"/> implementation, for non-localized assets it will return the current game locale (or an empty string for English) instead of null.</remarks>
#else
/// <summary>The content's locale code, if the content is localized.</summary>
#endif
string? Locale { get; }
#if SMAPI_DEPRECATED
/// <summary>The asset name being read.</summary>
/// <remarks>LEGACY NOTE: when reading this field from an <see cref="IAssetLoader"/> or <see cref="IAssetEditor"/> implementation, it's always equivalent to <see cref="NameWithoutLocale"/> for backwards compatibility.</remarks>
#else
/// <summary>The asset name being read.</summary>
#endif
public IAssetName Name { get; }
/// <summary>The <see cref="Name"/> with any locale codes stripped.</summary>
/// <remarks>For example, if <see cref="Name"/> contains a locale like <c>Data/Bundles.fr-FR</c>, this will be the name without locale like <c>Data/Bundles</c>. If the name has no locale, this field is equivalent.</remarks>
public IAssetName NameWithoutLocale { get; }
/// <summary>The content data type.</summary>
Type DataType { get; }
#if SMAPI_DEPRECATED
/// <summary>The normalized asset name being read. The format may change between platforms; see <see cref="AssetNameEquals"/> to compare with a known path.</summary>
[Obsolete($"Use {nameof(Name)} or {nameof(NameWithoutLocale)} instead. This property will be removed in SMAPI 4.0.0.")]
string AssetName { get; }
/// <summary>The content data type.</summary>
Type DataType { get; }
/*********
** Public methods
@ -35,5 +44,6 @@ namespace StardewModdingAPI
/// <param name="path">The expected asset path, relative to the game's content folder and without the .xnb extension or locale suffix (like 'Data\ObjectInformation').</param>
[Obsolete($"Use {nameof(Name)}.{nameof(IAssetName.IsEquivalentTo)} or {nameof(NameWithoutLocale)}.{nameof(IAssetName.IsEquivalentTo)} instead. This method will be removed in SMAPI 4.0.0.")]
bool AssetNameEquals(string path);
#endif
}
}

View File

@ -1,3 +1,4 @@
#if SMAPI_DEPRECATED
using System;
using StardewModdingAPI.Events;
@ -19,3 +20,4 @@ namespace StardewModdingAPI
T Load<T>(IAssetInfo asset);
}
}
#endif

View File

@ -17,11 +17,13 @@ namespace StardewModdingAPI
/// <exception cref="ArgumentException">There's already a command with that name.</exception>
ICommandHelper Add(string name, string documentation, Action<string, string[]> callback);
#if SMAPI_DEPRECATED
/// <summary>Trigger a command.</summary>
/// <param name="name">The command name.</param>
/// <param name="arguments">The command arguments.</param>
/// <returns>Returns whether a matching command was triggered.</returns>
[Obsolete("Use mod-provided APIs to integrate with mods instead. This method will be removed in SMAPI 4.0.0.")]
bool Trigger(string name, string[] arguments);
#endif
}
}

View File

@ -1,3 +1,4 @@
#if SMAPI_DEPRECATED
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
@ -80,3 +81,4 @@ namespace StardewModdingAPI
where T : notnull;
}
}
#endif

View File

@ -1,7 +1,9 @@
using System;
#if SMAPI_DEPRECATED
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using xTile;
#endif
namespace StardewModdingAPI
{
@ -47,6 +49,7 @@ namespace StardewModdingAPI
void WriteJsonFile<TModel>(string path, TModel data)
where TModel : class;
#if SMAPI_DEPRECATED
/// <summary>Load content from the content pack folder (if not already cached), and return it. When loading a <c>.png</c> file, this must be called outside the game's draw loop.</summary>
/// <typeparam name="T">The expected data type. The main supported types are <see cref="Map"/>, <see cref="Texture2D"/>, <see cref="IRawTextureData"/>, and data structures; other types may be supported by the game's content pipeline.</typeparam>
/// <param name="key">The relative file path within the content pack (case-insensitive).</param>
@ -61,5 +64,6 @@ namespace StardewModdingAPI
/// <exception cref="ArgumentException">The <paramref name="key"/> is empty or contains invalid characters.</exception>
[Obsolete($"Use {nameof(IContentPack.ModContent)}.{nameof(IModContentHelper.GetInternalAssetName)} instead. This method will be removed in SMAPI 4.0.0.")]
string GetActualAssetKey(string key);
#endif
}
}

View File

@ -1,4 +1,6 @@
#if SMAPI_DEPRECATED
using System;
#endif
using StardewModdingAPI.Events;
namespace StardewModdingAPI
@ -25,9 +27,11 @@ namespace StardewModdingAPI
/// <remarks>This API is intended for reading content assets from the mod files (like game data, images, etc); see also <see cref="Data"/> which is intended for persisting internal mod data.</remarks>
IModContentHelper ModContent { get; }
#if SMAPI_DEPRECATED
/// <summary>An API for loading content assets.</summary>
[Obsolete($"Use {nameof(IGameContentHelper)} or {nameof(IModContentHelper)} instead.")]
IContentHelper Content { get; }
#endif
/// <summary>An API for managing content packs.</summary>
IContentPackHelper ContentPacks { get; }

View File

@ -54,8 +54,10 @@ namespace StardewModdingAPI.Metadata
// detect Harmony & rewrite for SMAPI 3.12 (Harmony 1.x => 2.0 update)
yield return new HarmonyRewriter();
#if SMAPI_DEPRECATED
// detect issues for SMAPI 4.0.0
yield return new LegacyAssemblyFinder();
#endif
}
else
yield return new HarmonyRewriter(shouldRewrite: false);

View File

@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
#if SMAPI_DEPRECATED
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.Deprecations;
#endif
namespace StardewModdingAPI.Utilities
{
@ -41,12 +43,31 @@ namespace StardewModdingAPI.Utilities
/// <summary>Construct an instance.</summary>
/// <remarks><strong>Limitation with nullable reference types:</strong> when the underlying type <typeparamref name="T"/> is nullable, this sets the default value to null regardless of whether you marked the type parameter nullable. To avoid that, set the default value with the 'createNewState' argument instead.</remarks>
public PerScreen()
: this(null, nullExpected: true) { }
{
this.CreateNewState = (() => default!);
}
/// <summary>Construct an instance.</summary>
/// <param name="createNewState">Create the initial state for a screen.</param>
public PerScreen(Func<T> createNewState)
: this(createNewState, nullExpected: false) { }
{
if (createNewState is null)
{
#if SMAPI_DEPRECATED
createNewState = (() => default!);
SCore.DeprecationManager.Warn(
null,
$"calling the {nameof(PerScreen<T>)} constructor with null",
"3.14.0",
DeprecationLevel.Notice
);
#else
throw new ArgumentNullException(nameof(createNewState));
#endif
}
this.CreateNewState = createNewState;
}
/// <summary>Get all active values by screen ID. This doesn't initialize the value for a screen ID if it's not created yet.</summary>
public IEnumerable<KeyValuePair<int, T>> GetActiveValues()
@ -84,30 +105,6 @@ namespace StardewModdingAPI.Utilities
/*********
** Private methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="createNewState">Create the initial state for a screen.</param>
/// <param name="nullExpected">Whether a null <paramref name="createNewState"/> value is expected.</param>
/// <remarks>This constructor only exists to maintain backwards compatibility. In SMAPI 4.0.0, the overload that passes <c>nullExpected: false</c> should throw an exception instead.</remarks>
private PerScreen(Func<T>? createNewState, bool nullExpected)
{
if (createNewState is null)
{
createNewState = (() => default!);
if (!nullExpected)
{
SCore.DeprecationManager.Warn(
null,
$"calling the {nameof(PerScreen<T>)} constructor with null",
"3.14.0",
DeprecationLevel.Notice
);
}
}
this.CreateNewState = createNewState;
}
/// <summary>Remove screens which are no longer active.</summary>
private void RemoveDeadScreens()
{