๐ Hello everyone,
I'm exploring how to efficiently communicate between the Bevy ECS and the outside world (Javascript in this case).
I'm interested in passing events both ways -> getting events from the Javascript layer into the ECS,
and also emitting events from the ECS to the Javascript layer.
In my current implementation I use a global Mutex<Vec<JsEvent>>, like so:
pub static JS_EVENT_QUEUE: Mutex<Vec<JsEvent>> = Mutex::new(Vec::new());
And then draining this queue in a wasm-bound function.
But I was wondering whether there is a "better" way
or what the recommended way to make such communication happen is?
Thanks for any insights ๐
use bevy_ecs::system::Resource;
use serde::Serialize;
use std::sync::Mutex;
use wasm_bindgen::{prelude::*, JsValue};
// Static JS_EVENT_QUEUE serves as a global state to hold events for JS execution.
// We use a Mutex for safe concurrent modification. This way, events can be added to
// the queue from multiple parts of the Rust code, and later polled and drained for JS handling.
pub static JS_EVENT_QUEUE: Mutex<Vec<JsEvent>> = Mutex::new(Vec::new());
#[derive(Debug, Serialize, Clone)]
pub enum JsEvent {
SomeEvent(String),
}
#[derive(Resource, Default, Debug)]
pub struct JsEventQueue;
impl JsEventQueue {
pub fn push_event(&mut self, event: JsEvent) {
let mut js_event_queue = JS_EVENT_QUEUE.lock().unwrap();
js_event_queue.push(event);
}
}
#[wasm_bindgen]
pub fn poll_js_event_queue() -> JsValue {
let mut event_queue = JS_EVENT_QUEUE.lock().unwrap();
let events = event_queue.drain(..).collect::<Vec<_>>();
return serde_wasm_bindgen::to_value(&events).unwrap();
}
Github Discussion: https://github.com/bevyengine/bevy/discussions/10172