godotengine / godot

Godot Engine ā€“ Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.93k stars 21.15k forks source link

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

Open raulsntos opened 4 years ago

raulsntos commented 4 years ago

Godot version:

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

31 commented 3 years ago

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: https://github.com/dotnet/runtime/issues/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. šŸ™

ASavchenkov commented 3 years ago

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

neikeq commented 3 years ago

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>
neikeq commented 3 years ago

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

neikeq commented 3 years ago

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.

raulsntos commented 3 years ago

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.

kalysti commented 3 years ago

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

raulsntos commented 3 years ago

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

KuroiRoy commented 3 years ago

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.

altertain commented 2 years ago

I got the same error, but this fixed that issue. https://github.com/godotengine/godot/issues/48701#issuecomment-1179778240

ondesic commented 1 year ago

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.

raulsntos commented 1 year ago

Fixed in master by https://github.com/godotengine/godot/pull/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].

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

maximiliancsuk commented 9 months ago

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

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

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?

raulsntos commented 9 months ago

@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 :slightly_smiling_face:. 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 commented 9 months ago

@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! :)