//! WASM-JS bridge for game communication //! //! This module provides JavaScript bindings to expose game data and events //! from the Bevy/WASM game to the React frontend. use std::sync::{Arc, Mutex}; use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; use borders_core::ui::protocol::{CameraStateUpdate, RenderInputEvent}; // Global state for input handling (needs to be accessible from Bevy systems) lazy_static::lazy_static! { static ref INPUT_STATE: Arc> = Arc::new(Mutex::new(borders_core::ui::input::InputState::new())); static ref CAMERA_STATE: Arc>> = Arc::new(Mutex::new(None)); } /// Handle render input events from the frontend (clicks, keys, hover) #[wasm_bindgen] pub fn handle_render_input(event: JsValue) -> Result<(), JsValue> { let event: RenderInputEvent = serde_wasm_bindgen::from_value(event).map_err(|e| JsValue::from_str(&format!("Failed to deserialize render input: {}", e)))?; let mut state = INPUT_STATE.lock().map_err(|e| JsValue::from_str(&format!("Failed to lock input state: {}", e)))?; // TODO: Get actual map width from GameView or TerrainData let map_width = 2560; // Placeholder borders_core::ui::handle_render_input(&event, &mut state, map_width).map_err(|e| JsValue::from_str(&e)) } /// Handle camera state updates from the frontend #[wasm_bindgen] pub fn handle_camera_update(state: JsValue) -> Result<(), JsValue> { let update: CameraStateUpdate = serde_wasm_bindgen::from_value(state).map_err(|e| JsValue::from_str(&format!("Failed to deserialize camera state: {}", e)))?; let mut camera_state = CAMERA_STATE.lock().map_err(|e| JsValue::from_str(&format!("Failed to lock camera state: {}", e)))?; borders_core::ui::handle_camera_update(update, &mut camera_state).map_err(|e| JsValue::from_str(&e)) } /// Get the global input state (for Bevy systems to access) pub fn get_input_state() -> Arc> { INPUT_STATE.clone() } /// Track an analytics event #[wasm_bindgen] pub fn track_analytics_event(event: JsValue) -> Result<(), JsValue> { #[derive(serde::Deserialize)] struct AnalyticsEventPayload { event: String, #[serde(default)] properties: std::collections::HashMap, } let payload: AnalyticsEventPayload = serde_wasm_bindgen::from_value(event).map_err(|e| JsValue::from_str(&format!("Failed to deserialize analytics event: {}", e)))?; let telemetry_event = borders_core::telemetry::TelemetryEvent { event: payload.event, properties: payload.properties }; // Spawn a task to track the event asynchronously wasm_bindgen_futures::spawn_local(async move { borders_core::telemetry::track(telemetry_event).await; }); Ok(()) }