rhaiscript / rhai

Rhai - An embedded scripting language for Rust.
https://crates.io/crates/rhai
Apache License 2.0
3.79k stars 177 forks source link

Help With WASM Build #332

Closed Pebaz closed 3 years ago

Pebaz commented 3 years ago

I'm just starting a second game using Rhai and Macroquad, and I can't get a hello-world example to work in WASM:

use macroquad::*;
use glam::{vec2};
use rhai::*;  // When removed, it works fine in browser

#[macroquad::main("Game")]
async fn main()
{
    let sand1 = load_texture("res/img/Bloxel-Grass1-N.png").await;
    let mut pos = vec2(0.0, 0.0);

    // When Rhai code is removed, it loads and works fine in browser
    let engine = Engine::new();
    let mut scope = Scope::new();
    let source = include_str!("scripts/P1.rhai");
    let script = engine.compile(&source).unwrap();
    engine.eval_ast_with_scope::<()>(&mut scope, &script).unwrap();

    loop
    {
        pos.x += 1.0;
        clear_background(WHITE);
        draw_texture(sand1, pos.x, pos.y, Color::new(1.0, 1.0, 1.0, 1.0));
        next_frame().await
    }
}

Working WASM Build: image

When using Rhai, here is the error it shows in the browser console:

image

I'm building the game using:

cargo build --target wasm32-unknown-unknown

And serving it the same as without using Rhai:

basic-http-server . -a 0.0.0.0:4000

The HTML to load the bundled game:

<html lang="en">

<head>
    <meta charset="utf-8">
    <title>TITLE</title>
    <style>
        html,
        body,
        canvas {
            margin: 0px;
            padding: 0px;
            width: 100%;
            height: 100%;
            overflow: hidden;
            position: absolute;
            background: black;
            z-index: 0;
        }
    </style>
</head>

<body>
    <canvas id="glcanvas" tabindex='1'></canvas>
    <!-- Minified and statically hosted version of https://github.com/not-fl3/miniquad/blob/master/native/sapp-wasm/js/gl.js -->
    <script src="https://not-fl3.github.io/miniquad-samples/gl.js"></script>
    <script>load("/target/wasm32-unknown-unknown/debug/game.wasm");</script> <!-- Your compiled wasm file -->
</body>

</html>

Rhai version: 0.9.10 (f32_float, only_i32, no_module, unchecked) Rustc version: rustc 1.49.0-nightly (fd542592f 2020-10-26)

Any help on how to use Rhai with WASM would be appreciated!

schungx commented 3 years ago

You may look at the playground and how it is done?

@alvinhochun maybe you can give some pointers here as well?

schungx commented 3 years ago

Just a a wild shot after some Googling: https://github.com/rustwasm/console_error_panic_hook/issues/14

Another wild thought: Notice that all the __wbindgen_ functions do NOT end with two underscores, but the missing function __wbindgen_placeholder__ ends with __...

alvinhochun commented 3 years ago

Hi Pebaz. I should clarify beforehand that I have no experience with using miniquad and/or macroquad, so I don't really know how one is supposed to use it for the web target. It looks like that miniquad is designed to be used without wasm-bindgen, and this is going to cause some complications.

While Rhai itself doesn't directly use wasm-bindgen, its current use of the instant crate does make wasm-bindgen necessary. This means you have to invoke wasm-bindgen somewhere in your build process and then make use of its JS glue, instead of just using the gl.js from miniquad. Another possible solution is that you change Cargo.toml of Rhai to remove this requirement and then modify your gl.js to make the instant crate work. (The readme of instant may help.)

I wonder if @not-fl3 can chime in?

Side note: I wasn't able to set up and compile a project that mimics yours because not all the files are provided.

not-fl3 commented 3 years ago

It is a very common problem with Instant, the solution - use now feature instead of stdweb/wasm-bindgen

https://github.com/sebcrozet/instant#using-the-feature-now-without-stdweb-or-wasm-bindgen

not-fl3 commented 3 years ago

From what I can tell - rhai explicitly depend on wasm-bindgen https://github.com/rhaiscript/rhai/blob/master/Cargo.toml#L102, so it looks like the only way to use it with stdweb or pure wasm32-unknown-unknown target - modify Cargo.toml and rebuild rhai.

Maybe it is possible to make wasm-bindgen optional? Maybe allow users to choose the js interop layer, just like it is done in Instant itself?

Would be nice to use rhai from crates, without Cargo.toml hacks :)

schungx commented 3 years ago

now seems to to require providing a now function in JavaScript.

The best solution right now seems to be to add two new features, wasm-bindgen and stdweb that maps to instant/wasm-bindgen and instant/stdweb.

schungx commented 3 years ago

@Pebaz The latest drop in master includes the wasm-bindgen and stdweb features. Try pulling from here and specify stdweb feature.

Pebaz commented 3 years ago

@schungx thank you so much I'll give this a shot as soon as possible!

alvinhochun commented 3 years ago

Try pulling from here and specify stdweb feature.

Nah, the stdweb feature wouldn't work because it will require using the matching cargo-web tool to generate its own JS glue, which gives the same problem as with wasm-bindgen.

now seems to to require providing a now function in JavaScript.

As far as I can tell, there is no other way around it if one wants to avoid both wasm-bindgen and stdweb.

Luckily for @Pebaz, it looks like the gl.js glue code already includes it: https://github.com/not-fl3/miniquad/blob/a06b019f9387ba3e5f337a6a4fd177db84c764b3/native/sapp-wasm/js/gl.js#L597

alvinhochun commented 3 years ago

It is a very common problem with Instant, the solution - use now feature instead of stdweb/wasm-bindgen

https://github.com/sebcrozet/instant#using-the-feature-now-without-stdweb-or-wasm-bindgen

Actually, from looking at the code it seems that the now feature is only needed if you want to call the free function instant::now() directly. instant::Instant::new() is available even without it (but you'd still need to provide the now function from JS when not using either wasm-bindgen or stdweb).

schungx commented 3 years ago

According to the documentation of instant, it specifies that either wasm-bindgen or stdweb must be used in order to have a WASM-friendly implementation of Instant. I am not sure if it works without either...

schungx commented 3 years ago

OK, from code, it seems that instant only looks at target and doesn't really depend on the wasm-bindgen or stdweb features. That's good.

Instant::now() creates the Instant type so technically speaking we need it. However, another good news is that, without either wasm-bindgen or stdweb, Instant::now() falls back to external JS now. As @alvinhochun mentions, the feature now is only required to call the now function directly.

Therefore, @Pebaz you can try building for WASM without either wasm-bindgen or stdweb!

not-fl3 commented 3 years ago

now seems to to require providing a now function in JavaScript.

yes, exactly, and that is how instant suppose to works for the users who do not want to use either wasm-bindgen and stdweb.

So I believe exposing instant's now feature is equally important as exporting isntant's stdweb and wasm-bindgen feature :)

Pebaz commented 3 years ago

@schungx @alvinhochun @not-fl3 due to all your help I was able to successfully evaluate a Rhai script in the browser!

I forked Rhai, re-exported instant's now feature, complied it, and it worked in the browser!

Here is the change that was recommended by you guys!

I believe that it is working due to gl.js already including a now() function as @alvinhochun discovered, so a small note directing users to instant's docs might be helpful.

Thank you so much guys! I feel that you all have gone above and beyond to help me and I appreciate it greatly!

I'll definitely be evangelizing about Rhai and it's awesome maintainers! 🦀

not-fl3 commented 3 years ago

But it would be really nice to use crates version of rhai instead of custom a fork.
So maybe keep it open for others willing to use rhai without wasm-bindgen/stdweb? The only change needed to make this happen - re-export instant's now feature :)

Pebaz commented 3 years ago

I definitely agree, if possible, it would be amazing to have the change that worked in Rhai itself rather than the custom fork! 🙂

schungx commented 3 years ago

But it would be really nice to use crates version of rhai instead of custom a fork. So maybe keep it open for others willing to use rhai without wasm-bindgen/stdweb? The only change needed to make this happen - re-export instant's now feature :)

From the code it doesn't seem the now feature is needed because Rhai only calls Instant::now(). If neither wasm-bindgen nor stdweb are specified, it defaults to the now feature.

@Pebaz you can try pulling from this repo instead. It should work.

not-fl3 commented 3 years ago

From the code it doesn't seem the now feature is needed because Rhai only calls Instant::now(). If neither wasm-bindgen nor stdweb are specified, it defaults to the now feature.

My bad, I forgot about the now fallback in instant. I agree, it should works now, thanks for the effort in fixing this!

Pebaz commented 3 years ago

@schungx @not-fl3 This worked! I am pulling from master in Cargo.toml and it still works!

Thanks for all your help!