mirror of
https://github.com/Xevion/smart-rgb.git
synced 2025-12-06 07:16:25 -06:00
Update source files
This commit is contained in:
176
crates/borders-desktop/src/plugin.rs
Normal file
176
crates/borders-desktop/src/plugin.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
//! Tauri-Bevy integration plugin
|
||||
//!
|
||||
//! This module provides the main integration between Tauri and Bevy, handling
|
||||
//! the main application loop and event bridging.
|
||||
|
||||
use borders_core::app::{App, Plugin, Update};
|
||||
use borders_core::time::Time;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use tauri::{Manager, RunEvent};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::time::Instant;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use web_time::Instant;
|
||||
|
||||
use crate::render_bridge::{TauriRenderBridgeTransport, cache_leaderboard_snapshot_system};
|
||||
|
||||
const TARGET_FPS: f64 = 60.0;
|
||||
|
||||
pub fn generate_tauri_context() -> tauri::Context {
|
||||
tauri::generate_context!()
|
||||
}
|
||||
|
||||
fn setup_tauri_integration(app: &mut App, tauri_app: &tauri::AppHandle, shared_render_state: Arc<Mutex<Option<borders_core::ui::protocol::RenderInit>>>, shared_leaderboard_state: Arc<Mutex<Option<borders_core::ui::protocol::LeaderboardSnapshot>>>) {
|
||||
tracing::debug!("Setup tauri integration");
|
||||
|
||||
// Register state for render bridge commands
|
||||
tauri_app.manage(Arc::new(Mutex::new(None::<borders_core::ui::protocol::CameraStateUpdate>)));
|
||||
tauri_app.manage(Arc::new(Mutex::new(None::<borders_core::networking::GameView>)));
|
||||
|
||||
// InputState - shared between Tauri commands and ECS systems
|
||||
let input_state_shared = Arc::new(Mutex::new(borders_core::ui::input::InputState::new()));
|
||||
tauri_app.manage(input_state_shared.clone());
|
||||
app.insert_non_send_resource(input_state_shared);
|
||||
|
||||
// Register shared state with Tauri (for get_game_state command)
|
||||
tauri_app.manage(shared_render_state.clone());
|
||||
tauri_app.manage(shared_leaderboard_state.clone());
|
||||
|
||||
// Get the message queue from the transport (already added as plugin)
|
||||
let transport = app.world().get_resource::<borders_core::ui::RenderBridge<TauriRenderBridgeTransport>>().expect("RenderBridge should be added by plugin");
|
||||
let message_queue = transport.transport.inbound_messages();
|
||||
tauri_app.manage(message_queue);
|
||||
|
||||
// Store shared states in world
|
||||
app.insert_non_send_resource(shared_leaderboard_state);
|
||||
}
|
||||
|
||||
pub struct TauriPlugin {
|
||||
setup: Box<dyn Fn() -> tauri::App + Send + Sync>,
|
||||
}
|
||||
|
||||
impl TauriPlugin {
|
||||
pub fn new<F>(setup: F) -> Self
|
||||
where
|
||||
F: Fn() -> tauri::App + Send + Sync + 'static,
|
||||
{
|
||||
Self { setup: Box::new(setup) }
|
||||
}
|
||||
}
|
||||
|
||||
impl TauriPlugin {
|
||||
pub fn build_and_run(self, mut app: App) -> ! {
|
||||
let tauri_app = (self.setup)();
|
||||
|
||||
// Create shared state for game state recovery
|
||||
let shared_render_state = Arc::new(Mutex::new(None::<borders_core::ui::protocol::RenderInit>));
|
||||
let shared_leaderboard_state = Arc::new(Mutex::new(None::<borders_core::ui::protocol::LeaderboardSnapshot>));
|
||||
|
||||
// Create transport for Tauri frontend (handles both render and UI communication)
|
||||
let transport = TauriRenderBridgeTransport::new(tauri_app.handle().clone(), shared_render_state.clone());
|
||||
|
||||
// Add the render bridge plugin to handle all frontend communication
|
||||
borders_core::ui::FrontendPlugin::new(transport).build(&mut app);
|
||||
|
||||
// Set up Tauri integration directly (no startup system needed)
|
||||
setup_tauri_integration(&mut app, tauri_app.handle(), shared_render_state, shared_leaderboard_state);
|
||||
|
||||
// Add the leaderboard caching system
|
||||
app.add_systems(Update, cache_leaderboard_snapshot_system);
|
||||
|
||||
// Run the app
|
||||
run_tauri_app(app, tauri_app);
|
||||
std::process::exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_tauri_app(app: App, tauri_app: tauri::App) {
|
||||
let app_rc = Rc::new(RefCell::new(app));
|
||||
let mut tauri_app = tauri_app;
|
||||
let mut is_initialized = false;
|
||||
let mut last_frame_time = Instant::now();
|
||||
|
||||
let target_frame_duration = Duration::from_secs_f64(1.0 / TARGET_FPS);
|
||||
|
||||
loop {
|
||||
let frame_start = Instant::now();
|
||||
|
||||
#[allow(deprecated)]
|
||||
tauri_app.run_iteration(move |_app_handle, event: RunEvent| {
|
||||
match event {
|
||||
tauri::RunEvent::Ready => {
|
||||
// Event acknowledged, actual setup happens below
|
||||
}
|
||||
tauri::RunEvent::ExitRequested { .. } => {
|
||||
// Track session end and flush analytics before exit
|
||||
if borders_core::telemetry::client().is_some() {
|
||||
tracing::debug!("ExitRequested: tracking session end and flushing analytics");
|
||||
|
||||
// Create a minimal runtime for blocking operations
|
||||
let runtime = tokio::runtime::Builder::new_current_thread().enable_time().enable_io().build().expect("Failed to create tokio runtime for flush");
|
||||
|
||||
runtime.block_on(async {
|
||||
// Track session end event
|
||||
borders_core::telemetry::track_session_end().await;
|
||||
|
||||
// Flush all pending events (the batch-triggered send is now synchronous)
|
||||
if let Some(client) = borders_core::telemetry::client() {
|
||||
let timeout = std::time::Duration::from_millis(500);
|
||||
match tokio::time::timeout(timeout, client.flush()).await {
|
||||
Ok(_) => {
|
||||
tracing::debug!("Analytics flushed successfully before exit")
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::warn!("Analytics flush timed out after 500ms")
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
if tauri_app.webview_windows().is_empty() {
|
||||
tauri_app.cleanup_before_exit();
|
||||
break;
|
||||
}
|
||||
|
||||
// Initialize game plugin on first iteration after Tauri is ready
|
||||
if !is_initialized {
|
||||
let mut app = app_rc.borrow_mut();
|
||||
|
||||
// Add core game plugin
|
||||
borders_core::GamePlugin::new(borders_core::plugin::NetworkMode::Local).build(&mut app);
|
||||
|
||||
app.run_startup();
|
||||
app.finish();
|
||||
app.cleanup();
|
||||
|
||||
is_initialized = true;
|
||||
last_frame_time = Instant::now(); // Reset timer after initialization
|
||||
tracing::info!("Game initialized");
|
||||
}
|
||||
|
||||
// Update time resource with delta from PREVIOUS frame
|
||||
let mut app = app_rc.borrow_mut();
|
||||
let delta = frame_start.duration_since(last_frame_time);
|
||||
if let Some(mut time) = app.world_mut().get_resource_mut::<Time>() {
|
||||
time.update(delta);
|
||||
}
|
||||
|
||||
app.update();
|
||||
|
||||
let frame_duration = frame_start.elapsed();
|
||||
if frame_duration < target_frame_duration {
|
||||
std::thread::sleep(target_frame_duration - frame_duration);
|
||||
}
|
||||
|
||||
last_frame_time = frame_start;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user