diff --git a/build/common.targets b/build/common.targets
index 8b17c45a..92fd9a9a 100644
--- a/build/common.targets
+++ b/build/common.targets
@@ -5,6 +5,7 @@
SMAPI
latest
$(AssemblySearchPaths);{GAC}
+ $(DefineConstants);SMAPI_DEPRECATED
enable
@@ -20,14 +21,17 @@
- $(NoWarn);CS0436;CA1416;CS0809;NU1701
+ $(NoWarn);CS0612;CS0618
+ $(NoWarn);CS0436;CA1416;CS0809;NU1701
diff --git a/docs/technical/smapi.md b/docs/technical/smapi.md
index 44b6e49f..90990ee4 100644
--- a/docs/technical/smapi.md
+++ b/docs/technical/smapi.md
@@ -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
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs b/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs
index 32c2ed6d..338192af 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs
@@ -18,9 +18,11 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// The mod patches the game in a way that may impact stability.
PatchesGame = 4,
+#if SMAPI_FOR_WINDOWS
/// The mod uses the dynamic keyword which won't work on Linux/macOS.
[Obsolete("This value is no longer used by SMAPI and will be removed in the upcoming SMAPI 4.0.0.")]
UsesDynamic = 8,
+#endif
/// The mod references specialized 'unvalidated update tick' events which may impact stability.
UsesUnvalidatedUpdateTick = 16,
@@ -37,6 +39,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// Uses .NET APIs for shell or process access.
AccessesShell = 256,
+#if SMAPI_DEPRECATED
/// References the legacy System.Configuration.ConfigurationManager assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0.
DetectedLegacyConfigurationDll = 512,
@@ -45,5 +48,6 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// References the legacy System.Security.Permissions assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0.
DetectedLegacyPermissionsDll = 2048
+#endif
}
}
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index db88563e..33468717 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -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
/// The game framework running the game.
public static GameFramework GameFramework { get; } = EarlyConstants.GameFramework;
+#if SMAPI_DEPRECATED
/// The path to the game folder.
[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
/// The path to the game folder.
public static string GamePath { get; } = EarlyConstants.GamePath;
diff --git a/src/SMAPI/Framework/Content/AssetInfo.cs b/src/SMAPI/Framework/Content/AssetInfo.cs
index 773e3126..43feed27 100644
--- a/src/SMAPI/Framework/Content/AssetInfo.cs
+++ b/src/SMAPI/Framework/Content/AssetInfo.cs
@@ -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
///
public IAssetName NameWithoutLocale => this.NameWithoutLocaleImpl ??= this.Name.GetBaseAssetName();
+ ///
+ public Type DataType { get; }
+
+#if SMAPI_DEPRECATED
///
[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;
}
}
-
- ///
- public Type DataType { get; }
+#endif
/*********
@@ -71,6 +75,7 @@ namespace StardewModdingAPI.Framework.Content
this.GetNormalizedPath = getNormalizedPath;
}
+#if SMAPI_DEPRECATED
///
[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
/*********
diff --git a/src/SMAPI/Framework/Content/AssetInterceptorChange.cs b/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
index f3d4f3f4..3b5068dc 100644
--- a/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
+++ b/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
@@ -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
{
/// A wrapper for and for internal cache invalidation.
@@ -103,3 +103,4 @@ namespace StardewModdingAPI.Framework.Content
}
}
}
+#endif
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs
index 3e09ac62..9e044b44 100644
--- a/src/SMAPI/Framework/ContentCoordinator.cs
+++ b/src/SMAPI/Framework/ContentCoordinator.cs
@@ -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
/// The cached asset load/edit operations to apply, indexed by asset name.
private readonly TickCacheDictionary AssetOperationsByKey = new();
+#if SMAPI_DEPRECATED
/// A cache of asset operation groups created for legacy implementations.
[Obsolete("This only exists to support legacy code and will be removed in SMAPI 4.0.0.")]
private readonly Dictionary> LegacyLoaderCache = new(ReferenceEqualityComparer.Instance);
@@ -91,6 +94,7 @@ namespace StardewModdingAPI.Framework
/// A cache of asset operation groups created for legacy implementations.
[Obsolete("This only exists to support legacy code and will be removed in SMAPI 4.0.0.")]
private readonly Dictionary> LegacyEditorCache = new(ReferenceEqualityComparer.Instance);
+#endif
/*********
@@ -102,6 +106,7 @@ namespace StardewModdingAPI.Framework
/// The current language as a constant.
public LocalizedContentManager.LanguageCode Language => this.MainContentManager.Language;
+#if SMAPI_DEPRECATED
/// Interceptors which provide the initial versions of matching assets.
[Obsolete("This only exists to support legacy code and will be removed in SMAPI 4.0.0.")]
public IList> Loaders { get; } = new List>();
@@ -109,6 +114,7 @@ namespace StardewModdingAPI.Framework
/// Interceptors which edit matching assets after they're loaded.
[Obsolete("This only exists to support legacy code and will be removed in SMAPI 4.0.0.")]
public IList> Editors { get; } = new List>();
+#endif
/// The absolute path to the .
public string FullRootDirectory { get; }
@@ -498,15 +504,25 @@ namespace StardewModdingAPI.Framework
return invalidatedAssets.Keys;
}
+#if SMAPI_DEPRECATED
/// Get the asset load and edit operations to apply to a given asset if it's (re)loaded now.
/// The asset type.
/// The asset info to load or edit.
public AssetOperationGroup? GetAssetOperations(IAssetInfo info)
where T : notnull
+#else
+ /// Get the asset load and edit operations to apply to a given asset if it's (re)loaded now.
+ /// The asset info to load or edit.
+ public AssetOperationGroup? GetAssetOperations(IAssetInfo info)
+#endif
{
return this.AssetOperationsByKey.GetOrSet(
info.Name,
+#if SMAPI_DEPRECATED
() => this.GetAssetOperationsWithoutCache(info)
+#else
+ () => this.RequestAssetOperations(info)
+#endif
);
}
@@ -629,6 +645,7 @@ namespace StardewModdingAPI.Framework
return map;
}
+#if SMAPI_DEPRECATED
/// Get the asset load and edit operations to apply to a given asset if it's (re)loaded now, ignoring the cache.
/// The asset type.
/// The asset info to load or edit.
@@ -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
}
}
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
index 446f4a67..df7bdc59 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
@@ -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(info);
+ AssetOperationGroup? operations = this.Coordinator.GetAssetOperations
+#if SMAPI_DEPRECATED
+
+#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(info);
+ AssetOperationGroup? operations = this.Coordinator.GetAssetOperations
+#if SMAPI_DEPRECATED
+
+#endif
+ (info);
IAssetData asset =
this.ApplyLoader(info, operations?.LoadOperations)
?? new AssetDataForObject(info, this.RawLoad(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
}
}
}
diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs
index 70fe51f8..a1d977e4 100644
--- a/src/SMAPI/Framework/ContentPack.cs
+++ b/src/SMAPI/Framework/ContentPack.cs
@@ -96,6 +96,7 @@ namespace StardewModdingAPI.Framework
}
}
+#if SMAPI_DEPRECATED
///
[Obsolete($"Use {nameof(IContentPack.ModContent)}.{nameof(IModContentHelper.Load)} instead. This method will be removed in SMAPI 4.0.0.")]
public T LoadAsset(string key)
@@ -110,6 +111,7 @@ namespace StardewModdingAPI.Framework
{
return this.ModContent.GetInternalAssetName(key).Name;
}
+#endif
/*********
diff --git a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
index ddbd618a..21435f62 100644
--- a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
@@ -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
///
[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
}
}
diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
index 427adac2..9992cb52 100644
--- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
@@ -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
diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
index 48973691..9ac3b6f7 100644
--- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
@@ -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
/// Provides simplified APIs for writing mods.
internal class ModHelper : BaseHelper, IModHelper, IDisposable
{
+#if SMAPI_DEPRECATED
/*********
** Fields
*********/
/// The backing field for .
[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
///
public IModEvents Events { get; }
+#if SMAPI_DEPRECATED
///
[Obsolete($"Use {nameof(IGameContentHelper)} or {nameof(IModContentHelper)} instead.")]
public IContentHelper Content
@@ -42,6 +47,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
return this.ContentImpl;
}
}
+#endif
///
public IGameContentHelper GameContent { get; }
@@ -82,7 +88,6 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// The full path to the mod's folder.
/// Manages the game's input state for the current player instance. That may not be the main player in split-screen mode.
/// Manages access to events raised by SMAPI.
- /// An API for loading content assets.
/// An API for loading content assets from the game's Content folder or via .
/// An API for loading content assets from your mod's files.
/// An API for managing content packs.
@@ -96,9 +101,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// The path does not exist on disk.
public ModHelper(
IModMetadata mod, string modDirectory, Func 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
/// Get the underlying instance for .
[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
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
index e5aaa8ee..eb940c41 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
@@ -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;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/LegacyAssemblyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/LegacyAssemblyFinder.cs
index d3437b05..77380907 100644
--- a/src/SMAPI/Framework/ModLoading/Finders/LegacyAssemblyFinder.cs
+++ b/src/SMAPI/Framework/ModLoading/Finders/LegacyAssemblyFinder.cs
@@ -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
diff --git a/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs b/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
index 476c30d0..189ca64e 100644
--- a/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
+++ b/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
@@ -32,6 +32,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// The instruction accesses the OS shell or processes directly.
DetectedShellAccess,
+#if SMAPI_DEPRECATED
/// The module references the legacy System.Configuration.ConfigurationManager assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0.
DetectedLegacyConfigurationDll,
@@ -40,5 +41,6 @@ namespace StardewModdingAPI.Framework.ModLoading
/// The module references the legacy System.Security.Permissions assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0.
DetectedLegacyPermissionsDll
+#endif
}
}
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 385a94ea..ff3eadf5 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -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
/// The maximum number of consecutive attempts SMAPI should make to recover from an update error.
private readonly Countdown UpdateCrashTimer = new(60); // 60 ticks = roughly one second
+#if SMAPI_DEPRECATED
/// Asset interceptors added or removed since the last tick.
private readonly List ReloadAssetInterceptorsQueue = new();
+#endif
/// A list of queued commands to parse and execute.
/// This property must be thread-safe, since it's accessed from a separate console input thread.
@@ -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(), e.OldItems?.Cast(), this.ContentCore.Editors);
content.ObservableAssetLoaders.CollectionChanged += (_, e) => this.OnAssetInterceptorsChanged(metadata, e.NewItems?.Cast(), e.OldItems?.Cast(), 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
/// Raised after a mod adds or removes asset interceptors.
/// The asset interceptor type (one of or ).
/// The mod metadata.
@@ -1772,6 +1779,7 @@ namespace StardewModdingAPI.Framework
list.Remove(entry);
}
}
+#endif
/// Load a given mod.
/// The mod to load.
@@ -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
diff --git a/src/SMAPI/GameFramework.cs b/src/SMAPI/GameFramework.cs
index 60fbe56e..009865fe 100644
--- a/src/SMAPI/GameFramework.cs
+++ b/src/SMAPI/GameFramework.cs
@@ -1,13 +1,17 @@
+#if SMAPI_DEPRECATED
using System;
+#endif
namespace StardewModdingAPI
{
/// The game framework running the game.
public enum GameFramework
{
+#if SMAPI_DEPRECATED
/// The XNA Framework, previously used on Windows.
[Obsolete("Stardew Valley no longer uses XNA Framework on any supported platform. This value will be removed in SMAPI 4.0.0.")]
Xna,
+#endif
/// The MonoGame framework.
MonoGame
diff --git a/src/SMAPI/IAssetEditor.cs b/src/SMAPI/IAssetEditor.cs
index 9f22ed83..f3238ba9 100644
--- a/src/SMAPI/IAssetEditor.cs
+++ b/src/SMAPI/IAssetEditor.cs
@@ -1,3 +1,4 @@
+#if SMAPI_DEPRECATED
using System;
using StardewModdingAPI.Events;
@@ -19,3 +20,4 @@ namespace StardewModdingAPI
void Edit(IAssetData asset);
}
}
+#endif
diff --git a/src/SMAPI/IAssetInfo.cs b/src/SMAPI/IAssetInfo.cs
index 44fd91a5..20064946 100644
--- a/src/SMAPI/IAssetInfo.cs
+++ b/src/SMAPI/IAssetInfo.cs
@@ -8,25 +8,34 @@ namespace StardewModdingAPI
/*********
** Accessors
*********/
+#if SMAPI_DEPRECATED
/// The content's locale code, if the content is localized.
/// LEGACY NOTE: when reading this field from an or implementation, for non-localized assets it will return the current game locale (or an empty string for English) instead of null.
+#else
+ /// The content's locale code, if the content is localized.
+#endif
string? Locale { get; }
+#if SMAPI_DEPRECATED
/// The asset name being read.
/// LEGACY NOTE: when reading this field from an or implementation, it's always equivalent to for backwards compatibility.
+#else
+ /// The asset name being read.
+#endif
public IAssetName Name { get; }
/// The with any locale codes stripped.
/// For example, if contains a locale like Data/Bundles.fr-FR, this will be the name without locale like Data/Bundles. If the name has no locale, this field is equivalent.
public IAssetName NameWithoutLocale { get; }
+ /// The content data type.
+ Type DataType { get; }
+
+#if SMAPI_DEPRECATED
/// The normalized asset name being read. The format may change between platforms; see to compare with a known path.
[Obsolete($"Use {nameof(Name)} or {nameof(NameWithoutLocale)} instead. This property will be removed in SMAPI 4.0.0.")]
string AssetName { get; }
- /// The content data type.
- Type DataType { get; }
-
/*********
** Public methods
@@ -35,5 +44,6 @@ namespace StardewModdingAPI
/// The expected asset path, relative to the game's content folder and without the .xnb extension or locale suffix (like 'Data\ObjectInformation').
[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
}
}
diff --git a/src/SMAPI/IAssetLoader.cs b/src/SMAPI/IAssetLoader.cs
index 96b98793..205980a7 100644
--- a/src/SMAPI/IAssetLoader.cs
+++ b/src/SMAPI/IAssetLoader.cs
@@ -1,3 +1,4 @@
+#if SMAPI_DEPRECATED
using System;
using StardewModdingAPI.Events;
@@ -19,3 +20,4 @@ namespace StardewModdingAPI
T Load(IAssetInfo asset);
}
}
+#endif
diff --git a/src/SMAPI/ICommandHelper.cs b/src/SMAPI/ICommandHelper.cs
index 9f1c345c..c92a09c2 100644
--- a/src/SMAPI/ICommandHelper.cs
+++ b/src/SMAPI/ICommandHelper.cs
@@ -17,11 +17,13 @@ namespace StardewModdingAPI
/// There's already a command with that name.
ICommandHelper Add(string name, string documentation, Action callback);
+#if SMAPI_DEPRECATED
/// Trigger a command.
/// The command name.
/// The command arguments.
/// Returns whether a matching command was triggered.
[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
}
}
diff --git a/src/SMAPI/IContentHelper.cs b/src/SMAPI/IContentHelper.cs
index 7637edf0..b0e30a82 100644
--- a/src/SMAPI/IContentHelper.cs
+++ b/src/SMAPI/IContentHelper.cs
@@ -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
diff --git a/src/SMAPI/IContentPack.cs b/src/SMAPI/IContentPack.cs
index 73b1a860..5047b172 100644
--- a/src/SMAPI/IContentPack.cs
+++ b/src/SMAPI/IContentPack.cs
@@ -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(string path, TModel data)
where TModel : class;
+#if SMAPI_DEPRECATED
/// Load content from the content pack folder (if not already cached), and return it. When loading a .png file, this must be called outside the game's draw loop.
/// The expected data type. The main supported types are , , , and data structures; other types may be supported by the game's content pipeline.
/// The relative file path within the content pack (case-insensitive).
@@ -61,5 +64,6 @@ namespace StardewModdingAPI
/// The is empty or contains invalid characters.
[Obsolete($"Use {nameof(IContentPack.ModContent)}.{nameof(IModContentHelper.GetInternalAssetName)} instead. This method will be removed in SMAPI 4.0.0.")]
string GetActualAssetKey(string key);
+#endif
}
}
diff --git a/src/SMAPI/IModHelper.cs b/src/SMAPI/IModHelper.cs
index 15e4ed8d..a44d92c1 100644
--- a/src/SMAPI/IModHelper.cs
+++ b/src/SMAPI/IModHelper.cs
@@ -1,4 +1,6 @@
+#if SMAPI_DEPRECATED
using System;
+#endif
using StardewModdingAPI.Events;
namespace StardewModdingAPI
@@ -25,9 +27,11 @@ namespace StardewModdingAPI
/// This API is intended for reading content assets from the mod files (like game data, images, etc); see also which is intended for persisting internal mod data.
IModContentHelper ModContent { get; }
+#if SMAPI_DEPRECATED
/// An API for loading content assets.
[Obsolete($"Use {nameof(IGameContentHelper)} or {nameof(IModContentHelper)} instead.")]
IContentHelper Content { get; }
+#endif
/// An API for managing content packs.
IContentPackHelper ContentPacks { get; }
diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs
index dce0c6b1..efa91d20 100644
--- a/src/SMAPI/Metadata/InstructionMetadata.cs
+++ b/src/SMAPI/Metadata/InstructionMetadata.cs
@@ -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);
diff --git a/src/SMAPI/Utilities/PerScreen.cs b/src/SMAPI/Utilities/PerScreen.cs
index b86310b8..54657ade 100644
--- a/src/SMAPI/Utilities/PerScreen.cs
+++ b/src/SMAPI/Utilities/PerScreen.cs
@@ -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
/// Construct an instance.
/// Limitation with nullable reference types: when the underlying type 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.
public PerScreen()
- : this(null, nullExpected: true) { }
+ {
+ this.CreateNewState = (() => default!);
+ }
/// Construct an instance.
/// Create the initial state for a screen.
public PerScreen(Func createNewState)
- : this(createNewState, nullExpected: false) { }
+ {
+ if (createNewState is null)
+ {
+#if SMAPI_DEPRECATED
+ createNewState = (() => default!);
+ SCore.DeprecationManager.Warn(
+ null,
+ $"calling the {nameof(PerScreen)} constructor with null",
+ "3.14.0",
+ DeprecationLevel.Notice
+ );
+#else
+ throw new ArgumentNullException(nameof(createNewState));
+#endif
+ }
+
+ this.CreateNewState = createNewState;
+ }
/// Get all active values by screen ID. This doesn't initialize the value for a screen ID if it's not created yet.
public IEnumerable> GetActiveValues()
@@ -84,30 +105,6 @@ namespace StardewModdingAPI.Utilities
/*********
** Private methods
*********/
- /// Construct an instance.
- /// Create the initial state for a screen.
- /// Whether a null value is expected.
- /// This constructor only exists to maintain backwards compatibility. In SMAPI 4.0.0, the overload that passes nullExpected: false should throw an exception instead.
- private PerScreen(Func? createNewState, bool nullExpected)
- {
- if (createNewState is null)
- {
- createNewState = (() => default!);
-
- if (!nullExpected)
- {
- SCore.DeprecationManager.Warn(
- null,
- $"calling the {nameof(PerScreen)} constructor with null",
- "3.14.0",
- DeprecationLevel.Notice
- );
- }
- }
-
- this.CreateNewState = createNewState;
- }
-
/// Remove screens which are no longer active.
private void RemoveDeadScreens()
{