Skip to content

splo/bevy_remote_wasm

Repository files navigation

Bevy Remote Wasm

Crates.io Version docs.rs License: MIT OR Apache-2.0

A plugin for the Bevy Remote Protocol that exposes remote methods to Wasm/JavaScript.

Usage

Live Example

Check the Bevy Remote Playground at https://splo.github.io/bevy-remote-playground.

It is a web app that loads a Bevy Wasm module built on a bevy-remote-wasm-enabled app and provides a UI to explore the exposed methods and their results.

Setup

  1. Add bevy_remote to your dependencies, and bevy_remote_wasm only for wasm targets.

    [dependencies]
    bevy = "0.18"
    bevy_remote = { version = "0.18", default-features = false }
    
    [target.'cfg(target_family = "wasm")'.dependencies]
    bevy_remote_wasm = "0.1"

    Don't depend on bevy/bevy_remote (bevy = { version = "0.18", features = ["bevy_remote"] }) because it enables the default http transport, which does not compile on Wasm target (until this fix is merged and released).

    This is why it is recommended to depend on the separate crate bevy_remote with no default features (so bevy_remote/http is not enabled).

  2. Add RemotePlugin to your app, and add RemoteWasmPlugin only when compiling for Wasm.

    use bevy::prelude::*;
    use bevy_remote::RemotePlugin;
    
    fn main() {
        let mut app = App::new();
        app.add_plugins((DefaultPlugins, RemotePlugin::default()));
        #[cfg(target_family = "wasm")]
        app.add_plugins(bevy_remote_wasm::RemoteWasmPlugin);
        app.run();
    }
  3. Build for the wasm32-unknown-unknown target, then generate the JS bindings with wasm-bindgen directly (wasm-bindgen --target web --out-dir <output_dir> target/wasm32-unknown-unknown/debug/<crate_name>.wasm) or through a tool such as wasm-pack or Trunk.

  4. In JavaScript, load the generated JS module, call init(), then await getBridge():

    import init, { getBridge } from "/example.js";
    
    await init();
    const bridge = await getBridge();
    const response = await bridge.main["rpc.discover"]();
    console.log(response.info.version);

    The generated module path and init function depend on your build tool, but the initialization order does not: initialize the Wasm module first, then await the bridge.

    getBridge() may be called as soon as the JS module loads, but it resolves only after Bevy publishes the bridge during app startup, which, depending on your setup, may take some time.

    The wasm-bindgen output also includes TypeScript declarations for the root bridge (BrpBridge) and the default remote methods (BuiltInBrpBridge):

    import type { BuiltInBrpBridge } from "./example.js";
    
    const bridge = await getBridge() as BuiltInBrpBridge;
    const response = await bridge.main['world.query']({ data: { option: 'all' } });
    // Enjoy docs and autocompletion for built-in methods!
  5. Serve the generated HTML, JS, and .wasm files together from a web server or bundler that supports Wasm imports.

A minimal example can be found in examples/minimal.rs for the smallest Bevy setup and apps/get-version/index.html for an inline HTML module example.

Wasm API

The plugin builds a bridge object with app-scoped method maps based on the methods registered with bevy_remote::RemotePlugin, more specifically through the bevy_remote::RemoteMethods resource. Each time this resource is updated, the bridge is re-published with the new methods. The getBridge() function must be called again to get the updated bridge.

For Bevy 0.18, the bridge exposes only the main app. Method keys inside that namespace use the BRP method names directly, so call them with bracket notation such as bridge.main["world.query"] and bridge.main["world.list_components+watch"].

  • Instant methods (run once, return a result):
// Call the instant method with params but without callback.
// Returns the result wrapped in a Promise.
const result = await bridge.main['world.query']({ data: { option: 'all' } });
console.log(result);

// Call the instant method with params and a callback that will be called with the result.
// Returns `undefined` wrapped in a Promise.
await bridge.main['world.query']({ data: { option: 'all' } }, (result) => console.log(result));
  • Watching methods (stream results):
// Call the watching method with params and a callback that will be called on each result.
// Returns a closer function wrapped in a Promise.
const close = await bridge.main['world.list_components+watch']({ entity: 123 }, (result) => console.log(result));

// Stop the stream.
close();

Version Table

Bevy Bevy Remote Wasm
0.18 0.1

Development

Tech Stack

This project is built with:

Some just recipes depend on external tools and will fail fast if they are not installed.

Common Tasks

Use the Justfile for the usual development workflow:

  • Validate the whole repository with just check. This runs Rust linting, formatting checks, a crate publish dry-run, and the TypeScript package check.
  • Format the Rust code with just format. This uses cargo +nightly fmt, so a nightly toolchain is required for formatting.
  • Run the Wasm tests with just test. Tests run on the wasm32-unknown-unknown target with wasm-bindgen-test-runner.
  • Discover available demos with just list-examples and just list-apps.
  • Build and serve demos locally with just run-example. By default this builds the minimal example with the get-version app and serves it on http://localhost:8080.
  • Build demo artifacts without serving them with just build-example. Generated browser assets are written under target/dist/<app>.

Contributing

The repository is split into a few small parts:

  • src/ contains the core RemoteWasmPlugin implementation and the Wasm bridge exported to JavaScript.
  • build.rs combines the TypeScript declaration files into a single output that wasm-bindgen embeds in the generated bindings.
  • examples/ contains Rust-side Bevy examples such as the minimal app and the 3D demo.
  • apps/ contains browser-facing HTML and JavaScript apps that load those examples and exercise the bridge.
  • bevy-remote-wasm-types/ contains the type-only NPM package and its declaration files.

A good contribution flow is: update the Rust bridge in src/, keep the embedded TypeScript surface in sync and run one of the example apps locally. Validate with just check before submitting a pull request.

Deployment

A GitHub Actions CI workflow runs on pushes to main and on pull requests.

License

Dual-licensed under Apache-2.0 and MIT. See LICENSE-APACHE and LICENSE-MIT.

About

A plugin for the Bevy Remote Protocol that exposes remote methods to Wasm/JavaScript.

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Contributors