pub mod bridge; pub mod render_bridge; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; use wasm_bindgen::prelude::*; #[wasm_bindgen(start)] pub fn main() { // Set up panic hook for better error messages in the browser console_error_panic_hook::set_once(); // Initialize tracing for WASM (outputs to browser console) // Debug builds: debug level for our crates, info for dependencies // Release builds: warn level for our crates, error for dependencies #[cfg(debug_assertions)] let level_filter = "borders_core=debug,borders_protocol=debug,borders_wasm=debug,info"; #[cfg(not(debug_assertions))] let level_filter = "borders_core=warn,borders_protocol=warn,borders_wasm=warn,error"; if let Err(e) = tracing_subscriber::registry() .with(tracing_subscriber::EnvFilter::new(level_filter)) .with(wasm_tracing::WasmLayer::new( wasm_tracing::WasmLayerConfig::new() .set_show_fields(true) .set_report_logs_in_timings(true) .set_console_config(wasm_tracing::ConsoleConfig::ReportWithConsoleColor) // Only show origin (filename, line number) in debug builds .set_show_origin(true) .clone(), )) .try_init() { eprintln!("Failed to initialize tracing: {}", e); } // Log copyright information tracing::info!("Iron Borders v{}", env!("CARGO_PKG_VERSION")); tracing::info!("© 2025 Ryan Walters. All Rights Reserved."); // Start the Bevy app wasm_bindgen_futures::spawn_local(async { // Initialize telemetry (async to load user ID from IndexedDB) borders_core::telemetry::init(borders_core::telemetry::TelemetryConfig::default()).await; tracing::info!("Telemetry initialized"); run().await; }); } async fn run() { use borders_core::app::{App, Plugin}; use borders_core::time::Time; use std::time::Duration; use web_time::Instant; let mut app = App::new(); // Initialize time tracking app.insert_resource(Time::new()); // Add core game logic and frontend transport borders_core::GamePlugin::new(borders_core::plugin::NetworkMode::Local).build(&mut app); borders_core::ui::FrontendPlugin::new(render_bridge::WasmRenderBridgeTransport).build(&mut app); // Insert InputState as NonSend resource (shared with WASM bindings) let input_state_shared = bridge::get_input_state(); app.insert_non_send_resource(input_state_shared); // Run startup systems app.run_startup(); // Finish app setup app.finish(); app.cleanup(); // Manual update loop at 60 FPS (worker-compatible) let frame_time = Duration::from_millis(16); // ~60 FPS let mut last_frame_time = Instant::now(); loop { let frame_start = Instant::now(); // Update time resource with delta from PREVIOUS frame let delta = frame_start.duration_since(last_frame_time); if let Some(mut time) = app.world_mut().get_resource_mut::