Skip to content

[.NET] Add web export support#106125

Draft
raulsntos wants to merge 1 commit intogodotengine:masterfrom
raulsntos:dotnet/mono-static-linking
Draft

[.NET] Add web export support#106125
raulsntos wants to merge 1 commit intogodotengine:masterfrom
raulsntos:dotnet/mono-static-linking

Conversation

@raulsntos
Copy link
Member

Use Mono embedding APIs statically linked to load the Mono WASM runtime.

  • The Mono runtime is fetched using the GetRuntimePack C# project. It needs the wasm-tools workload.
  • The GetRuntimePack defines stub functions for all the function pointers we need to retrieve from C#, so the PInvokeTableGenerator includes them in the generated pinvoke-table.h file.
  • Globalization doesn't work, so for now we're always enabling invariant mode.
  • The Mono runtime uses stubs for their exported JavaScript functions, these need to be replaced with the real implementation in their dotnet.runtime.js module but we don't have an easy way to do that yet, which means some BCL APIs will not work (i.e.: crypto APIs)
  • Closes Readd support for web platform exports when using the C# (.NET) version of the engine #70796

Warning

This is still a work in progress, but it should be possible to export a C# game to web with some manual changes to the exported .js afterwards.


This PR was tested with the demo project: Web .NET prototype

Note

The demo project requires multi-threading, so make sure to change the <WasmEnableThreads> property to true to use the right Mono runtime.

Use Mono embedding APIs statically linked to load the Mono WASM runtime.
@Aisht669

This comment was marked as off-topic.

@WithinAmnesia
Copy link

WithinAmnesia commented May 8, 2025

@raulsntos Will this and other Godot C# full features run smoother in the long run as Godot 5.x? As in developing if Godot 4.x gets prototype C# features (such as web support) running and then in Godot 5.x it is optimized with the 'would be nice' features like size and security optimizations? I say this after reading your and the creative community's comments on the C# suite features and the prototype type work and potential for better solutions ( #70796 (comment) ).

Details I wonder this in order to better plan big projects (like a working vertical slice C# MIT Smooth Voxel MMORPG template for Godot) be it Godot 4.x and or Godot 5.x due to API and other complex team coordination which requires a stable software platform / game engine to first under take. If a Godot 5.x alpha with C# full features is being built soon (Godot 4.x may already be past its mid-life cycle once 4.5 is stable) it might be better for the 'power users' / big movers / proper game studios of the creative community to 'Wait for Godot' than to force teams of people to re-engine from Godot 4.x to Godot 5.x. Software long term stability is very critical for long term planning.

A lot of senior game studios are re-engining now and it is by default UE5 but personally I'd like it to be Godot also. A lot of studios are re-engining from the Unity implosion and I want Godot to succeed where Unity failed. Yet long term stability is very critical for the old grumblers of the industry.

Thus starting on a prototype Godot 5.x alpha with full C# features and optimizations and long term stability (and other details that require stability like Godot C# API issues being fully addressed) after ~Godot 4.5 stable is probably a practical way forward for the Godot creative community to keep both near future growth of Godot game engine progressing well and also keep Godot 4.x stable. Godot should get ready for the next gaming generation come 2027 / the late 2020's. Official Godot 5.x alpha plans for a feature roadmap can do a lot to also powerfully grow and future proof the Godot game engine creative community for today's and tomorrow's game building adventures.

It seems like Godot 4.x will get unified C# support as a GDextension and full C# parity support; yet this might be as prototype form. Once the Godot 4.x full C# features are operational this whole suite of C# features might require optimizations that are only properly done with seeing everything on the table first in working form and then in a big strong unified effort lead a proper engine rewrite to properly integrate the features to be optimized for being the best they can be. As from what can be currently done as the creative community for this console generation and the next console / gaming generation planned for 2027 / late 2020's with the PS6 / new Xbox / next gen consoles and gaming hardware.

The work of https://github.com/raulsntos and friends is very helpful to the creative community and I wish to thank the people involved greatly. This kind of work helps the creative community to make great games for everyone.

@enxas
Copy link
Contributor

enxas commented May 12, 2025

When using Firefox with the privacy.resistFingerprinting value in about:config set to true, the demo project at https://lab.godotengine.org/godot-dotnet-web/ loads briefly before displaying a black screen with console errors. It loads fine when privacy.resistFingerprinting is set to false.

@MadBunnyGames
Copy link

Can confirm, with this solution it is possible to make a project with godot4 + .net, and run it on itch.io.
Proof of ... proof of concept? :
https://mad-bunny-games.itch.io/godot-45-net-web-test

@timothyleerussell

This comment was marked as off-topic.

@icavalheiro

This comment was marked as off-topic.

@sonny-campbell

This comment was marked as off-topic.

@ahopness
Copy link

any updates on this? any chances this pr gets included in 4.5?

@Calinou
Copy link
Member

Calinou commented Aug 19, 2025

any updates on this? any chances this pr gets included in 4.5?

4.5 is in feature freeze, so any new features can only be merged in 4.6 at the earliest.

@sonny-campbell
Copy link

Can confirm, with this solution it is possible to make a project with godot4 + .net, and run it on itch.io. Proof of ... proof of concept? : https://mad-bunny-games.itch.io/godot-45-net-web-test

Hey @MadBunnyGames - is there any chance you would be willing to share how you got the .net build working?

I would be happy to hack together whatever I needed, but it seems like there is minimal action on this recently so would be great if I could get something working for prototyping

@Gnumaru
Copy link
Contributor

Gnumaru commented Sep 12, 2025

I suspect that this PR will be turned down in favor of #110441 ( which superseded #90510 and will turn #106125 unnecessary in the future). As soon as we become able to compile and run godot as a shared library, It will be preferable to compile godot as a wasm sidemodule that will be loaded and initialized by standard .net instead of mono, or am I misunderstanding something?

@zorbathut
Copy link
Contributor

zorbathut commented Sep 12, 2025

Worth noting that #110441 does not yet support running C# programs from C# because there's some fiddly changes that need to happen to prevent the two uses of the same environment from stomping on each other, but assuming 110441 is accepted, I'll be putting up a PR for those shortly afterwards; I've been using that on my own framework for months now, so this is at least somewhat battletested.

I haven't yet tried to use this to support web export .NET but I don't know of any expected roadblocks.

@sonny-campbell
Copy link

@Gnumaru @zorbathut would that substantially change how we have to export web builds for dotnet compared to the current process?

For current web exports there's simply an export template required to build the web version - will devs now need to compile the godot engine from source to use as a shared library, or will all that be hidden away from users?

For example with your framework @zorbathut it requires setting up a whole toolchain for building from source. I'm happy to do it if necessary for the moment, but it's not an ideal experience compared to the current process of just downloading the editor and a template, and then running the web export from inside the editor...

@zorbathut
Copy link
Contributor

zorbathut commented Sep 13, 2025

My personal framework requires a compilation framework because I'm a tinkerer and make custom changes to the engine all the time :) I can think of no reason a properly-upstreamed libgodot-based .NET-web-export platform would require that though, it'd be the same user experience as it is today for all the other platforms.

A big part of the reason I'm trying to get libgodot upstreamed is so people can take advantage of its features without having to mess around with custom vendoring frameworks like mine.

@sdepouw
Copy link

sdepouw commented Sep 16, 2025

Very excited to hear about the progress on this! Wishing you all the best of luck in getting this out the door. Once this is in place I can build Godot stuff in C#!

@sonny-campbell
Copy link

My personal framework requires a compilation framework because I'm a tinkerer and make custom changes to the engine all the time :) I can think of no reason a properly-upstreamed libgodot-based .NET-web-export platform would require that though, it'd be the same user experience as it is today for all the other platforms.

A big part of the reason I'm trying to get libgodot upstreamed is so people can take advantage of its features without having to mess around with custom vendoring frameworks like mine.

@zorbathut Ah I understand, yeah it would be great to get it integrated. Getting a web build for C# is the only thing that's giving me a headache at the minute, as otherwise I've found working with Godot such a nice experience to use...

@MadBunnyGames
Copy link

MadBunnyGames commented Sep 21, 2025

Hey @MadBunnyGames - is there any chance you would be willing to share how you got the .net build working?

Basically pulled the changes from here into a 4.4.1 (or some nightly 4.5 dev, not sure), edited the .csproj file to match .net version and threading support, built the editor and the templates (as Godot documentation says... note: I use windows and had to switch to WSL when building the web template, because emscc command was too long for PowerShell), also set up a local nuget store for godot package during build (its in godot docs too), matched my project file, referenced the newly built nugets, needed to add entrypoint to the project (main function), then built and exported the project with the new templates, manually removed the DOTNET.setup(...) call from the .js that was generated for the c# project, then just uploaded to itch as is.
Had some road bumps along the way, but I don't really remember them, because it was 2+ months ago.

@xr0gu3
Copy link

xr0gu3 commented Sep 28, 2025

If you want to try out the solution made by @raulsntos and reproduced by @MadBunnyGames and you aware how to use Docker you could try my "one liner" to achieve the web build. Nothing is guaranteed, it's all experimental and may break.
I’ve created a Docker image to streamline the HTML5 export process for Godot .NET projects using Godot 4.5. You can run it with the following command in your project folder:
docker container run --rm -v ".:/src" kstgrd/godot-mono-web:4.5.dev
This will generate a build directory with the exported project and an index.zip file ready for upload to itch.io. As a proof of concept, I’ve successfully built and uploaded a prototype here: https://kstgrd.itch.io/godot45net?secret=lc3VjeGNpn4PXlzJZRattN79s

P.S. ~4gb will be downloaded by docker to get the image
Update:

  • demo project source: here
  • your .csproj should use <TargetFramework>net9.0</TargetFramework> as image was built relying on that version of .NET
  • don't forget to create an empty Program.cs file in your project root with an empty public static void Main() function in it
public class Program
{
    public static void Main(string[] args)
    {
        
    }
}

@danmastrow
Copy link

Also wanting to know if there's any estimation on release for this, if there's any blockers or help needed please let us know.

@AR-DEV-1
Copy link
Contributor

AR-DEV-1 commented Nov 16, 2025

Hey there! I'm AR & I have contributed to the .NET backend. We currently can't tell you when this will be merged because as Raul mentioned earlier, there are some issues (The JS one). If you want to compile it yourself, feel free to check out the docs. The PR is also work in progress meaning there will be changes Raul wants to implement before merging,

@renanrcp
Copy link

@AR-DEV-1

Ah yeah, the JS issue is a big problem in this preview because [JsImport] and [JsExport] doesn't work.

Yeah you can think: I don't will use it anyway, but .NET HttpClient and ClientWebSocket uses it (fetch and WebSocket), in my case I just do my own implementation using Godot HttpClient and WebSocket, but I understand it isn't safe for all godot users.

Also I've tried a lot of things trying dotnet.js works with godot, but it seems impossible for me (for now), because dotnet.js starts the .NET Runtime and in godot the engine starts .NET Runtime (also linking the WASM).

I also tried to start a concurrent dotnet.js with another .NET Runtime running, but the export tables from dotnet.runtime.js is "locked" to the wasm execution.

I don't know why .NET can't allow us to "link dotnet.runtime.js" or something like that without starting the runtime from dotnet.js and it's very frustrating.

I also will try compile this preview version with a 4.5 stable version again as I unfortunatelly doesn't have success in the past.

I think we just wanna more visibility if we have some news or updates about this theme, because it seems a little abandoned :(

@AR-DEV-1
Copy link
Contributor

@renanrcp I have asked Raul about it & if he needs any help. Will let y'all know as soon as I get a reply.

@AR-DEV-1
Copy link
Contributor

Yes, I'm going to work on it. Wooh! Let's just hope it gets implemented :)

@GeorgeS2019
Copy link

GeorgeS2019 commented Nov 22, 2025

@renanrcp
Copy link

I'm so happy to see that @AR-DEV-1, I will help with anything in my range ❤️

@renanrcp FYI #70796 (comment) dotnet/runtime#75257 (comment)

The .NET issue is about the dynamic linking for .NET AOT in Web, in this PR we are using mono static linking, the real issue here in my vision is about the dotnet.js problem, if we can solve or find a workaround for this problem it can be near to have a RC.

I using this preview in a big personal project and it's working very fine.

@renanrcp
Copy link

Ok, trying to discover why this preview doesn't work with 4.5.1 again this week 👀

@GeorgeS2019
Copy link

GeorgeS2019 commented Nov 28, 2025

From Chat Godot contribution forum
https://chat.godotengine.org/channel/dotnet?msg=cSymXsYWNWKWYHuWf

@raulsntos
@AR-DEV-1

If you try out the PR, you'll see that the exported game won't work out of the box. If you take a look at the browser console you'll see more details, but it's all about the .NET initialization and how we're merging the dotnet.js and godot.js emscripten files.

In the prototype that uses this PR you'll see that, as a workaround, it was enough with removing the DOTNET.setup call. However, this means .NET is not properly initialized, so the stub functions are not replaced with their real implementation among other things.

@AR-DEV-1
Copy link
Contributor

From Chat Godot contribution forum https://chat.godotengine.org/channel/dotnet?msg=cSymXsYWNWKWYHuWf

@raulsntos @AR-DEV-1

If you try out the PR, you'll see that the exported game won't work out of the box. If you take a look at the browser console you'll see more details, but it's all about the .NET initialization and how we're merging the dotnet.js and godot.js emscripten files.

In the prototype that uses this PR you'll see that, as a workaround, it was enough with removing the DOTNET.setup call. However, this means .NET is not properly initialized, so the stub functions are not replaced with their real implementation among other things.

Yeah, I'll have a look at that later after I'm free.

@renanrcp
Copy link

renanrcp commented Nov 29, 2025

Hey @AR-DEV-1 I've tried a lot of things, and I will share with you:

About incompatibility with 4.5/4.5.1:

Apparently this preview works fine with 4.5.1 if you doesn't use multithreading. Like you can use new Thread() by example, but any heavy execution will crash the game and the page, you can see it in the @raulsntos prototype, if you export with Threads supports but add WithConcurrentBuild(false) in the code compilation you will se everything works.

In my case I tested with my personal project which is using exactly this PR and enabling multithreads.

With this preview (4.4 + web support) everything works fine, multithreading or not, my project only uses .ConfigureAwait(false) in many places.

With this preview + 4.5.1-stable the game crashes in the first ConfigureAwait(false).

What I've tried to fix:

  • Set Max and Min ThreadPool in the C# side:
// Both calls returned true, also checked with ThreadPool.GetMaxThreads
ThreadPool.SetMinThreads(1, 1);
ThreadPool.SetMaxThreads(6, 1);
  • Changed Godot Pool Size to 6 and 8.
  • Disabled Godot Wasm SIMD with: scons platform=web target=template_debug threads=yes dev_build=yes dev_mode=true module_mono_enabled=true wasm_simd=false

Yeah nothing fixed it, I really don't know what really change between 4.4 and 4.5 to break the multithreading in .NET side.

About the stubs, the "Js Problem"

Yeah this will be hard to fix I reflected and studied a lot about the Interop with JS in .NET Wasm and the only solution I consider will be this:

Finishing...

I also don't know if you prefer this type of message here or in the godot contribution forum, I can use both without problems :)

FYI: @raulsntos @AR-DEV-1

@renanrcp
Copy link

renanrcp commented Nov 29, 2025

Also compiling with template_release we finally got a message error:

Tried to spawn a new thread, but the thread pool is exhausted.
This might result in a deadlock unless some threads eventually exit or the code explicitly breaks out to the event loop.
If you want to increase the pool size, use setting `-sPTHREAD_POOL_SIZE=...`.
If you want to throw an explicit error instead of the risk of deadlocking in those cases, use setting `-sPTHREAD_POOL_SIZE_STRICT=2`.

I also tried to reduce more in .NET ThreadPool 2, 1. But the problem continues, if I increase the emscriptenPoolSize to 16 it fixes, but that's strange, I've checked the flags used for the emsdk and in godot 4.4 it is originally 8 too.

@AR-DEV-1
Copy link
Contributor

@renanrcp I was switching operating systems (Windows to Linux!). We always appreciate help from the community. I think the error message is a bit weird, can you share the command you used?

@renanrcp
Copy link

renanrcp commented Nov 30, 2025

@renanrcp I was switching operating systems (Windows to Linux!). We always appreciate help from the community. I think the error message is a bit weird, can you share the command you used?

Merge this PR in the 4.5.1-stable branch, change the <WasmEnableThreads>false</WasmEnableThreads> to true and compile a release template:

scons platform=web target=template_release threads=yes module_mono_enabled=yes production=yes

Then export the Raul's prototype with the template (maybe the .sln file doesn't support the Release as publish mode just insert the lines).

You'll see this error in the JS console.

If you change the emscriptenPoolSize to 12 or 16 you'll see everything working fine.

Also, yes I using Linux, Ubuntu 24.04 with latest chrome version.

FYI: @AR-DEV-1

@GeorgeS2019
Copy link

GeorgeS2019 commented Dec 3, 2025

We currently can't tell you when this will be merged because as Raul mentioned earlier, there are some issues (The JS one). If you want to compile it yourself, feel free to check out the docs. The PR is also work in progress meaning there will be changes Raul wants to implement before merging,

@renanrcp

Curious, how likely godot 4 web export could be embeded in blazor and how the JS interoperability and c# between blazor and embeded godot4 web export could proceed.

Interested to learn from you, based on your testing, and added additional files (corebindings.c, JsFunctionBinding (called by source generators), JS Side.), could this be done soon?

@renanrcp

Did you test your finding with this web demo prototype?
https://github.com/raulsntos/godot4-web-dotnet-prototype

@renanrcp
Copy link

We currently can't tell you when this will be merged because as Raul mentioned earlier, there are some issues (The JS one). If you want to compile it yourself, feel free to check out the docs. The PR is also work in progress meaning there will be changes Raul wants to implement before merging,

@renanrcp

Curious, how likely godot 4 web export could be embeded in blazor and how the JS interoperability and c# between blazor and embeded godot4 web export could proceed.

Interested to learn from you, based on your testing, and added additional files (corebindings.c, JsFunctionBinding (called by source generators), JS Side.), could this be done soon?

@renanrcp

Did you test your finding with this web demo prototype? https://github.com/raulsntos/godot4-web-dotnet-prototype

Hello @GeorgeS2019 I'm too busy in last weeks lol...

First of all, yes I've tested everything with Raul's prototype, I'm also have a personal project using it.

For this current preview seems impossible to embed blazor in godot4 web export, because blazor really needs dotnet.js and others dependencies (it's impossible for now, don't know for the future). But maybe you can run a blazor app and a godot web app and communicate them via JS Interop for both solutions, don't know if it will work with multiple wasms in page.

I don't have so much knowledge about C/C++ side of .NET and Godot, so I don't know if these additional files solve the problem or if they could be done soon, I'm here to help with anything about .NET/C# because it's my primary knowledge.

I will try help with other things but maybe I will be useless outside .NET/C#

@GeorgeS2019

This comment was marked as off-topic.

@akien-mga

This comment was marked as off-topic.

@Ekscentricitet
Copy link

Hi, and thanks for your work on this.
This PR is currently listed in the milestones/priorities, but there hasn’t been any activity for a while, so I was wondering if there has been any progress or if work is happening elsewhere. Would help from a first-time contributor be useful?

I’ve also seen some discussion about using Godot as a library as a possible new approach, and was wondering if that affects the direction here.

Any updates would be appreciated, thanks.

@renanrcp
Copy link

Hi, and thanks for your work on this. This PR is currently listed in the milestones/priorities, but there hasn’t been any activity for a while, so I was wondering if there has been any progress or if work is happening elsewhere. Would help from a first-time contributor be useful?

I’ve also seen some discussion about using Godot as a library as a possible new approach, and was wondering if that affects the direction here.

Any updates would be appreciated, thanks.

The problem for this feature is the js issue, some features in .NET Web needs some interop functions in the JS Side, godot cannot replicate these functions for now (because dotnet.js starts the runtime and setup these functions, but in godot, the engine starts the runtime).

If you don't consider the js issue this feature is stable for me (currently using it in my personal project).

@coffandro
Copy link

Am i reading right that this is functional as long as you dont need the JSBridge?

@renanrcp
Copy link

Am i reading right that this is functional as long as you dont need the JSBridge?

The [JsImport] and [JsExport] of .NET, they use it in some places like HttpClient, WebSocket... You can just use the Godot things and it will work fine.

@Ekscentricitet
Copy link

Sounds useful then. Is there a build for 4.6 somewhere with this commit for testing purposes? The Docker image above doesn't have the source code available, or at least I couldn't find it.

Are there plans to add this as it is to the project or are you waiting for a complete solution first?

@Kezzo
Copy link

Kezzo commented Feb 13, 2026

Am i reading right that this is functional as long as you dont need the JSBridge?

The [JsImport] and [JsExport] of .NET, they use it in some places like HttpClient, WebSocket... You can just use the Godot things and it will work fine.

Would it make sense to add the current state of the web export to the next Godot version as an experimental feature with clear instructions about what is not supported yet?
Many games don't need to use the HttpClient, WebSockets etc. but just require the default functionalities of Godot.

We have been waiting for a resolution to this problem for 3 years, maybe it makes sense at this point to move this forward in an imperfect way, but at least unblock a majority of game developers trying to make web builds with Godot.

@renanrcp
Copy link

renanrcp commented Feb 13, 2026

Sounds useful then. Is there a build for 4.6 somewhere with this commit for testing purposes? The Docker image above doesn't have the source code available, or at least I couldn't find it.

Are there plans to add this as it is to the project or are you waiting for a complete solution first?

I've done a public repo for it, but I've built with 4.5.1 in my on machine, it's not hard, just merge this PR with a stable branch like 4.6 and it will work, just setup multithreads in the right way if you wanna use.

https://github.com/renanrcp/godot-web-dotnet-building
#106125 (comment)

Am i reading right that this is functional as long as you dont need the JSBridge?

The [JsImport] and [JsExport] of .NET, they use it in some places like HttpClient, WebSocket... You can just use the Godot things and it will work fine.

Would it make sense to add the current state of the web export to the next Godot version as an experimental feature with clear instructions about what is not supported yet? Many games don't need to use the HttpClient, WebSockets etc. but just require the default functionalities of Godot.

We have been waiting for a resolution to this problem for 3 years, maybe it makes sense at this point to move this forward in an imperfect way, but at least unblock a majority of game developers trying to make web builds with Godot.

Yep, you also can use HttpClient and WebSocket, by example, just build a custom HttpMessageHandler and a custom WebSocket using godot clients.

@HexBlit
Copy link

HexBlit commented Feb 13, 2026

I've been investigating the JS interop stub replacement problem and have a made some progress with a proof-of-concept that replaces the crypto stub with a real browser implementation. and investigating the JSImport and mainly the whole "How could we hook this all up" Sharing findings here in case they're useful.

Also, i know the GodotLib is the way forward, but working on a project now was trying to put together an idea that helps now and get devs building .net to web exports for their projects sooner than later.

Environment

I am working on this in windows.

  • Custom build of PR [.NET] Add web export support #106125 (commit aa1f5ff, 2025-05-06)

  • Followed the documentation on building Godot from source

    • I did have to wsl to compile the mono templates due to windows/command prompt "line too long errors."
    • pointing the advanced options export pointing to:
      • ~/godot/bin/godot.web.template_debug.wasm32.nothreads.mono.zip
      • ~/godot/bin/godot.web.template_release.wasm32.nothreads.mono.zip
  • Using .NET 9.0 as recommened, and adding that empty Program.cs so things compile.

My Test CS file

using Godot;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;

public partial class Main : Node
{
	public override void _Ready()
	{
		var bytes = new byte[16];
		System.Security.Cryptography.RandomNumberGenerator.Fill(bytes); // I got this working via wasm_entropy stub replacement
		GD.Print("Crypto result: " + BitConverter.ToString(bytes));	 
		runHTTP(); // Was tracing the JSImport signatures...
	}
	private async void runHTTP()
	{
		try {
			using var client = new System.Net.Http.HttpClient();
			var response = await client.GetStringAsync("https://httpbin.org/get");
			GD.Print("HTTP result: " + response.Substring(0, 50));
		} 
		catch (Exception e) 
		{
			GD.Print("HTTP error: " + e.GetType().Name + ": " + e.Message);
		}
	}
}
	

Dealing with initialization

After exporting and applying the known DOTNET.setup() removal workaround I was seeing what everyone else saw. Godot API's work and obviously dotnet not hooked up.

So I wondered, what it would take to find a way to get to the stubs, so my question was how tied are dotnet.js and dotnet.runtime.js and how much does it need to be setup? Do we HAVE to initialize it or can we find a way to route it ourselves?

so I uncommented out the dotnet setup and started with the errors

First up,

Problem 1: require is not defined

DOTNET.setup() crashes immediately in the browser because dotnet_replacements references require unconditionally:

const dotnet_replacements = {
    fetch: globalThis.fetch,
    ENVIRONMENT_IS_WORKER,
    require,           //  crashes as undefined
    modulePThread,
    scriptDirectory
};

So let's just guard against it, so i added this. require: typeof require !== 'undefined' ? require : undefined

Problem 2: Module.__dotnet_runtime is undefined

After fixing require, DOTNET.setup() crashes on:

Module.__dotnet_runtime.initializeReplacements(dotnet_replacements)

Because dotnet.runtime.js is never loaded in the export, so __dotnet_runtime is never set.

dotnet.runtime.js cannot be loaded standalone — it's an ES module tightly coupled to dotnet.js's bootstrap (as @renanrcp already found). Loading it directly fails because its internal state.

So as a work around I created a no-op shim that allows DOTNET.setup() to complete without crashing:

Module.__dotnet_runtime = {
    initializeReplacements: function(replacements) {},
    passEmscriptenInternals: function(internals, buildOptions) {},
    configureEmscriptenStartup: function(mod) {}
};

With both fixes applied, the game boots and C# executes normally — but stubs remain unpatched.

Idea: Can I discover / determine imports programmatically.

Using wasm-objdump via my wsl command window on dotnet.native.wasm, I confirmed the stub functions are WASM imports. (I mainly wanted to try to build a map to the actual implementation)

wasm-objdump -j Import -x /c/_projects/godot/modules/mono/runtime/GetRuntimePack/bin/mono_runtime/dotnet.native.wasm

Import[93]:
 - func[0]  <env.mono_wasm_bind_js_import_ST>
 - func[1]  <env.mono_wasm_invoke_jsimport_ST>
 - func[43] <env.mono_wasm_browser_entropy>
 ...

In the exported index.js, each stub returns a runtime_idx marker:

function _mono_wasm_browser_entropy() {
    return { runtime_idx: 19 };
}

These markers are meant for dotnet.runtime.js to replace with real implementations during initialization. Since that never happens, the stubs persist. (As we know is the actual problem....)

Working Crypto Fix

Because stubs are WASM imports, they're bound at instantiation time from index.js. Replacing the JS function before WASM loads works:

function _mono_wasm_browser_entropy(pBuffer, length) {
    var view = new Uint8Array(wasmMemory.buffer, pBuffer, length);
    crypto.getRandomValues(view);
    return 0;
}

Result:

// Before (stub):
Crypto result: 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00

// After (patched):
Crypto result: F3-95-D9-BC-9C-C1-FF-80-D1-DC-8D-9E-B5-D0-A4-6D

The function signature (pointer + length) was confirmed via logging — pBuffer is a WASM memory offset, length is the byte count.

JSImport Stubs (fetch, WebSocket)

I also traced the [JSImport] path. When C# code uses HttpClient, it triggers:

  1. _mono_wasm_bind_js_import_ST(signature_ptr) — registers a JS function by reading its name from WASM memory
  2. _mono_wasm_invoke_jsimport_ST(fn_handle, data_ptr) — calls the registered function with marshalled arguments

By reading the memory at the signature pointer, I can see the function names Mono is trying to bind:

INTERNAL.http_wasm_create_controller
INTERNAL.http_wasm_fetch

These are the INTERNAL.* functions from dotnet.runtime.js that implement HttpClient over the browser's fetch API.

Unlike the crypto stub (simple pointer + length → void), the JSImport marshalling layer is complex — and is something that will need some more complex debugging to mapping as we might be able to find a way to either wrestle dotnet.runtime.js with a shim and direct to the marshalling. As I would hate to backwards engineer this. But I might try.....

here is my personally stub replacement as I was trying to understand the mapping and process

  function _mono_wasm_bind_js_import_ST(signature_ptr) {
      var heap = new Uint8Array(wasmMemory.buffer);

      // Read further to get full strings
      var str = "";
      for (var i = 0; i < 512; i++) {
        var b = heap[signature_ptr + i];
        if (b >= 32 && b < 127) str += String.fromCharCode(b);
        else if (b === 0 && str.length > 0) str += " | ";
      }

      console.log("bind_js_import_ST ptr:", signature_ptr, "full string:", str);
      return 0;
    }
  
  function _mono_wasm_invoke_jsimport_ST(fn_handle, data_ptr) {
      var heap = new Uint8Array(wasmMemory.buffer);
      var str = "";
      for (var i = 0; i < 256; i++) {
        var b = heap[data_ptr + i];
        if (b >= 32 && b < 127) str += String.fromCharCode(b);
        else if (b === 0 && str.length > 0) str += " | ";
      }
      console.log("invoke_jsimport_ST handle:", fn_handle, "data string:", str);
      return 0;
    }

@renanrcp
Copy link

Hey @HexBlit can you show the JS file you edited, I wanna check if I can create replacements for INTERNAL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Readd support for web platform exports when using the C# (.NET) version of the engine