# Bevy WASM Mod your Bevy games with WebAssembly! [![CI](https://github.com/BrandonDyer64/bevy_wasm/actions/workflows/rust.yml/badge.svg?branch=main)](https://github.com/BrandonDyer64/bevy_wasm/actions) [![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/BrandonDyer64/bevy_wasm#license) [![Crates.io](https://img.shields.io/crates/d/bevy_wasm.svg?color=blue)](https://crates.io/crates/bevy_wasm)
[![Bevy](https://img.shields.io/badge/bevy-v0.10-blueviolet)](https://crates.io/crates/bevy) | | | | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | `bevy_wasm` | [![](https://img.shields.io/crates/v/bevy_wasm.svg)](https://crates.io/crates/bevy_wasm) [![](https://docs.rs/bevy_wasm/badge.svg)](https://docs.rs/bevy_wasm) | For games | | `bevy_wasm_sys` | [![](https://img.shields.io/crates/v/bevy_wasm_sys.svg)](https://crates.io/crates/bevy_wasm_sys) [![](https://docs.rs/bevy_wasm_sys/badge.svg)](https://docs.rs/bevy_wasm_sys) | For mods | | `bevy_wasm_shared` | [![](https://img.shields.io/crates/v/bevy_wasm_shared.svg)](https://crates.io/crates/bevy_wasm_shared) [![](https://docs.rs/bevy_wasm_shared/badge.svg)](https://docs.rs/bevy_wasm_shared) | For protocols | See [examples/cubes](https://github.com/BrandonDyer64/bevy_wasm/tree/main/examples/cubes) for a comprehensive example of how to use this. [Changelog](https://github.com/BrandonDyer64/bevy_wasm/blob/main/CHANGELOG.md) ## Protocol Our protocol crate defines the two message types for communicating between the game and mods. ```toml [dependencies] bevy_wasm_shared = "0.10" serde = { version = "1.0", features = ["derive"] } ``` ```rust use bevy_wasm_shared::prelude::*; use serde::{Deserialize, Serialize}; /// The version of the protocol. Automatically set from the `CARGO_PKG_XXX` environment variables. pub const PROTOCOL_VERSION: Version = version!(); /// A message to be sent Mod -> Game. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ModMessage { Hello, } /// A message to be sent Game -> Mod. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum GameMessage { HiThere, } ``` ## Game Our game will import `WasmPlugin` from [`bevy_wasm`](https://crates.io/crates/bevy_wasm), and use it to automatically send and receive messages with the mods. ```toml [dependencies] bevy = "0.10" bevy_wasm = "0.10" my_game_protocol = { git = "https://github.com/username/my_game_protocol" } ``` ```rust use bevy::prelude::*; use bevy_wasm::prelude::*; use my_game_protocol::{GameMessage, ModMessage, PROTOCOL_VERSION}; fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(WasmPlugin::::new(PROTOCOL_VERSION)) .add_startup_system(add_mods) .add_system(listen_for_mod_messages) .add_system(send_messages_to_mods) .run(); } fn add_mods(mut commands: Commands, asset_server: Res) { commands.spawn(WasmMod { wasm: asset_server.load("some_mod.wasm"), }); commands.spawn(WasmMod { wasm: asset_server.load("some_other_mod.wasm"), }) } fn listen_for_mod_messages(mut events: EventReader) { for event in events.iter() { match event { ModMessage::Hello => { println!("The mod said hello!"); } } } } fn send_messages_to_mods(mut events: EventWriter) { events.send(GameMessage::HiThere); } ``` ## Mod Our mod will import `FFIPlugin` from [`bevy_wasm_sys`](https://crates.io/crates/bevy_wasm_sys), and use it to automatically send and receive messages with the game. ```toml [dependencies] bevy_wasm_sys = "0.10" my_game_protocol = { git = "https://github.com/username/my_game_protocol" } ``` ```rust use bevy_wasm_sys::prelude::*; use my_game_protocol::{GameMessage, ModMessage, PROTOCOL_VERSION}; #[no_mangle] pub unsafe extern "C" fn build_app() { App::new() .add_plugin(FFIPlugin::::new(PROTOCOL_VERSION)) .add_system(listen_for_game_messages) .add_system(send_messages_to_game) .run(); } fn listen_for_game_messages(mut events: EventReader) { for event in events.iter() { match event { GameMessage::HiThere => { println!("The game said hi there!"); } } } } fn send_messages_to_game(mut events: EventWriter) { events.send(ModMessage::Hello); } ``` ## Sharing Resources **Protocol:** ```rust #[derive(Resource, Default, Serialize, Deserialize)] pub struct MyResource { pub value: i32, } ``` **Game:** ```rust App::new() ... .add_resource(MyResource { value: 0 }) .add_plugin( WasmPlugin::::new(PROTOCOL_VERSION) .share_resource::() ) .add_system(change_resource_value) ... fn change_resource_value(mut resource: ResMut) { resource.value += 1; } ``` **Mod:** ```rust App::new() ... .add_plugin(FFIPlugin::::new(PROTOCOL_VERSION)) .add_startup_system(setup) .add_system(print_resource_value) ... fn setup(mut extern_resource: ResMut) { extern_resources.insert::(); } fn print_resource_value(resource: ExternRes) { println!("MyResource value: {}", resource.value); } ``` See [examples/shared_resources](https://github.com/BrandonDyer64/bevy_wasm/tree/main/examples/shared_resources) for a full example. ## Roadmap | | | | --- | ------------------------------------------------ | | ✅ | wasmtime runtime in games | | ✅ | Send messages from mods to game | | ✅ | Send messages from game to mods | | ✅ | Multi-mod support | | ✅ | Time keeping | | ✅ | Protocol version checking | | ✅ | Extern Resource | | ✅ | Startup system mod loading | | ✅ | Direct update control | | ✅ | Mod unloading | | ✅ | Mod discrimination (events aren't broadcast all) | | ✅ | Browser support | | ⬜ | Extern Query | | ⬜ | Synced time | | ⬜ | Mod hotloading | | ⬜ | Automatic component syncing | ## License Bevy WASM is free, open source and permissively licensed! Except where noted (below and/or in individual files), all code in this repository is dual-licensed under either: - MIT License ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) at your option. This means you can select the license you prefer! This dual-licensing approach is the de-facto standard in the Rust ecosystem and there are [very good reasons](https://github.com/bevyengine/bevy/issues/2373) to include both. ### Your contributions Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.