diff --git a/build/common.targets b/build/common.targets
index b2441af8..10b94d7e 100644
--- a/build/common.targets
+++ b/build/common.targets
@@ -70,6 +70,11 @@
+
+
+
+
+
diff --git a/build/unix/prepare-install-package.sh b/build/unix/prepare-install-package.sh
index 01c3a0ec..01cd2080 100755
--- a/build/unix/prepare-install-package.sh
+++ b/build/unix/prepare-install-package.sh
@@ -151,6 +151,11 @@ for folder in ${folders[@]}; do
cp "$smapiBin/System.Management.dll" "$bundlePath/smapi-internal"
fi
+ # copy legacy .NET dependencies (remove in SMAPI 4.0.0)
+ cp "$smapiBin/System.Configuration.ConfigurationManager.dll" "$bundlePath/smapi-internal"
+ cp "$smapiBin/System.Runtime.Caching.dll" "$bundlePath/smapi-internal"
+ cp "$smapiBin/System.Security.Permissions.dll" "$bundlePath/smapi-internal"
+
# copy bundled mods
for modName in ${bundleModNames[@]}; do
fromPath="src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish"
diff --git a/build/windows/prepare-install-package.ps1 b/build/windows/prepare-install-package.ps1
index 6731486b..7e3c6c86 100644
--- a/build/windows/prepare-install-package.ps1
+++ b/build/windows/prepare-install-package.ps1
@@ -172,6 +172,11 @@ foreach ($folder in $folders) {
cp "$smapiBin/System.Management.dll" "$bundlePath/smapi-internal"
}
+ # copy legacy .NET dependencies (remove in SMAPI 4.0.0)
+ cp "$smapiBin/System.Configuration.ConfigurationManager.dll" "$bundlePath/smapi-internal"
+ cp "$smapiBin/System.Runtime.Caching.dll" "$bundlePath/smapi-internal"
+ cp "$smapiBin/System.Security.Permissions.dll" "$bundlePath/smapi-internal"
+
# copy bundled mods
foreach ($modName in $bundleModNames) {
$fromPath = "src/SMAPI.Mods.$modName/bin/$buildConfig/$runtime/publish"
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs b/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs
index cf804df4..32c2ed6d 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModWarning.cs
@@ -35,6 +35,15 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
AccessesFilesystem = 128,
/// Uses .NET APIs for shell or process access.
- AccessesShell = 256
+ AccessesShell = 256,
+
+ /// 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,
+
+ /// References the legacy System.Runtime.Caching assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0.
+ DetectedLegacyCachingDll = 1024,
+
+ /// 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
}
}
diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index 7cee20b9..be25c070 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -88,6 +88,10 @@ namespace StardewModdingAPI.Framework
/// The warning to set.
IModMetadata SetWarning(ModWarning warning);
+ /// Remove a warning flag for the mod.
+ /// The warning to remove.
+ IModMetadata RemoveWarning(ModWarning warning);
+
/// Set the mod instance.
/// The mod instance to set.
/// The translations for this mod (if loaded).
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
index fb5ebc01..e5aaa8ee 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
@@ -163,6 +163,29 @@ namespace StardewModdingAPI.Framework.ModLoading
this.AssemblyDefinitionResolver.Add(assembly.Definition);
}
+ // special case: clear legacy-DLL warnings if the mod bundles a copy
+ if (mod.Warnings.HasFlag(ModWarning.DetectedLegacyCachingDll))
+ {
+ if (File.Exists(Path.Combine(mod.DirectoryPath, "System.Runtime.Caching.dll")))
+ mod.RemoveWarning(ModWarning.DetectedLegacyCachingDll);
+ else
+ {
+ // remove duplicate warnings (System.Runtime.Caching.dll references these)
+ mod.RemoveWarning(ModWarning.DetectedLegacyConfigurationDll);
+ mod.RemoveWarning(ModWarning.DetectedLegacyPermissionsDll);
+ }
+ }
+ if (mod.Warnings.HasFlag(ModWarning.DetectedLegacyConfigurationDll))
+ {
+ if (File.Exists(Path.Combine(mod.DirectoryPath, "System.Configuration.ConfigurationManager.dll")))
+ mod.RemoveWarning(ModWarning.DetectedLegacyConfigurationDll);
+ }
+ if (mod.Warnings.HasFlag(ModWarning.DetectedLegacyPermissionsDll))
+ {
+ if (File.Exists(Path.Combine(mod.DirectoryPath, "System.Security.Permissions.dll")))
+ mod.RemoveWarning(ModWarning.DetectedLegacyPermissionsDll);
+ }
+
// throw if incompatibilities detected
if (!assumeCompatible && mod.Warnings.HasFlag(ModWarning.BrokenCodeLoaded))
throw new IncompatibleInstructionException();
@@ -429,6 +452,21 @@ namespace StardewModdingAPI.Framework.ModLoading
mod.SetWarning(ModWarning.AccessesShell);
break;
+ 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);
+ break;
+
+ case InstructionHandleResult.DetectedLegacyConfigurationDll:
+ template = $"{logPrefix}Detected reference to System.Configuration.ConfigurationManager.dll, which will be removed in SMAPI 4.0.0.";
+ mod.SetWarning(ModWarning.DetectedLegacyConfigurationDll);
+ break;
+
+ case InstructionHandleResult.DetectedLegacyPermissionsDll:
+ template = $"{logPrefix}Detected reference to System.Security.Permissions.dll, which will be removed in SMAPI 4.0.0.";
+ mod.SetWarning(ModWarning.DetectedLegacyPermissionsDll);
+ break;
+
case InstructionHandleResult.None:
break;
diff --git a/src/SMAPI/Framework/ModLoading/Finders/LegacyAssemblyFinder.cs b/src/SMAPI/Framework/ModLoading/Finders/LegacyAssemblyFinder.cs
new file mode 100644
index 00000000..d3437b05
--- /dev/null
+++ b/src/SMAPI/Framework/ModLoading/Finders/LegacyAssemblyFinder.cs
@@ -0,0 +1,49 @@
+using Mono.Cecil;
+using StardewModdingAPI.Framework.ModLoading.Framework;
+
+namespace StardewModdingAPI.Framework.ModLoading.Finders
+{
+ /// Detects assembly references which will break in SMAPI 4.0.0.
+ internal class LegacyAssemblyFinder : BaseInstructionHandler
+ {
+ /*********
+ ** Public methods
+ *********/
+ /// Construct an instance.
+ public LegacyAssemblyFinder()
+ : base(defaultPhrase: "legacy assembly references") { }
+
+
+ ///
+ public override bool Handle(ModuleDefinition module)
+ {
+ foreach (AssemblyNameReference assembly in module.AssemblyReferences)
+ {
+ InstructionHandleResult flag = this.GetFlag(assembly);
+ if (flag is InstructionHandleResult.None)
+ continue;
+
+ this.MarkFlag(flag);
+ }
+
+ return false;
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// Get the instruction handle flag for the given assembly reference, if any.
+ /// The assembly reference.
+ private InstructionHandleResult GetFlag(AssemblyNameReference assemblyRef)
+ {
+ return assemblyRef.Name switch
+ {
+ "System.Configuration.ConfigurationManager" => InstructionHandleResult.DetectedLegacyConfigurationDll,
+ "System.Runtime.Caching" => InstructionHandleResult.DetectedLegacyCachingDll,
+ "System.Security.Permission" => InstructionHandleResult.DetectedLegacyPermissionsDll,
+ _ => InstructionHandleResult.None
+ };
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs b/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
index e3f108cb..476c30d0 100644
--- a/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
+++ b/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
@@ -30,6 +30,15 @@ namespace StardewModdingAPI.Framework.ModLoading
DetectedFilesystemAccess,
/// The instruction accesses the OS shell or processes directly.
- DetectedShellAccess
+ DetectedShellAccess,
+
+ /// 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,
+
+ /// The module references the legacy System.Runtime.Caching assembly and doesn't include a copy in the mod folder, so it'll break in SMAPI 4.0.0.
+ DetectedLegacyCachingDll,
+
+ /// 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
}
}
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index fe54634b..aa4d2d8c 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -138,6 +138,13 @@ namespace StardewModdingAPI.Framework.ModLoading
return this;
}
+ ///
+ public IModMetadata RemoveWarning(ModWarning warning)
+ {
+ this.ActualWarnings &= ~warning;
+ return this;
+ }
+
///
public IModMetadata SetMod(IMod mod, TranslationHelper translations)
{
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index c453562f..731731d4 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -1679,6 +1679,31 @@ namespace StardewModdingAPI.Framework
}
#pragma warning restore CS0612, CS0618
+ // log deprecation warnings
+ if (metadata.HasWarnings(ModWarning.DetectedLegacyCachingDll, ModWarning.DetectedLegacyConfigurationDll, ModWarning.DetectedLegacyPermissionsDll))
+ {
+ string?[] referenced =
+ new[]
+ {
+ metadata.Warnings.HasFlag(ModWarning.DetectedLegacyConfigurationDll) ? "System.Configuration.ConfigurationManager" : null,
+ metadata.Warnings.HasFlag(ModWarning.DetectedLegacyCachingDll) ? "System.Runtime.Caching" : null,
+ metadata.Warnings.HasFlag(ModWarning.DetectedLegacyPermissionsDll) ? "System.Security.Permissions" : null
+ }
+ .Where(p => p is not null)
+ .ToArray();
+
+ foreach (string? name in referenced)
+ {
+ DeprecationManager.Warn(
+ metadata,
+ $"using {name} without bundling it",
+ "3.14.7",
+ DeprecationLevel.Notice,
+ logStackTrace: false
+ );
+ }
+ }
+
// call entry method
Context.HeuristicModsRunningCode.Push(metadata);
try
diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs
index 4d512546..dce0c6b1 100644
--- a/src/SMAPI/Metadata/InstructionMetadata.cs
+++ b/src/SMAPI/Metadata/InstructionMetadata.cs
@@ -53,6 +53,9 @@ namespace StardewModdingAPI.Metadata
// detect Harmony & rewrite for SMAPI 3.12 (Harmony 1.x => 2.0 update)
yield return new HarmonyRewriter();
+
+ // detect issues for SMAPI 4.0.0
+ yield return new LegacyAssemblyFinder();
}
else
yield return new HarmonyRewriter(shouldRewrite: false);
diff --git a/src/SMAPI/SMAPI.csproj b/src/SMAPI/SMAPI.csproj
index 95249bfd..a0ca54cc 100644
--- a/src/SMAPI/SMAPI.csproj
+++ b/src/SMAPI/SMAPI.csproj
@@ -28,6 +28,9 @@
+
+
+