I needed a way to embed Godot in .NET applications, and ended up facilitating HTML5 export for C# games, too, which really reinvigorated my passion for Godot in game jams. And for boring work, I needed parallel unit testing (across multiple ALCs).
But what is it…? I call it a C# front-end for the Godot engine, but really…
2dog is still Godot
All the Godot goodness still works, including programming with GodotSharp (their official C# SDK) and the 4.7 official editor, which was important to me for stability and trust (also, easier to swap out or extend).
The nuget packages are free and libre open source under the MIT license. 2dog uses libgodot and creates a choice of small .NET “host” applications that you can tailor to your needs - HTML5 browser export, embedding in WinForms, xUnit testing, etc.
Still hot off the press, always happy about visitors to the ⤜outfox⤏ discord or issues on our GitHub.



We started in Forward+ rendering so it is definitely possible that something went wrong when switching to Compatibility renderer. I did a blank test project and everything went fine so it must be something related to the project.
Yes! It’s open source and I made a branch with the renderer switched to Compatibility and our water shader removed (incompatible with Compatibility renderer). Should be able to just add 2dog right away and run it. (The regular desktop build runs fine)
You can find it here: https://gitlab.com/gmtk26zbandco/gmtk26-godot-game/-/tree/web-build
I added the source map and the stack trace looks more usable now:
Full error
[MONO] /__w/1/s/src/runtime/src/mono/mono/mini/aot-runtime-wasm.c:188 <disabled> Mc<@wasm_native_to_interp_GodotSharp_Godot_Bridge_ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance (http://127.0.0.1:8000/_framework/dotnet.runtime.js:3:172284 dotnet.native.wasm.wasm_trace_logger@:wasm-function[161353]:0x283a3f9 dotnet.native.wasm.eglib_log_adapter@:wasm-function[148375]:0x2640662 dotnet.native.wasm.monoeg_g_logv_nofree@:wasm-function[148280]:0x263e7bf dotnet.native.wasm.monoeg_g_log@:wasm-function[148282]:0x263e88f dotnet.native.wasm.monoeg_g_log_disabled@:wasm-function[148283]:0x263e8c4 dotnet.native.wasm.mono_wasm_get_interp_to_native_trampoline@:wasm-function[151868]:0x26f0907 dotnet.native.wasm.ves_pinvoke_method@:wasm-function[147551]:0x261556b dotnet.native.wasm.mono_interp_exec_method@:wasm-function[147539]:0x260893b dotnet.native.wasm.mono_thread_info_current@:wasm-function[148504]:0x26446fb dotnet.native.wasm.interp_throw_ex_general@:wasm-function[147541]:0x26149c6 dotnet.native.wasm.mono_interp_exec_method@:wasm-function[147539]:0x2608867 dotnet.native.wasm.interp_runtime_invoke@:wasm-function[147589]:0x261680e dotnet.native.wasm.mono_jit_runtime_invoke@:wasm-function[151658]:0x26e159e dotnet.native.wasm.do_runtime_invoke@:wasm-function[150592]:0x26aff97 dotnet.native.wasm.mono_runtime_invoke_checked@:wasm-function[150591]:0x26aff53 dotnet.native.wasm.mono_runtime_try_invoke_byrefs@:wasm-function[150687]:0x26b5306 dotnet.native.wasm.ves_icall_InternalInvoke@:wasm-function[149718]:0x268592e dotnet.native.wasm.ves_icall_InternalInvoke_raw@:wasm-function[149814]:0x2689e7b dotnet.native.wasm.do_icall@:wasm-function[147637]:0x26189f1 dotnet.native.wasm.do_icall_wrapper@:wasm-function[147549]:0x261538f dotnet.native.wasm.mono_interp_exec_method@:wasm-function[147539]:0x2608835 dotnet.native.wasm.mono_assertion_message_disabled@:wasm-function[148285]:0x263e908 dotnet.native.wasm.ftnptr_to_imethod@:wasm-function[147548]:0x2615271 dotnet.native.wasm.mono_interp_exec_method@:wasm-function[147539]:0x2608764 dotnet.native.wasm.interp_entry@:wasm-function[147626]:0x261817a dotnet.native.wasm.interp_entry_static_ret_4@:wasm-function[147665]:0x26198f5 dotnet.native.wasm.wasm_native_to_interp_GodotSharp_Godot_Bridge_ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance@:wasm-function[161284]:0x2838975) dotnet.native.wasm.CSharpScript::_create_instance(Variant const**, int, Object*, bool, Callable::CallError&)@:wasm-function[105210]:0x1bfe612 dotnet.native.wasm.CSharpScript::instance_create(Object*)@:wasm-function[105221]:0x1c012a3 dotnet.native.wasm.Object::set_script(Variant const&)@:wasm-function[137662]:0x2419e21 dotnet.native.wasm.SceneState::instantiate(SceneState::GenEditState) const@:wasm-function[42805]:0xa44c80 dotnet.native.wasm.PackedScene::instantiate(PackedScene::GenEditState) const@:wasm-function[42808]:0xa46e43 dotnet.native.wasm.Main::start()@:wasm-function[133508]:0x230c7e8 dotnet.native.wasm.GodotInstance::start()@:wasm-function[134260]:0x232b9bc dotnet.native.wasm.MethodBindTR<bool>::ptrcall(Object*, void const**, void*) const@:wasm-function[137157]:0x23ff93a dotnet.native.wasm.gdextension_object_method_bind_ptrcall(void const*, void*, void const* const*, void*)@:wasm-function[87320]:0x14f1f59 dotnet.native.wasm.do_icall@:wasm-function[147637]:0x26189d3 dotnet.native.wasm.do_icall_wrapper@:wasm-function[147549]:0x261538f dotnet.native.wasm.mono_interp_exec_method@:wasm-function[147539]:0x2608835 dotnet.native.wasm.interp_runtime_invoke@:wasm-function[147589]:0x261680e dotnet.native.wasm.mono_jit_runtime_invoke@:wasm-function[151658]:0x26e159e dotnet.native.wasm.do_runtime_invoke@:wasm-function[150592]:0x26aff97 dotnet.native.wasm.mono_runtime_invoke_checked@:wasm-function[150591]:0x26aff53 dotnet.native.wasm.mono_runtime_try_invoke_byrefs@:wasm-function[150687]:0x26b5306 dotnet.native.wasm.ves_icall_InternalInvoke@:wasm-function[149718]:0x268592e dotnet.native.wasm.ves_icall_InternalInvoke_raw@:wasm-function[149814]:0x2689e7b dotnet.native.wasm.do_icall@:wasm-function[147637]:0x26189f1 dotnet.native.wasm.do_icall_wrapper@:wasm-function[147549]:0x261538f dotnet.native.wasm.mono_interp_exec_method@:wasm-function[147539]:0x2608835 dotnet.native.wasm.interp_runtime_invoke@:wasm-function[147589]:0x261680e dotnet.native.wasm.mono_jit_runtime_invoke@:wasm-function[151658]:0x26e159e dotnet.native.wasm.do_runtime_invoke@:wasm-function[150592]:0x26aff97 dotnet.native.wasm.mono_runtime_try_invoke@:wasm-function[150601]:0x26b0803 dotnet.native.wasm.mono_runtime_invoke@:wasm-function[150637]:0x26b2ce0 dotnet.native.wasm.mono_wasm_invoke_jsexport@:wasm-function[161345]:0x2839f50 createDotnetRuntime/Module._mono_wasm_invoke_jsexport@http://127.0.0.1:8000/_framework/dotnet.native.js:14520:195 wn@http://127.0.0.1:8000/_framework/dotnet.runtime.js:3:29440 Vc/<@http://127.0.0.1:8000/_framework/dotnet.runtime.js:3:178845 Vc@http://127.0.0.1:8000/_framework/dotnet.runtime.js:3:178996 async*Godot/Module.callMain@http://127.0.0.1:8000/godot.js:70:37 SafeEngine/start/Engine</</<@http://127.0.0.1:8000/godot.js:894:24 promise callback*SafeEngine/start/Engine</<@http://127.0.0.1:8000/godot.js:893:25 SafeEngine/start/Engine<@http://127.0.0.1:8000/godot.js:886:13 promise callback*start@http://127.0.0.1:8000/godot.js:865:22 SafeEngine/startGame/Engine<@http://127.0.0.1:8000/godot.js:932:22 promise callback*startGame@http://127.0.0.1:8000/godot.js:931:8 @http://127.0.0.1:8000/:146:24 @http://127.0.0.1:8000/:161:10 logging.ts:129:25Thanks for taking the time!
Awesome, thanks for the detailed report and prepared repository to reproduce in.
The failure is a bit different on my end - first I get a bunch of issues trying to serve the blender files (which it shouldn’t, on the web, very likely my bug! bad dog, bad dog… - should already be imported), and then I get your crash. Is this what you get, too?Game seems to run okay in the editor. I’m working on making the blender import happen correctly or raise an error during the 2dog import step,
I think this is where it goes wrong. (also, fantastic test case for future tests… I built 2dog because of resource import issues, now resource import bites me in the belly yet again, ha.)Edit: No, the problem is not the missing blender import, after correctly setting it to my blender path I get your behaviour. Which is great, only need to find the missing native trampoline now…
Awesome! Let me know if you need anything else and feel free to keep the project around as test case.
I think Godot converts the Blender files internally so good to hear that it works now with the path set.
Getting closer, it’s GD.PushError and some other functions with 7 parameters under the hood. (a little confused why these aren’t generated, or how the native equivalent to GD.PushError(“Blah”) has 7 params, maybe it tries to look up all candidates) … I need to do something else for the next hour or so, and then can get back to this. My automated and manual tests always fail on errors so ironically that led to GD.PushError never being tested.
In related news, who ever in the .NET/mono team thought it was a good idea to call native signature strings “cookies” in the context of WASM specifically deserves a villain award… I navigated away from the documentation page three times thinking it was about web cookies.