drop support for XNA Framework

Stardew Valley 1.5.5 migrates to MonoGame on all platforms.
This commit is contained in:
Jesse Plamondon-Willard
2021-11-28 00:01:40 -05:00
parent c8c6b3897c
commit 6efaa651cb
15 changed files with 60 additions and 195 deletions
-1
View File
@@ -57,7 +57,6 @@ 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_FOR_XNA` | Whether SMAPI is being compiled for XNA Framework; if not set, the code assumes MonoGame. Set automatically in `common.targets` with the same value as `SMAPI_FOR_WINDOWS`.
## For SMAPI developers
### Compiling from source
+9 -23
View File
@@ -43,38 +43,24 @@
<!--*********************************************
** Add assembly references
**********************************************-->
<!-- common -->
<ItemGroup>
<!-- game -->
<Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="$(CopyModReferencesToBuildOutput)" />
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="$(CopyModReferencesToBuildOutput)" />
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="$(CopyModReferencesToBuildOutput)" />
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="$(CopyModReferencesToBuildOutput)" />
<!-- SMAPI -->
<Reference Include="StardewModdingAPI" HintPath="$(GamePath)\StardewModdingAPI.exe" Private="$(CopyModReferencesToBuildOutput)" />
<Reference Include="SMAPI.Toolkit.CoreInterfaces" HintPath="$(GamePath)\smapi-internal\SMAPI.Toolkit.CoreInterfaces.dll" Private="$(CopyModReferencesToBuildOutput)" />
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="$(CopyModReferencesToBuildOutput)" />
<!-- Harmony -->
<Reference Include="0Harmony" Condition="'$(EnableHarmony)' == 'true'" HintPath="$(GamePath)\smapi-internal\0Harmony.dll" Private="$(CopyModReferencesToBuildOutput)" />
</ItemGroup>
<!-- Windows only -->
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="$(CopyModReferencesToBuildOutput)" />
<!-- Windows only -->
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="$(CopyModReferencesToBuildOutput)" Condition="'$(OS)' == 'Windows_NT'" />
</ItemGroup>
<!-- Game framework -->
<Choose>
<When Condition="'$(GameFramework)' == 'Xna'">
<ItemGroup>
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="$(CopyModReferencesToBuildOutput)" />
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="$(CopyModReferencesToBuildOutput)" />
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="$(CopyModReferencesToBuildOutput)" />
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="$(CopyModReferencesToBuildOutput)" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="$(CopyModReferencesToBuildOutput)" />
</ItemGroup>
</Otherwise>
</Choose>
<!--*********************************************
** Show validation messages
@@ -14,6 +14,7 @@
<ItemGroup>
<Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" />
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="False" />
</ItemGroup>
@@ -22,23 +23,6 @@
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
</ItemGroup>
<!-- Game framework -->
<Choose>
<When Condition="$(DefineConstants.Contains(SMAPI_FOR_XNA))">
<ItemGroup>
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<None Update="manifest.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
@@ -19,9 +19,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
public override void Apply(Harmony harmony, IMonitor monitor)
{
harmony.Patch(
original: Constants.GameFramework == GameFramework.Xna
? this.RequireMethod<SpriteBatch>("InternalDraw")
: this.RequireMethod<SpriteBatch>("CheckValid", new[] { typeof(Texture2D) }),
original: this.RequireMethod<SpriteBatch>("CheckValid", new[] { typeof(Texture2D) }),
postfix: this.GetHarmonyMethod(nameof(SpriteBatchPatcher.After_CheckValid))
);
}
@@ -30,13 +28,8 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/*********
** Private methods
*********/
#if SMAPI_FOR_XNA
/// <summary>The method to call after <see cref="SpriteBatch.InternalDraw"/>.</summary>
/// <param name="texture">The texture to validate.</param>
#else
/// <summary>The method to call after <see cref="SpriteBatch.CheckValid"/>.</summary>
/// <param name="texture">The texture to validate.</param>
#endif
private static void After_CheckValid(Texture2D texture)
{
if (texture?.IsDisposed == true)
@@ -15,6 +15,7 @@
<ItemGroup>
<Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" />
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
<Reference Include="StardewValley.GameData" HintPath="$(GamePath)\StardewValley.GameData.dll" Private="False" />
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="False" />
</ItemGroup>
@@ -24,23 +25,6 @@
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
</ItemGroup>
<!-- Game framework -->
<Choose>
<When Condition="$(DefineConstants.Contains(SMAPI_FOR_XNA))">
<ItemGroup>
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<None Update="i18n\*.json" CopyToOutputDirectory="PreserveNewest" />
<None Update="manifest.json" CopyToOutputDirectory="PreserveNewest" />
+24 -58
View File
@@ -40,12 +40,7 @@ namespace StardewModdingAPI
internal static GamePlatform Platform { get; } = (GamePlatform)Enum.Parse(typeof(GamePlatform), LowLevelEnvironmentUtility.DetectPlatform());
/// <summary>The game framework running the game.</summary>
internal static GameFramework GameFramework { get; } =
#if SMAPI_FOR_XNA
GameFramework.Xna;
#else
GameFramework.MonoGame;
#endif
internal static GameFramework GameFramework { get; } = GameFramework.MonoGame;
/// <summary>The game's assembly name.</summary>
internal static string GameAssemblyName => EarlyConstants.Platform == GamePlatform.Windows ? "Stardew Valley" : "StardewValley";
@@ -260,61 +255,32 @@ namespace StardewModdingAPI
removeAssemblyReferences.Add("StardewModdingAPI.Toolkit.CoreInterfaces");
targetAssemblies.Add(typeof(StardewModdingAPI.IManifest).Assembly);
// get changes for platform
if (Constants.Platform != Platform.Windows)
// XNA Framework before Stardew Valley 1.5.5
removeAssemblyReferences.AddRange(new[]
{
removeAssemblyReferences.AddRange(new[]
{
"Netcode",
"Stardew Valley"
});
targetAssemblies.Add(
typeof(StardewValley.Game1).Assembly // note: includes Netcode types on Linux/macOS
);
}
else
{
removeAssemblyReferences.Add(
"StardewValley"
);
targetAssemblies.AddRange(new[]
{
typeof(Netcode.NetBool).Assembly,
typeof(StardewValley.Game1).Assembly
});
}
"Microsoft.Xna.Framework",
"Microsoft.Xna.Framework.Game",
"Microsoft.Xna.Framework.Graphics",
"Microsoft.Xna.Framework.Xact"
});
targetAssemblies.Add(
typeof(Microsoft.Xna.Framework.Vector2).Assembly
);
// get changes for game framework
switch (framework)
{
case GameFramework.MonoGame:
removeAssemblyReferences.AddRange(new[]
{
"Microsoft.Xna.Framework",
"Microsoft.Xna.Framework.Game",
"Microsoft.Xna.Framework.Graphics",
"Microsoft.Xna.Framework.Xact"
});
targetAssemblies.Add(
typeof(Microsoft.Xna.Framework.Vector2).Assembly
);
break;
// `Netcode.dll` merged into the game assembly in Stardew Valley 1.5.5
removeAssemblyReferences.Add(
"Netcode"
);
case GameFramework.Xna:
removeAssemblyReferences.Add(
"MonoGame.Framework"
);
targetAssemblies.AddRange(new[]
{
typeof(Microsoft.Xna.Framework.Vector2).Assembly,
typeof(Microsoft.Xna.Framework.Game).Assembly,
typeof(Microsoft.Xna.Framework.Graphics.SpriteBatch).Assembly
});
break;
default:
throw new InvalidOperationException($"Unknown game framework '{framework}'.");
}
// Stardew Valley reference
removeAssemblyReferences.Add(
Constants.Platform == Platform.Windows
? "StardewValley"
: "Stardew Valley"
);
targetAssemblies.Add(
typeof(StardewValley.Game1).Assembly
);
return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences.ToArray(), targetAssemblies.ToArray());
}
+1 -8
View File
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using Microsoft.Xna.Framework;
using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Toolkit.Utilities;
using StardewValley;
@@ -52,13 +51,7 @@ namespace StardewModdingAPI.Framework.Content
this.Cache = reflection.GetField<Dictionary<string, object>>(contentManager, "loadedAssets").GetValue();
// get key normalization logic
if (Constants.GameFramework == GameFramework.Xna)
{
IReflectedMethod method = reflection.GetMethod(typeof(TitleContainer), "GetCleanPath");
this.NormalizeAssetNameForPlatform = path => method.Invoke<string>(path);
}
else
this.NormalizeAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load<T> logic
this.NormalizeAssetNameForPlatform = PathUtilities.NormalizePath; //this.NormalizeAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load<T> logic
}
/****
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
@@ -157,11 +158,8 @@ namespace StardewModdingAPI.Framework.Input
yield break;
// buttons
foreach (var pair in this.ButtonStates)
{
if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button))
yield return button.ToSButton();
}
foreach (Buttons button in this.GetPressedGamePadButtons())
yield return button.ToSButton();
// triggers
if (this.LeftTrigger > 0.2f)
@@ -201,7 +199,7 @@ namespace StardewModdingAPI.Framework.Input
rightThumbStick: this.RightStickPos,
leftTrigger: this.LeftTrigger,
rightTrigger: this.RightTrigger,
buttons: this.GetButtonBitmask() // MonoGame requires one bitmask here; don't specify multiple values
buttons: this.GetPressedGamePadButtons().ToArray()
);
return this.State.Value;
@@ -211,17 +209,14 @@ namespace StardewModdingAPI.Framework.Input
/*********
** Private methods
*********/
/// <summary>Get a bitmask representing the pressed buttons.</summary>
private Buttons GetButtonBitmask()
/// <summary>Get the pressed gamepad buttons.</summary>
private IEnumerable<Buttons> GetPressedGamePadButtons()
{
Buttons flag = 0;
foreach (var pair in this.ButtonStates)
{
if (pair.Value == ButtonState.Pressed && pair.Key.TryGetController(out Buttons button))
flag |= button;
yield return button;
}
return flag;
}
}
}
+1 -6
View File
@@ -6,7 +6,6 @@ using System.Threading;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Framework.Events;
using StardewModdingAPI.Framework.Reflection;
using StardewValley;
using StardewValley.Menus;
namespace StardewModdingAPI.Framework
@@ -150,11 +149,7 @@ namespace StardewModdingAPI.Framework
/// <param name="reflection">The reflection helper with which to access private fields.</param>
public static bool IsOpen(this SpriteBatch spriteBatch, Reflector reflection)
{
string fieldName = Constants.GameFramework == GameFramework.Xna
? "inBeginEndPair"
: "_beginCalled";
return reflection.GetField<bool>(Game1.spriteBatch, fieldName).GetValue();
return reflection.GetField<bool>(spriteBatch, "_beginCalled").GetValue();
}
}
}
+4 -4
View File
@@ -253,10 +253,10 @@ namespace StardewModdingAPI.Framework.Logging
switch (exception)
{
// audio crash
case InvalidOperationException ex when ex.Source == "Microsoft.Xna.Framework.Xact" && ex.StackTrace.Contains("Microsoft.Xna.Framework.Audio.AudioEngine..ctor"):
this.Monitor.Log("The game couldn't load audio. Do you have speakers or headphones plugged in?", LogLevel.Error);
this.Monitor.Log($"Technical details: {ex.GetLogSummary()}");
break;
//case InvalidOperationException ex when ex.Source == "Microsoft.Xna.Framework.Xact" && ex.StackTrace.Contains("Microsoft.Xna.Framework.Audio.AudioEngine..ctor"):
// this.Monitor.Log("The game couldn't load audio. Do you have speakers or headphones plugged in?", LogLevel.Error);
// this.Monitor.Log($"Technical details: {ex.GetLogSummary()}");
// break;
// missing content folder exception
case FileNotFoundException ex when ex.Message == "Couldn't find file 'C:\\Program Files (x86)\\Steam\\SteamApps\\common\\Stardew Valley\\Content\\XACT\\FarmerSounds.xgs'.": // path in error is hardcoded regardless of install path
@@ -4,7 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
/// <summary>Provides <see cref="SpriteBatch"/> method signatures that can be injected into mod code for compatibility between Linux/macOS or Windows.</summary>
/// <summary>Provides <see cref="SpriteBatch"/> method signatures that can be injected into mod code for compatibility with mods written for XNA Framework before Stardew Valley 1.5.5.</summary>
/// <remarks>This is public to support SMAPI rewriting and should not be referenced directly by mods.</remarks>
[SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Used via assembly rewriting")]
[SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/macOS.")]
@@ -18,14 +18,6 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
public SpriteBatchFacade(GraphicsDevice graphicsDevice) : base(graphicsDevice) { }
/****
** MonoGame signatures
****/
public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix? matrix)
{
base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, matrix ?? Matrix.Identity);
}
/****
** XNA signatures
****/
+1 -7
View File
@@ -16,9 +16,7 @@ using Microsoft.Xna.Framework;
#if SMAPI_FOR_WINDOWS
using Microsoft.Win32;
#endif
#if SMAPI_FOR_XNA
using System.Windows.Forms;
#endif
using Microsoft.Xna.Framework;
using Newtonsoft.Json;
using StardewModdingAPI.Enums;
using StardewModdingAPI.Events;
@@ -224,10 +222,6 @@ namespace StardewModdingAPI.Framework
this.Toolkit.JsonHelper.JsonSettings.Converters.Add(converter);
// add error handlers
#if SMAPI_FOR_XNA
Application.ThreadException += (sender, e) => this.Monitor.Log($"Critical thread exception: {e.Exception.GetLogSummary()}", LogLevel.Error);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
#endif
AppDomain.CurrentDomain.UnhandledException += (sender, e) => this.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error);
// add more lenient assembly resolver
+5 -2
View File
@@ -1,12 +1,15 @@
using System;
namespace StardewModdingAPI
{
/// <summary>The game framework running the game.</summary>
public enum GameFramework
{
/// <summary>The XNA Framework on Windows.</summary>
/// <summary>The XNA Framework, previously used on Windows.</summary>
[Obsolete("Stardew Valley no longer uses XNA Framework on any supported platform.")]
Xna,
/// <summary>The MonoGame framework, usually on non-Windows platforms.</summary>
/// <summary>The MonoGame framework.</summary>
MonoGame
}
}
+3 -5
View File
@@ -36,9 +36,6 @@ namespace StardewModdingAPI.Metadata
// rewrite for crossplatform compatibility
if (rewriteMods)
{
if (platformChanged)
yield return new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchFacade));
// rewrite for Stardew Valley 1.5
yield return new FieldReplaceRewriter(typeof(DecoratableLocation), "furniture", typeof(GameLocation), nameof(GameLocation.furniture));
yield return new FieldReplaceRewriter(typeof(Farm), "resourceClumps", typeof(GameLocation), nameof(GameLocation.resourceClumps));
@@ -48,8 +45,9 @@ namespace StardewModdingAPI.Metadata
yield return new HeuristicFieldRewriter(this.ValidateReferencesToAssemblies);
yield return new HeuristicMethodRewriter(this.ValidateReferencesToAssemblies);
// rewrite for 64-bit mode
// re-enable in Stardew Valley 1.5.5
// rewrite for Stardew Valley 1.5.5
if (platformChanged)
yield return new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchFacade));
//yield return new ArchitectureAssemblyRewriter();
// detect Harmony & rewrite for SMAPI 3.12 (Harmony 1.x => 2.0 update)
+1 -18
View File
@@ -30,32 +30,15 @@
<Reference Include="System.Runtime.Caching" Private="True" />
<Reference Include="GalaxyCSharp" HintPath="$(GamePath)\GalaxyCSharp.dll" Private="False" />
<Reference Include="Lidgren.Network" HintPath="$(GamePath)\Lidgren.Network.dll" Private="False" />
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
<Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="False" />
</ItemGroup>
<!-- Windows only -->
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<!-- Game framework -->
<Choose>
<When Condition="$(DefineConstants.Contains(SMAPI_FOR_XNA))">
<ItemGroup>
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" Private="False" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="MonoGame.Framework" HintPath="$(GamePath)\MonoGame.Framework.dll" Private="False" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<ProjectReference Include="..\SMAPI.Toolkit.CoreInterfaces\SMAPI.Toolkit.CoreInterfaces.csproj" />
<ProjectReference Include="..\SMAPI.Toolkit\SMAPI.Toolkit.csproj" />