godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine

Home Page:https://godotengine.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TargetFramework .NET Standard 2.0+ crashes on launch, can't find assembly System.Text.Json

raulsntos opened this issue · comments

Godot version:

  • 3.2.3 mono stable
  • 4.0 mono compiled at commit d425cf6

OS/device including version: Ubuntu 20.04

Issue description:

Godot 3.2.3 was released and includes PR #41408 which, as I understand, should allow me to change the framework to .NET Standard 2.0 or greater. So I tried changing the framework to netstandard2.1 and found this issue.

The game compiles fine but when launching it, it immediately crashes with the error:

E 0:00:00.509   debug_send_unhandled_exception_error: System.IO.FileNotFoundException: Could not load file or assembly 'System.Text.Json, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies.
  <C++ Error>   Unhandled exception
  <C++ Source>  modules/mono/mono_gd/gd_mono_utils.cpp:424 @ debug_send_unhandled_exception_error()

The .csproj is as follows:

<Project Sdk="Godot.NET.Sdk/3.2.3">
  <PropertyGroup>
    <TargetFramework>netstandard2.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="System.Text.Json" Version="4.7.2" />
  </ItemGroup>
</Project>

This only happens when changing the framework to netstandard2.0 or netstandard2.1, I have found that both of those throw that error, but if the framework is kept as net472 it works fine. I'm not sure if 3.2.3 is supposed to work well with .NET Standard since the default is still net472 so I tried compiling master as well and got the same results.

Steps to reproduce:

  1. Create a new project
  2. Change TargetFramework to netstandard2.1
  3. Add System.Text.Json package (dotnet add package System.Text.Json)
  4. Launch Game in Editor
  5. Game builds and launches but immediately crashes

Minimal reproduction project:
NetStd21.zip

commented

I looked a little into this.

First, it looks like the place Godot/Mono is expecting to find the DLL is .mono\temp\bin\Debug. So the first question is why the package's DLLs show up there for net472 but not netstandard2.0 / netstandard2.1.

I don't know the answer. But, adding <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> to a PropertyGroup in the csproj makes them show up.


But with netstandard2.1 there's still an exception thrown at runtime after that workaround, it just changes. New exception:

Unhandled Exception:
System.TypeInitializationException: The type initializer for 'System.Text.Json.JsonSerializer' threw an exception. ---> System.MissingMethodException: Method not found: int System.Text.Encodings.Web.TextEncoder.FindFirstCharacterToEncodeUtf8(System.ReadOnlySpan`1<byte>)
  at System.Text.Json.JsonEncodedText.EncodeHelper (System.ReadOnlySpan`1[T] utf8Value, System.Text.Encodings.Web.JavaScriptEncoder encoder) [0x00000] in <3e024d1008104aac9fe12198f4c68344>:0
  at System.Text.Json.JsonEncodedText.TranscodeAndEncode (System.ReadOnlySpan`1[T] value, System.Text.Encodings.Web.JavaScriptEncoder encoder) [0x00033] in <3e024d1008104aac9fe12198f4c68344>:0
  at System.Text.Json.JsonEncodedText.Encode (System.ReadOnlySpan`1[T] value, System.Text.Encodings.Web.JavaScriptEncoder encoder) [0x00014] in <3e024d1008104aac9fe12198f4c68344>:0
...

I found this issue about it: dotnet/runtime#1460. The version of System.Memory.dll that ends up at .mono\temp\bin\Debug conflicts with the one provided by Mono, so there are actually two different versions of ReadOnlySpan<byte> loaded that aren't matching up with each other. (If that issue really does apply to this situation.)

The workaround mentioned in that issue is to add <PackageReference Include="System.Memory" Version="4.5.3" IncludeAssets="None" /> to an ItemGroup in the csproj to make sure that Mono's version of System.Memory.dll is used. (Note: 4.5.3 is old and causes a build warning--4.5.4 is the new version.) This workaround does make System.Memory.dll disappear from .mono, but the error doesn't change for me.

Not sure where to go from there. I don't see any explanation on that thread of how you can actually tell what ReadOnlySpan<byte> implementations are being loaded. 🙁

So I've been having similar issues with other packages, specifically MessagePack.

Looking at the minimal repro project, it looks like even when you
apply 31's workaround of <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> , the "wrong" dll gets copied from C:\<User>\.nuget\packages\system.text.json\4.7.2\lib.
It's copying files from the netstandard2.0 folder.

Given my limited understanding of the build process, I don't know if that's valid considering the lack of a netstandard2.1 folder.

That being said, when I work with MessagePack, there is a netstandard2.1 netcoreapp2.1 (I misread the folder name earlier) folder with a MessagePack.dll, and it's still drawing from netstandard2.0. ( I understand now that it probably shouldn't draw from netcoreapp2.1 either now.)

Looking at the .deps file that shows up in.mono\temp\bin\Debug there's a lot of stuff labeled netstandard2.0 despite the project targeting netstandard2.1. Is that fine if the dependencies don't use features of 2.1?

edit: I just realized I didn't actually upload anything supporting this. I've modified the OP minimal program, so here it is.

Changing the target to netstandard2.1 causes a failure to find the "ConvertToJSON" function.

NetStd21.zip

It looks like this can be solved if we set CopyLocalLockFileAssemblies to true in our Sdk (it's disabled by default for .NETStandard projects and non-Exe .NETCoreApp projects).
Meanwhile you can add this manually to your csproj:

  <PropertyGroup>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
  </PropertyGroup>

Oh, never mind. I'm blind. That's what your comment says...

No idea if this is a bug with Mono, the packages or something Godot is doing wrong.
I made sure Godot was using Mono's assemblies. In my case the exception is: System.TypeLoadException: VTable setup of type System.Text.Json.Utf8JsonWriter failed. Maybe because of the Mono version I'm using (6.12.0.107).

The log file has this:

no implementation for interface method System.IAsyncDisposable::DisposeAsync() in class System.Text.Json.Utf8JsonWriter

Looking at the minimal repro project, it looks like even when you
apply 31's workaround of <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> , the "wrong" dll gets copied from C:\<User>\.nuget\packages\system.text.json\4.7.2\lib.
It's copying files from the netstandard2.0 folder.

That should be the correct assemblies as .NET Standard 2.0 is compatible with 2.1.

I tried again with Godot 3.2.4 RC1 and updated to Mono 6.12.0.107 but I did not get that exception, building with netstandard2.1 and CopyLocalLockFileAssemblies I'm getting the same exception that 31 gets:

System.TypeInitializationException: The type initializer for 'System.Text.Json.JsonSerializer' threw an exception. ---> System.MissingMethodException: Method not found: int System.Text.Encodings.Web.TextEncoder.FindFirstCharacterToEncodeUtf8(System.ReadOnlySpan`1<byte>)
  at System.Text.Json.JsonEncodedText.EncodeHelper (System.ReadOnlySpan`1[T] utf8Value, System.Text.Encodings.Web.JavaScriptEncoder encoder) [0x00000] in <7e3a59f5e4004edbb4b17c580799cc52>:0 
  at System.Text.Json.JsonEncodedText.TranscodeAndEncode (System.ReadOnlySpan`1[T] value, System.Text.Encodings.Web.JavaScriptEncoder encoder) [0x00033] in <7e3a59f5e4004edbb4b17c580799cc52>:0 
  at System.Text.Json.JsonEncodedText.Encode (System.ReadOnlySpan`1[T] value, System.Text.Encodings.Web.JavaScriptEncoder encoder) [0x00014] in <7e3a59f5e4004edbb4b17c580799cc52>:0 
  at System.Text.Json.JsonEncodedText.Encode (System.String value, System.Text.Encodings.Web.JavaScriptEncoder encoder) [0x00014] in <7e3a59f5e4004edbb4b17c580799cc52>:0 
  at System.Text.Json.JsonSerializer..cctor () [0x00042] in <7e3a59f5e4004edbb4b17c580799cc52>:0 
   --- End of inner exception stack trace ---
  at Main._Ready () [0x00001] in /home/raul/GodotProjects/NetStd21/Main.cs:14 

With netstandard2.0 and CopyLocalLockFileAssemblies I get no exception and it works.

Out of curiosity I tried net5.0 and CopyLocalLockFileAssemblies and I get a different exception:

System.TypeLoadException: Could not load type of field 'System.Text.Json.JsonSerializerOptions:_encoder' (14) due to: Could not load file or assembly 'System.Text.Encodings.Web, Version=4.0.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies.

Also I'm not sure how Godot builds the project, but from reading the CopyLocalLockFileAssemblies documentation it seems to me we are using dotnet build when maybe we should be using dotnet publish?

No idea if this is a bug with Mono, the packages or something Godot is doing wrong.

My guess is it has something to do with Godot since System.Text.Json works fine for me in non-Godot projects.

Cant reproduce the issue. I using the same package in a lot of caes. Can u give me a simple test project? @raulsntos

Did you try the linked minimal reproduction project in the first post? I can still reproduce it with Godot 3.3.

I get the same issue. After adding the System.Text.Json package from Nuget it builds fine. But when running JsonSerializer.Serialize(data) it gives the same exception as @raulsntos

I'm using Godot 3.3 and my project is set to netstandard2.1. Trying to use netstandard2.0 resolves the problem, you do lose some new .Net features but that is okay for me right now.

commented

I got the same error, but this fixed that issue. #48701 (comment)

I can confirm the System.Text.Json crashes with the same errors on Android using v3.52 RC 2.
I found a user mentioned that Newtonsoft's JSON will work.
I hope this can get fixed soon because Newtonsoft is slower than System.Text.Json with big files.

Fixed in master by #64089.

For users in 3.x, my recommendation would be to stay on .NET Framework 4.7.2. Keep in mind, this does not restrict you from using the latest version of C#1.

Footnotes

  1. Some features require runtime support, everything else should work but it may require polyfills (see https://github.com/Sergio0694/PolySharp).

A lot of time has passed, but I just now stumbled upon this issue myself. I'm using

  • Godot 3.6-beta3 (but it would very likely be the same on stable 3.5 I'd assume)
  • Godot-SDK Godot.NET.Sdk/3.3.0
  • .Net Target Framework: netstandard2.1

What I was trying is adding Sentry (library for sending crash reports), which internally uses System.Text.Json. The specific error I'm getting is:

VTable setup of type System.Text.Json.Utf8JsonWriter failed
  at System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[TStateMachine] (TStateMachine& stateMachine) [0x0002c] in <c84d94161bca414384015e66544862cf>:0 
  at Sentry.Protocol.Envelopes.Envelope.SerializeHeaderAsync (System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger logger, Sentry.Infrastructure.ISystemClock clock, System.Threading.CancellationToken cancellationToken) [0x0003d] in <4280cf04ff3343328bfffed38ba93a4f>:0 
  at Sentry.Protocol.Envelopes.Envelope.SerializeAsync (System.IO.Stream stream, Sentry.Extensibility.IDiagnosticLogger logger, Sentry.Infrastructure.ISystemClock clock, System.Threading.CancellationToken cancellationToken) [0x00024] in <4280cf04ff3343328bfffed38ba93a4f>:0 
  at Sentry.Internal.Http.EnvelopeHttpContent.SerializeToStreamAsync (System.IO.Stream stream, System.Net.TransportContext context) [0x0008f] in <4280cf04ff3343328bfffed38ba93a4f>:0 

Which hints at the same problems discussed here.

None of the things I tried worked, including

  • setting CopyLocalLockFileAssemblies in project settings
  • using different versions of Sentry
  • using different versions of System.Text.Json

So, AFAICT there are no workarounds... which sucks, considering 3.x was supposed to have long-term support :(

Am I missing anything? Has anybody made this work? I suspect lots of libraries have already moved to System.Text.Json from Newtonsoft.Json, considering it's the new standard. I assume none of those work in Godot 3.x?

@maximiliancsuk Can you target .NET Framework 4.7.2 instead in your project? Any library that targets .NET Standard 2.0 should be usable (it seems Sentry does).

3.x was supposed to have long-term support

It is 🙂. When you create a project in 3.x, it targets .NET Framework 4.7.2 which guarantees the most stable support. You have to manually modify the csproj to target .NET Standard 2.1, this is fine but it comes with some caveats. I'm sorry that this caused issues for you.

Keep in mind you can still use System.Text.Json and any other library that targets .NET Standard 2.0 in a project that targets .NET Framework 4.7.2. So my recommendation is to stay on .NET Framework 4.7.2 unless you have a very strong reason not to.

@maximiliancsuk Can you target .NET Framework 4.7.2 instead in your project? Any library that targets .NET Standard 2.0 should be usable (it seems Sentry does).

3.x was supposed to have long-term support

It is 🙂. When you create a project in 3.x, it targets .NET Framework 4.7.2 which guarantees the most stable support. You have to manually modify the csproj to target .NET Standard 2.1, this is fine but it comes with some caveats. I'm sorry that this caused issues for you.

Keep in mind you can still use System.Text.Json and any other library that targets .NET Standard 2.0 in a project that targets .NET Framework 4.7.2. So my recommendation is to stay on .NET Framework 4.7.2 unless you have a very strong reason not to.

Ohhh, you're right! I switched to .Net Standard 2.1 for some new features and totally forgot that it's not actually the default. It works as it should using .Net Framework 472.

Thanks a lot! :)