Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

collab #61

Open
konsumer opened this issue Jul 9, 2024 · 1 comment
Open

collab #61

konsumer opened this issue Jul 9, 2024 · 1 comment

Comments

@konsumer
Copy link

konsumer commented Jul 9, 2024

Hey, I work on node-raylib and raylib-wasm, and also my own game-engine null0, which all do similar things, but with different approaches. I love what you are doing here. We should join forces!

Here are some things I worked on, that I think relate:

  • null0 this is my engine. I compile the native C host with emscripten, so I can share all rendering between "cart wasm" (which is similar to what wasm game is in zozlib) and the host. This allows me to share the native hooks across web and native (I have a native runtime that loads the same user-code wasm, too) which means no separate implementations of drawing and stuff (so they look exactly the same.) The downside is there is a lot of code to bind memory back & forth between the small game-wasm & the bloated emscripten-wasm host
  • null0-webfirst this uses a similar approach to zozlib, where i implement the entire null0 API directly in browser js (no wasm-to-wasm) the downside is that I will need to implement the same API in native wasm-host to share games between runtimes, and they won't 100% match, without a lot of work, but it is much smaller/faster/simpler on web, at least. I sort of figured out some tricks to smoothing out these differences, but there are always going to be lil things that are off (some of these things can actually be fixed with little 1-off wasm-based functions that use the same C code, if needed, too)
  • raylib-wasm - Here i do kind of the same thing I did in null0, where I compile raylib host runtime to emscripten, but expose the entire API to js, and add some struct-wrappers that operate on the host wasm-memory, directly. Here are some demos (view-source to see the js.) bunnymark is a nice example of what I am trying to do there. There is no wasm-to-wasm communication, because you write your game in JS, and it hooks into real raylib-wasm, that was compiled with emscripten.

Here are some ideas, I have initially, of how we might be able to work together:

  • Make an initial emscritpen-based host, that uses raylib-wasm to expose a more complete raylib API to the "user game wasm" (which is very light wasm, made without emscripten, that imports what it needs from host.) This will fill in a lot of API gaps, and make the JS code look more like regular raylib (those struct-things are pretty handy) and we can slowly replace stuff with browser-native js. Downsides are it will be more bloated (because of emscripten, and some stuff being double-implemented in browser-host & C) and some things will need careful extraction (like changing how images work will require replacing everything that touches images) also it will require lots of memory-passing to take things to/from the host and hand it to the game wasm. I'm not gonna lie, this is a drag. In null0, I automate a lot of this, with code-generation, which helps a lot. The upside is this will be a closer implementation to original raylib, right away, since it's the same C code, but also personally, I don't like this as much as just reimplementing it all in web-host, like you are doing here. It's more work overall, but the end-product is nicer, in my opinion.
  • Implement more of the API in browser-native JS. I can also help just get more of the API done, without emscripten, and smooth out differences between native raylib & browser js implementations (canvas quirks, font-loading, etc)
  • extra tooling/wrapping - In raylib-wasm I use a web-component to connect the user's JS code to a single canvas, and it makes it all self-contained. I could help with similar here, and other little tools that make it all nicer to work on, like hot-reloading wasm-host, etc.
  • more wasm-headers - I could help with codegen for lots of user-code headers for raylib, so people can make their wasms in more languages than just C
  • native host - I have some experience with implementing a native C wasm host (and codegen for it) so it might be cool to make a native runtime that uses real raylib, but loads the same user-code wasms. This would mean you can make your game in any wasm-lang (micropython, tinygo, C with wasi-sdk, rust, etc) and the same game would run in browser and native without recompile.

There are probly other things, since it seems like we are working on a lot of the same ideas.

@konsumer
Copy link
Author

konsumer commented Jul 9, 2024

One issue with no-emscripten host is aync is very tricky, and downloading files pretty much requires async (although you can do oldschool XHR synchronous, it's blocking and will mess up the main thread, if the files are big.) I get around this in null0 with a zip file that represents all the assets the wasm has access to. I preload it in both hosts, so all the code works the same. For self-contained "game carts" this works best for me, but it's not exactly like it would be in plain-native.

In raylib-wasm, I get around it by using ASYNCIFY (in emscripten) since raylib is already compiled with emscripten, and it just waits for the files before using them (in C, it thinks it's synchronous, but really it gets paused.)

Another solution is to map all the files up-front with WASI. bjorn3/browser_wasi_shim does this, so when the game-wasm asks for a file, it's already been downloaded & mapped to a file-location, and can be accessed synchronously. This is similar to what emscripten does with FS, too. I like this, because most things can target WASI, now, so it's pretty much automatic, and no special async stuff is needed (which is not available in every wasm-language, or wasm-host.) In native host we could say "mount this directory to this location" (similar to how the native wasm-host CLIs do it) and it will roughly work as expected.

Also, following a naming-convention helps with both of these ways, I think. In my zip-files, I have main.wasm which is the user-code entry-point. this could also be an option, if people need another entry filename. With dirs, too, this could be the same, so you could do:

zoz mygamedir

and it would automount mygamedir/ as ./ and run mygamedir/main.wasm.

in browser this could be done like

const game = await zoz('main.wasm', {
  fs: {
    'assets/logo.png': 'https://example.com/assets/logo.png'
  }
})

where everything in the fs option gets downloaded before the wasm runs. It could support zips, too, for a similar thing to null0, which might be easier for distributing your zoz-game:

const game = await zoz('game.zip')

This would download the zip, extract all the files and build the initial WASI fs, and then run main.wasm.

You can use "magic bytes" to detect what kind of file it is, since both have 4-byte headers, and even combine them (initially load the zip fs, and then download all the files in fs option, and clobber over them.)

On web, this can be supported with unzip.js or similar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant