I've managed to get Windows MSVC editor builds working about as well as Linux builds on my fork: https://github.com/lewiji/godot/tree/dotnet6
This fork includes patches mentioned in the details below for libicu compatibility, as well as specific fixes for MSVC compatibility.
I've also set up Github CI builds there which should be working for both Linux and Windows editors (unsure about export templates though at least the Linux ones are building currently).
On both platforms, I'd recommend putting the editor into "self-contained" mode by creating an empty file called _sc_
or ._sc_
in the same folder as the editor. This will prevent any config/editor settings spilling over from Mono versions of godot if you've used them before.
You also will have to add the GodotSharp/Tools/nupkgs
folder as a local nuget source; the Linux CI builds come with a script to do that, you can do it manually via the command dotnet nuget add source -n GodotSharp /full/path/to/GodotSharp/Tools/nupkgs
or by manually editing your NuGet.Config
file. Eventually this won't be necessary as the packages will be published to the public NuGet package source.
Also worth noting that currently the dotnet6 branch is a fair bit behind master which has received a lot of fixes and features as well as breaking changes in the meantime; the merge from dotnet6 into master isn't completely trivial and will take some effort, so if you've been warned not to start a serious project in Godot 4 alpha, this goes double for the dotnet6 branch as it stands today.
In the current dotnet6
branch, there is an issue with certain Linux distros (Arch, at least) where the .NET runtime somehow gets confused by the partial libicu
bundle data generated by the text_server_adv
module (possibly the stub icu locale data that is used there means .NET isn't receiving correct locale data, but I'm unsure).
This causes the binary to throw a System.TypeInitializationException
when the interop is initialised and calls System.Globalization.CompareInfo
for some locale operations - seemingly because the culture info is left undefined/empty (?). There is a workaround and a fix for this:
Workaround: use scons flags module_text_server_adv_enabled=no module_text_server_fb_enabled=yes
to disable the new text server and fallback to the old 3.x compatible text server. This disables the more advanced text features.
Fix: compiler flags were added to force libicu in the text server module to rename its internal functions with a suffix, which prevents this conflict: https://github.com/godotengine/godot/pull/59656/files
You can apply this to dotnet6 by applying these 2 patches in order (the first is a patch to fix another issue with a compilation flag builtin_icu=no
which prevents this bundling completely, though this isn't necessary for this problem, it's required for the second patch to succeed):
https://patch-diff.githubusercontent.com/raw/godotengine/godot/pull/59621.patch
https://patch-diff.githubusercontent.com/raw/godotengine/godot/pull/59656.patch
Once patched, compile as below and
I now have a working linuxbsd
build thanks to the assistance of @raulsntos and others in the community. The crucial part missing from the original gist is that at the time of writing, the release_debug
target strips (or doesn't compile in the first place, unsure) the engine binary of the methods needed for the C# runtime to call out to the engine C++ code. On a freshly installed Ubuntu 20.04 VM, I ran:
$ scons p=linuxbsd tools=yes module_mono_enabled=yes
$ bin/godot.linuxbsd.tools.64.mono --generate-mono-glue modules/mono/glue
$ python3 modules/mono/build_scripts/build_assemblies.py --godot-output-dir=bin --godot-target=release_debug
$ dotnet nuget add source /home/lewiji/godot/bin/GodotSharp/Tools/nupkgs -n GodotSharp
This built a working editor in Debug mode, which allowed me to create and run C# scripts successfully. The accidental stripping of needed methods will be fixed later, and release_debug
works fine for the C# assemblies, just not for the main godot binary.
I've compiled the commit comments from the branch to date here to make it a bit easier to read:
https://gist.github.com/lewiji/29ae4f75f00c90ba68900630d4df537c
So here's what i've done so far, caveat, this could be wrong/incomplete as so far I haven't got everything to actually work, but it seems to be roughly along the right lines. I'm just using the dotnet6 branch as is for now, haven't attempted to merge it into master.
Compiling like normal, one of the commits says the mono_glue flag is no longer needed, since the glue generation is now done in C# by the mono module while compiling, however, I found that it was needed. I ran into an error initially, which I've described below (may be linux only, so try compiling first).
After generating the glue, there's an additional step to build the assemblies.
$ scons p=linuxbsd tools=yes module_mono_enabled=yes mono_glue=no target=release_debug -j$(nproc)
$ ./bin/godot.linuxbsd.opt.tools.64.mono --generate-mono-glue modules/mono/glue
On both of my linux machines, trying to compile the engine this way immediately gives a RuntimeError, saying that it can't find: /usr/share/dotnet/packs/Microsoft.NETCore.App.Host.linux-x64/6.0.2/runtimes/linux-x64/native/libnethost.a:
At least on Arch and Fedora where I tried this, that path is actually platform specific, in this case the linux
part of the folder name Microsoft.NETCore.App.Host.linux-x64
is actually either arch
or fedora.34
on my systems. I guess this is dependent on the package manager used, maybe it doesn't apply to other platforms.
So, looking through the trace, I need to modify modules/mono/build_scripts/mono_configure.py
. On line 115, inside the names_map
declaration in the determine_runtime_identifier(env):
function I changed:
"linuxbsd": "linux",
to:
"linuxbsd": "arch",
Which then correctly sets up the path to the runtime, and when re-running scons, the compilation succeeds.
This generates the binaries and nuget packages for the C# integration and tools.
$ ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=/home/ljp/dotnet6/bin --godot-target=release_debug --godot-platform=linuxbsd
This outputs a GodotSharp
folder into the bin folder which includes assemblies for GodotSharp
, GodotSharpEditor
, and GodotPlugins
in the API
subfolder, and in the Tools
subfolder, various GodotTools
binaries that seem to be to do with logging, messaging, shared components. also inside the Tools folder is a nupkgs
folder which contains the nuget packages for local sourcing them into the project
Now the glue and the assemblies have been created, we can build the final binary,
$ scons p=linuxbsd tools=yes module_mono_enabled=yes mono_glue=yes target=release_debug -j$(nproc)
So far so good, now the issue is that though the editor binary runs and opens the project manager, and I can create new projects, I can't actually open any projects, I get an exception:
┌─[lewiji][roguelike][~/godot/dotnet6]
└─▪
└─▪ System.InvalidOperationException: Dependency resolution failed for component /home/lewiji/dev/test1244/.godot/mono/temp/bin/Debug/test1244.dll with error code -2147450734. Detailed error: Failed to locate managed application [/home/lewiji/dev/test1244/.godot/mono/temp/bin/Debug/test1244.dll]
at System.Runtime.Loader.AssemblyDependencyResolver..ctor(String componentAssemblyPath)
at GodotPlugins.PluginLoadContext..ctor(String pluginPath, ICollection`1 sharedAssemblies, AssemblyLoadContext mainLoadContext) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs:line 18
at GodotPlugins.Main.LoadPlugin(String assemblyPath) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs:line 138
at GodotPlugins.Main.LoadProjectAssembly(Char* nAssemblyPath) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs:line 79
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.TypeInitializationException: The type initializer for 'Godot.EditorPlugin' threw an exception.
---> System.EntryPointNotFoundException: Unable to find an entry point named 'godotsharp_string_new_with_utf16_chars' in shared library '__Internal'.
at Godot.NativeInterop.NativeFuncs.godotsharp_string_new_with_utf16_chars(godot_string& r_dest, Char* p_contents)
at Godot.NativeInterop.Marshaling.ConvertStringToNative(String p_mono_string) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs:line 811
at Godot.NativeInterop.NativeFuncs.godotsharp_string_name_new_from_string(String name) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs:line 49
at Godot.StringName..ctor(String name) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs:line 65
at Godot.StringName.op_Implicit(String from) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs:line 74
at Godot.EditorPlugin..cctor() in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharpEditor/Generated/GodotObjects/EditorPlugin.cs:line 50
--- End of inner exception stack trace ---
at Godot.EditorPlugin..ctor() in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharpEditor/Generated/GodotObjects/EditorPlugin.cs:line 59
at GodotTools.GodotSharpEditor..ctor()
at GodotTools.GodotSharpEditor.InternalCreateInstance() in /home/ljp/dotnet6/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs:line 551
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at GodotPlugins.Main.LoadToolsAssembly(Char* nAssemblyPath) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs:line 116
ERROR: FATAL: Condition "editor_plugin_obj == nullptr" is true.
at: _editor_init_callback (modules/mono/csharp_script.cpp:1175)
================================================================
handle_crash: Program crashed with signal 4
Engine version: Godot Engine v4.0.alpha.mono.custom_build (8b85bc71595e1b95f66857d13a6558d492505927)
Dumping the backtrace. Please include this when reporting the bug on https://github.com/godotengine/godot/issues
[1] /usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.2/libcoreclr.so(+0x4b1b2e) [0x7f4b1c5d1b2e] (??:0)
[2] /usr/lib/libc.so.6(+0x42560) [0x7f4b4159c560] (??:0)
[3] /home/lewiji/godot/dotnet6/godot.linuxbsd.opt.tools.64.mono() [0x1092ecf] (??:0)
[4] /home/lewiji/godot/dotnet6/godot.linuxbsd.opt.tools.64.mono() [0x1b10043] (??:0)
[5] /home/lewiji/godot/dotnet6/godot.linuxbsd.opt.tools.64.mono() [0xa793b6] (??:0)
[6] /home/lewiji/godot/dotnet6/godot.linuxbsd.opt.tools.64.mono() [0xa2bd65] (??:0)
[7] /usr/lib/libc.so.6(+0x2d310) [0x7f4b41587310] (??:0)
[8] /usr/lib/libc.so.6(__libc_start_main+0x81) [0x7f4b415873c1] (??:0)
[9] /home/lewiji/godot/dotnet6/godot.linuxbsd.opt.tools.64.mono() [0xa3607e] (??:0)
-- END OF BACKTRACE --
================================================================
Ok, so the built project DLL doesn't exist, and I guess currently it just expects that it will, rather than the default behaviour of loading a regular, non-C# project and creating the C# stuff later. I noted in the commit log that currently the dotnet6 branch doesn't do the automated copying over of the required assemblies, so I tried to set up a C# project manually using the info I could glean from the repo changes:
- used the official alpha 3 build to create a new Godot project called "testdotnet62",
- opened the new godot project in official alpha 3, and exited godot editor again,
- created a new, blank .NET Solution in the new project's folder from Rider,
- added a
.csproj
file as follows:
<Project Sdk="Godot.NET.Sdk/4.0.0-dev8">
<PropertyGroup>
<RootNamespace>testdotnet62</RootNamespace>
<LangVersion>10</LangVersion>
<Nullable>enable</Nullable>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>
And added it to my solution. Note the Sdk version is 4.0.0-dev8
, LangVersion
is 10, and TargetFramework
is net6.0
. These settings are found in the changes to the mono editor module projects themselves, so I assumed the game config would be similar.
This won't actually load yet, because the 4.0.0-dev8
Sdk version is in this new local nuget package, rather than copied into the project as an assembly or published on nuget.org. As noted in a commit comment, you can use the dotnet
cli tool to add the folder outputted from compiling Godot in bin/GodotSharp/Tools/nupkgs
:
$ dotnet nuget add source ~/godot/dotnet6/GodotSharp/Tools/nupkgs/ -n GodotSharpTools
If I then open the project in Rider and use the NuGet package manager, I can now see this GodotSharpTools
source in the nuget feeds list, and 3 packages are implicitly installed, I guess automatically by the Sdk dependency in the csproj: Godot.SourceGenerators
, GodotSharp
, and GodotSharpEditor
. It's important I guess to check these are definitely installed from your local source and not nuget.org, for example, though Godot.SourceGenerators
reports being installed as 4.0.0-0
in the nuget panel, and it even offers to upgrade it to 4.0.0-dev8
(which doesn't seem to do anything), if I navigate to the assembly in the Solution panel and open it in file I can see it's coming from the 4.0.0-dev8
package folder in ~/.nuget/packagesgodot.sourcegenerators/
.)
Now the solution/project will load and I can see in my solution tree that the dependency root is .NET 6.0
, under Assemblies > Implicit
I can see GodotSharp
and GodotSharpEditor
, under Analyzers
I can see Godot.SourceGenerators
.
At this point, I can build the solution and it outputs the .dll
that it was complaining about in the stack trace earlier. Great! Let's try again!
┌─[lewiji][roguelike][~/godot/dotnet6]
└─▪ System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.TypeInitializationException: The type initializer for 'Godot.EditorPlugin' threw an exception.
---> System.EntryPointNotFoundException: Unable to find an entry point named 'godotsharp_string_new_with_utf16_chars' in shared library '__Internal'.
at Godot.NativeInterop.NativeFuncs.godotsharp_string_new_with_utf16_chars(godot_string& r_dest, Char* p_contents)
at Godot.NativeInterop.Marshaling.ConvertStringToNative(String p_mono_string) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs:line 811
at Godot.NativeInterop.NativeFuncs.godotsharp_string_name_new_from_string(String name) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs:line 49
at Godot.StringName..ctor(String name) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs:line 65
at Godot.StringName.op_Implicit(String from) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs:line 74
at Godot.EditorPlugin..cctor() in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharpEditor/Generated/GodotObjects/EditorPlugin.cs:line 50
--- End of inner exception stack trace ---
at Godot.EditorPlugin..ctor() in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotSharpEditor/Generated/GodotObjects/EditorPlugin.cs:line 59
at GodotTools.GodotSharpEditor..ctor()
at GodotTools.GodotSharpEditor.InternalCreateInstance() in /home/ljp/dotnet6/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs:line 551
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at GodotPlugins.Main.LoadToolsAssembly(Char* nAssemblyPath) in /home/ljp/dotnet6/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs:line 116
ERROR: FATAL: Condition "editor_plugin_obj == nullptr" is true.
at: _editor_init_callback (modules/mono/csharp_script.cpp:1175)
================================================================
handle_crash: Program crashed with signal 4
Engine version: Godot Engine v4.0.alpha.mono.custom_build (8b85bc71595e1b95f66857d13a6558d492505927)
Dumping the backtrace. Please include this when reporting the bug on https://github.com/godotengine/godot/issues
[1] /usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.2/libcoreclr.so(+0x4b1b2e) [0x7f2054dcab2e] (??:0)
[2] /usr/lib/libc.so.6(+0x42560) [0x7f2094c16560] (??:0)
[3] /home/lewiji/godot/dotnet6/godot.linuxbsd.opt.tools.64.mono() [0x1092ecf] (??:0)
[4] /home/lewiji/godot/dotnet6/godot.linuxbsd.opt.tools.64.mono() [0x1b10043] (??:0)
[5] /home/lewiji/godot/dotnet6/godot.linuxbsd.opt.tools.64.mono() [0xa793b6] (??:0)
[6] /home/lewiji/godot/dotnet6/godot.linuxbsd.opt.tools.64.mono() [0xa2bd65] (??:0)
[7] /usr/lib/libc.so.6(+0x2d310) [0x7f2094c01310] (??:0)
[8] /usr/lib/libc.so.6(__libc_start_main+0x81) [0x7f2094c013c1] (??:0)
[9] /home/lewiji/godot/dotnet6/godot.linuxbsd.opt.tools.64.mono() [0xa3607e] (??:0)
-- END OF BACKTRACE --
================================================================
...ah.
Well, at least it's not complaining about being unable to resolve the assembly, but we're still getting this Godot.EditorPlugin
exception about something called godotsharp_string_new_with_utf16_chars
.
Now, I'm pretty sure that the move from dynamic lookups to NativeInterop.NativeFuncs
via this __Internal
shared library happened in one of the dotnet6
branch commits, so will investigate further, once I'm done with this pesky interruption called the "day job" ;)
I half suspect it might be an issue on my machine, so it'd be good to see if anyone else has success with this - since at the trace is coming from libc
, and my host machine is Arch linux, which I know has had a few issues with libc
compatibility recently, as the package was without a maintainer for a long time, and was only recently updated after being left out of date for quite some time (years?). I've seen at least one other piece of software break with a stacktrace about libc
in the last 2 weeks as a result, so it's possible that's the cause. I built the binary in a Fedora 34 VM, to get as close as possible to the official builds, so next I'll try compiling directly in arch, and if all else fails, Windows, to see what happens there.
All I could find is dotnet/runtime#43631, which the author seems to have solved by re-installing libicu. Not sure if it's the same problem as yours.