blish-hud / Blish-HUD

A Guild Wars 2 overlay with extreme extensibility through compiled modules.
https://blishhud.com
MIT License
311 stars 60 forks source link

Update MonoGame to 3.8.1 / .NET 6 #924

Open sliekens opened 9 months ago

sliekens commented 9 months ago

Discussion Reference

https://discord.com/channels/531175899588984842/536970543736291346/1163095679602794627

The primary motivation for this update is that .NET Framework is considered obsolete. For instance, there will be no new MonoGame releases for .NET Framework. New development should happen on .NET 6 or newer.

(Cross-platform compatibility is currently not a goal, even though .NET 6 runs on Linux, much more work is required to make Blish work on Linux due to dependencies on the Win32 API.)

Is this a breaking change?

Yes, this affects Blish HUD development and module development: you need to have .NET 6 SDK installed because MonoGame 3.8.1 discontinued support for all older .NET versions.

Modules compiled for MonoGame 3.8.0 / .NET Framework have a high chance to crash when loaded in this new version, so a major version bump is in order.

Current issues are listed in detail at the bottom of this description.

Technically

I followed the migration steps in https://github.com/MonoGame/monogame.github.io/blob/main/articles/migrate_38.md

After this change, you can uninstall the .NET Core 3.1 runtime which is out of support.

The new version of the MonoGame.Content.Builder.Task no longer includes a bundled version of the mgcb utility. Instead it uses dotnet mgcb that is available on your PATH. It will execute dotnet tools restore prior to building, so I included a dotnet-tools.json file that references the latest version of mgcb. It is not necessary to install mgcb as a global tool.

The task has the following options which can be set at build time (or in the csproj):

  <PropertyGroup>
    <DotnetCommand Condition="'$(DotnetCommand)' == ''">dotnet</DotnetCommand>
    <EnableMGCBItems Condition="'$(EnableMGCBItems)' == ''">true</EnableMGCBItems>
    <MGCBCommand Condition="'$(MGCBCommand)' == ''">mgcb</MGCBCommand>
  </PropertyGroup>

I updated all workflows to install .NET 6 instead of .NET Core 3.1 and also updated the README accordingly.

Current issues

Unfortunately it is not possible to load modules that depend on older versions of MonoGame. For example, the Pathing module contains shaders compiled for MonoGame 3.8.0 and those need to be recompiled for MonoGame 3.8.1.

System.Exception: This MGFX effect is for an older release of MonoGame and needs to be rebuilt.
   at Microsoft.Xna.Framework.Graphics.Effect.ReadHeader(Byte[] effectCode, Int32 index)
   at Microsoft.Xna.Framework.Graphics.Effect..ctor(GraphicsDevice graphicsDevice, Byte[] effectCode, Int32 index, Int32 count)
   at Blish_HUD.Modules.Managers.ContentsManager.GetEffect(String effectPath) in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\Modules\Managers\ContentsManager.cs:line 73
   at BhModule.Community.Pathing.SharedPackState.InitShaders() in C:\BhApps\BhudRequestFor\work\a86UuE3uUKD9KrNr8349w\State\SharedPackState.cs:line 153
   at BhModule.Community.Pathing.SharedPackState..ctor(PathingModule module) in C:\BhApps\BhudRequestFor\work\a86UuE3uUKD9KrNr8349w\State\SharedPackState.cs:line 52
   at BhModule.Community.Pathing.PackInitiator..ctor(String watchPath, PathingModule module, IProgress`1 loadingIndicator) in C:\BhApps\BhudRequestFor\work\a86UuE3uUKD9KrNr8349w\PackInitiator.cs:line 47
   at BhModule.Community.Pathing.PathingModule.LoadAsync() in C:\BhApps\BhudRequestFor\work\a86UuE3uUKD9KrNr8349w\PathingModule.cs:line 0
   at Blish_HUD.Modules.Module.InternalLoadAsync() in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\Modules\Module.cs:line 102

I noticed other modules can have various problems with missing methods. I suspect that this is because the modules are compiled for .NET Framework and can be resolved by recompiling for .NET 6 (or .NET Standard)

Nekres.Musician.MusicianModule | Module "Minstrel Protocol (Nekres.Musician_Module) v0.8.4" had an unhandled exception while loading.
System.MissingMethodException: Method not found: 'Void LiteDB.Async.LiteDatabaseAsync..ctor(LiteDB.ConnectionString, LiteDB.BsonMapper)'.
   at Nekres.Musician.UI.MusicSheetService.LoadDatabase()
   at Nekres.Musician.MusicianModule.LoadAsync() in C:\BhApps\BhudRequestFor\work\4wyg9njBLk67jcICB4hcGA\Musician Module\MusicianModule.cs:line 136
   at Blish_HUD.Modules.Module.InternalLoadAsync() in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\Modules\Module.cs:line 102

Next steps

In order to move this forward, I believe the module template will have to be updated. Modules must have their TargetFramework updated to net6.0 and MonoGame updated to 3.8.1. Blish HUD itself must have its version bumped to 2.0.0 so modules can have their manifest updated to require Blish HUD 2.0.0.

{
    "dependencies": {
        "bh.blishhud" : "^2.0.0"
    }
}

Blish HUD 2.0.0 should block modules developed for the .NET Framework. I think many modules use a version range like >=1.0.0 which does not exclude 2.0.0, so another mechanism is needed to detect compatibility.

For transitioning from MonoGame 3.8.0 to 3.8.1, it would be preferable if modules could somehow target both versions, such that Blish HUD can choose the right assembly based on which MonoGame version it's running on. If we build such a mechanism now, Blish will be more resistant to future breaking changes to MonoGame.

Documentation that references .NET Framework / Visual Studio 2019 will have to be updated to .NET 6 and Visual Studio 2022.

sliekens commented 9 months ago

It seems MonoGame itself also needs to be updated to 3.8.1 as it's crashing with 3.8.0 https://docs.monogame.net/articles/migrate_38.html

System.Exception: This MGFX effect seems to be for a newer release of MonoGame.

Full stack trace

System.TypeInitializationException: The type initializer for 'Blish_HUD.Controls.Effects.ScrollingHighlightEffect' threw an exception. ---> System.Exception: This MGFX effect seems to be for a newer release of MonoGame.
   at Microsoft.Xna.Framework.Graphics.Effect.ReadHeader(Byte[] effectCode, Int32 index)
   at Microsoft.Xna.Framework.Graphics.Effect..ctor(GraphicsDevice graphicsDevice, Byte[] effectCode, Int32 index, Int32 count)
   at Microsoft.Xna.Framework.Content.EffectReader.Read(ContentReader input, Effect existingInstance)
   at Microsoft.Xna.Framework.Content.ContentTypeReader`1.Read(ContentReader input, Object existingInstance)
   at Microsoft.Xna.Framework.Content.ContentReader.InnerReadObject[T](T existingInstance)
   at Microsoft.Xna.Framework.Content.ContentReader.ReadObject[T]()
   at Microsoft.Xna.Framework.Content.ContentReader.ReadAsset[T]()
   at Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[T](String assetName, Action`1 recordDisposableObject)
   at Microsoft.Xna.Framework.Content.ContentManager.Load[T](String assetName)
   at Blish_HUD.Controls.Effects.ScrollingHighlightEffect..cctor() in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\Controls\Effects\ScrollingHighlightEffect.cs:line 24
   --- End of inner exception stack trace ---
   at Blish_HUD.Controls.Effects.ScrollingHighlightEffect..ctor(Control assignedControl)
   at Blish_HUD.Controls.MenuItem.Initialize() in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\Controls\MenuItem.cs:line 186
   at Blish_HUD.Controls.MenuItem..ctor(String text, AsyncTexture2D icon) in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\Controls\MenuItem.cs:line 182
   at Blish_HUD.OverlayService.PrepareSettingsTab() in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\OverlayService.cs:line 118
   at Blish_HUD.OverlayService.Initialize() in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\OverlayService.cs:line 111
   at Blish_HUD.GameService.DoInitialize(BlishHud game) in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\GameService.cs:line 51
   at Blish_HUD.BlishHud.Initialize() in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\BlishHud.cs:line 78
   at Microsoft.Xna.Framework.Game.DoInitialize()
   at Microsoft.Xna.Framework.Game.Run(GameRunBehavior runBehavior)
   at Blish_HUD.Program.Main(String[] args) in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\Program.cs:line 92
sliekens commented 9 months ago

Mostly everything works with .NET 6, but many modules will also need to be updated for .NET 6 and the new MonoGame version.

For example when loading the Pathing module, there is a specific error about MGFX effects.

Inner exception

System.Exception: This MGFX effect is for an older release of MonoGame and needs to be rebuilt.
   at Microsoft.Xna.Framework.Graphics.Effect.ReadHeader(Byte[] effectCode, Int32 index)
   at Microsoft.Xna.Framework.Graphics.Effect..ctor(GraphicsDevice graphicsDevice, Byte[] effectCode, Int32 index, Int32 count)
   at Blish_HUD.Modules.Managers.ContentsManager.GetEffect(String effectPath) in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\Modules\Managers\ContentsManager.cs:line 73
   at BhModule.Community.Pathing.SharedPackState.InitShaders() in C:\BhApps\BhudRequestFor\work\a86UuE3uUKD9KrNr8349w\State\SharedPackState.cs:line 153
   at BhModule.Community.Pathing.SharedPackState..ctor(PathingModule module) in C:\BhApps\BhudRequestFor\work\a86UuE3uUKD9KrNr8349w\State\SharedPackState.cs:line 52
   at BhModule.Community.Pathing.PackInitiator..ctor(String watchPath, PathingModule module, IProgress`1 loadingIndicator) in C:\BhApps\BhudRequestFor\work\a86UuE3uUKD9KrNr8349w\PackInitiator.cs:line 47
   at BhModule.Community.Pathing.PathingModule.LoadAsync() in C:\BhApps\BhudRequestFor\work\a86UuE3uUKD9KrNr8349w\PathingModule.cs:line 0
   at Blish_HUD.Modules.Module.InternalLoadAsync() in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\Modules\Module.cs:line 102
   at Blish_HUD.Modules.Module.CheckForLoaded() in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\Modules\Module.cs:line 117
   at Blish_HUD.Modules.Module.DoUpdate(GameTime gameTime) in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\Modules\Module.cs:line 146
   at Blish_HUD.ModuleService.Update(GameTime gameTime) in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\ModuleService.cs:line 379
   at Blish_HUD.GameService.DoUpdate(GameTime gameTime) in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\GameServices\GameService.cs:line 80
   at Blish_HUD.BlishHud.Update(GameTime gameTime) in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\BlishHud.cs:line 136
   at Microsoft.Xna.Framework.Game.DoUpdate(GameTime gameTime)
   at Microsoft.Xna.Framework.Game.Tick()
   at MonoGame.Framework.WinFormsGameWindow.TickOnIdle(Object sender, EventArgs e)
   at System.Windows.Forms.Application.ThreadContext.Interop.Mso.IMsoComponent.FDoIdle(msoidlef grfidlef) in /_/src/System.Windows.Forms/src/System/Windows/Forms/Application.ThreadContext.cs:line 1508
   at System.Windows.Forms.Application.ComponentManager.Interop.Mso.IMsoComponentManager.FPushMessageLoop(UIntPtr dwComponentID, msoloop uReason, Void* pvLoopData) in /_/src/System.Windows.Forms/src/System/Windows/Forms/Application.ComponentManager.cs:line 369
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(msoloop reason, ApplicationContext context) in /_/src/System.Windows.Forms/src/System/Windows/Forms/Application.ThreadContext.cs:line 1117
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop reason, ApplicationContext context) in /_/src/System.Windows.Forms/src/System/Windows/Forms/Application.ThreadContext.cs:line 981
   at System.Windows.Forms.Application.Run(Form mainForm) in /_/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs:line 1188
   at MonoGame.Framework.WinFormsGameWindow.RunLoop()
   at Microsoft.Xna.Framework.Game.Run(GameRunBehavior runBehavior)
   at Blish_HUD.Program.Main(String[] args) in C:\Users\Steven\source\repos\Blish-HUD\Blish HUD\Program.cs:line 92
sonarcloud[bot] commented 9 months ago

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

Tharylia commented 9 months ago

Multi targeting for a module is possible in theory but there needs to be an updated blish nuget first.

After that you can just use conditions to switch between the nugets depending on which version you build for.

<PackageReference Include="BlishHUD" Version="1.0.0" Condition="'$(TargetFramework)' == 'net48'">
    <ExcludeAssets>runtime;contentFiles</ExcludeAssets>
</PackageReference>

The note for the blish manifest dependency using >= instead of ^ is a good point. I will change that in modules for now