diff --git a/Cargo.lock b/Cargo.lock index 0130271..76d3316 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2205,6 +2205,7 @@ dependencies = [ "bytes", "eyre", "gethostname", + "lanspread-compat", "lanspread-db", "lanspread-mdns", "lanspread-proto", @@ -2233,6 +2234,7 @@ name = "lanspread-tauri-deno-ts" version = "0.1.0" dependencies = [ "eyre", + "lanspread-compat", "lanspread-db", "lanspread-mdns", "lanspread-peer", diff --git a/crates/lanspread-peer/Cargo.toml b/crates/lanspread-peer/Cargo.toml index 04e4eaf..df60321 100644 --- a/crates/lanspread-peer/Cargo.toml +++ b/crates/lanspread-peer/Cargo.toml @@ -13,6 +13,7 @@ unwrap_used = "warn" [dependencies] # local +lanspread-compat = { path = "../lanspread-compat" } lanspread-db = { path = "../lanspread-db" } lanspread-mdns = { path = "../lanspread-mdns" } lanspread-proto = { path = "../lanspread-proto" } diff --git a/crates/lanspread-peer/src/lib.rs b/crates/lanspread-peer/src/lib.rs index 6b3d264..dc50731 100644 --- a/crates/lanspread-peer/src/lib.rs +++ b/crates/lanspread-peer/src/lib.rs @@ -12,6 +12,7 @@ use std::{ }; use bytes::BytesMut; +use lanspread_compat::eti::EtiGame; use lanspread_db::db::{Game, GameDB, GameFileDescription}; use lanspread_mdns::{LANSPREAD_SERVICE_TYPE, MdnsAdvertiser, discover_service}; use lanspread_proto::{Message, Request, Response}; @@ -23,6 +24,9 @@ use s2n_quic::{ provider::limits::Limits, stream::BidirectionalStream, }; + +/// Handle for loading ETI games from Tauri resources +pub type EtiGamesLoader = Arc eyre::Result> + Send + Sync>; use tokio::{ fs::OpenOptions, io::{AsyncSeekExt, AsyncWriteExt}, @@ -82,6 +86,15 @@ impl From for PeerError { pub fn start_peer( game_dir: String, tx_notify_ui: UnboundedSender, +) -> eyre::Result> { + start_peer_with_eti_loader(game_dir, tx_notify_ui, None) +} + +/// Initialize and start the peer system with ETI games loader +pub fn start_peer_with_eti_loader( + game_dir: String, + tx_notify_ui: UnboundedSender, + eti_games_loader: Option, ) -> eyre::Result> { log::info!("Starting peer system with game directory: {game_dir}"); @@ -90,7 +103,7 @@ pub fn start_peer( // Start the peer in a background task let tx_control_clone = tx_control.clone(); tokio::spawn(async move { - if let Err(e) = run_peer(rx_control, tx_notify_ui).await { + if let Err(e) = run_peer_with_eti_loader(rx_control, tx_notify_ui, eti_games_loader).await { log::error!("Peer system failed: {e}"); } }); @@ -1077,13 +1090,29 @@ async fn retry_failed_chunks( exhausted } -async fn load_local_game_db(game_dir: &str) -> eyre::Result { +/// Load games from the ETI game.db resource file +fn load_eti_games(eti_games_loader: Option) -> eyre::Result> { + if let Some(loader) = eti_games_loader { + loader() + } else { + log::warn!("ETI games loader not provided - returning empty games list"); + Ok(Vec::new()) + } +} + +/// Load local game database combining ETI games and locally installed games +async fn load_local_game_db_with_eti( + game_dir: &str, + eti_games_loader: Option, +) -> eyre::Result { let game_path = PathBuf::from(game_dir); - // Scan game directory for game folders - let mut games = Vec::new(); - let mut entries = tokio::fs::read_dir(&game_path).await?; + // Load games from ETI database using the loader (from() sets installed=false, local_version=None) + let eti_games = load_eti_games(eti_games_loader)?; + let mut games: Vec = eti_games.into_iter().map(Into::into).collect(); + // Scan game directory and mark found games as installed + let mut entries = tokio::fs::read_dir(&game_path).await?; while let Some(entry) = entries.next_entry().await? { let path = entry.path(); if path.is_dir() @@ -1091,23 +1120,31 @@ async fn load_local_game_db(game_dir: &str) -> eyre::Result { { // Check if this game has a version.ini file if let Ok(version) = lanspread_db::db::read_version_from_ini(&path) { - let size = calculate_directory_size(&path).await?; - let game = Game { - id: game_id.to_string(), - name: game_id.to_string(), // Use folder name as game name for now - description: String::new(), - release_year: String::new(), - publisher: String::new(), - max_players: 1, - version: "1.0".to_string(), - genre: String::new(), - size, - thumbnail: None, - installed: true, - eti_game_version: version.clone(), - local_version: version, - }; - games.push(game); + // Update existing game or create new one if not found in ETI DB + if let Some(game) = games.iter_mut().find(|g| g.id == game_id) { + game.installed = true; + game.local_version.clone_from(&version); + // Keep the ETI size for informational purposes + } else { + // Game not found in ETI DB, create basic entry + let size = calculate_directory_size(&path).await?; + let game = Game { + id: game_id.to_string(), + name: game_id.to_string(), + description: String::new(), + release_year: String::new(), + publisher: String::new(), + max_players: 1, + version: "1.0".to_string(), + genre: String::new(), + size, + thumbnail: None, + installed: true, + eti_game_version: version.clone(), + local_version: version, + }; + games.push(game); + } } } } @@ -1137,12 +1174,24 @@ struct Ctx { game_dir: Arc>>, local_game_db: Arc>>, peer_game_db: Arc>, + eti_games_loader: Option, } -#[derive(Clone, Debug)] +#[derive(Clone)] struct PeerCtx { game_dir: Arc>>, local_game_db: Arc>>, + eti_games_loader: Option, +} + +impl std::fmt::Debug for PeerCtx { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PeerCtx") + .field("game_dir", &"...") + .field("local_game_db", &"...") + .field("eti_games_loader", &self.eti_games_loader.is_some()) + .finish() + } } /// Main peer execution loop that handles peer commands and manages the peer system. @@ -1152,19 +1201,30 @@ struct PeerCtx { /// This function will panic if the games folder is None after being checked for None. /// The panic occurs at line 908 where `games_folder.expect("checked above")` is called. pub async fn run_peer( + rx_control: UnboundedReceiver, + tx_notify_ui: UnboundedSender, +) -> eyre::Result<()> { + run_peer_with_eti_loader(rx_control, tx_notify_ui, None).await +} + +/// Main peer execution loop that handles peer commands and manages the peer system with ETI games loader. +pub async fn run_peer_with_eti_loader( mut rx_control: UnboundedReceiver, tx_notify_ui: UnboundedSender, + eti_games_loader: Option, ) -> eyre::Result<()> { // peer context let ctx = Ctx { game_dir: Arc::new(RwLock::new(None)), local_game_db: Arc::new(RwLock::new(None)), peer_game_db: Arc::new(RwLock::new(PeerGameDB::new())), + eti_games_loader, }; let peer_ctx = PeerCtx { game_dir: ctx.game_dir.clone(), local_game_db: ctx.local_game_db.clone(), + eti_games_loader: ctx.eti_games_loader.clone(), }; // Start server component @@ -1431,8 +1491,9 @@ async fn handle_set_game_dir_command(ctx: &Ctx, game_dir: String) { // Load local game database when game directory is set let game_dir = game_dir.clone(); let local_game_db = ctx.local_game_db.clone(); + let eti_games_loader = ctx.eti_games_loader.clone(); tokio::spawn(async move { - match load_local_game_db(&game_dir).await { + match load_local_game_db_with_eti(&game_dir, eti_games_loader).await { Ok(db) => { *local_game_db.write().await = Some(db); log::info!("Local game database loaded successfully"); diff --git a/crates/lanspread-tauri-deno-ts/src-tauri/Cargo.toml b/crates/lanspread-tauri-deno-ts/src-tauri/Cargo.toml index a4df579..2fedac9 100644 --- a/crates/lanspread-tauri-deno-ts/src-tauri/Cargo.toml +++ b/crates/lanspread-tauri-deno-ts/src-tauri/Cargo.toml @@ -31,6 +31,7 @@ tauri-build = { version = "2", features = [] } lanspread-peer = { path = "../../lanspread-peer" } lanspread-db = { path = "../../lanspread-db" } lanspread-mdns = { path = "../../lanspread-mdns" } +lanspread-compat = { path = "../../lanspread-compat" } # external eyre = { workspace = true } diff --git a/crates/lanspread-tauri-deno-ts/src-tauri/game.db b/crates/lanspread-tauri-deno-ts/src-tauri/game.db new file mode 100644 index 0000000..9c70efd Binary files /dev/null and b/crates/lanspread-tauri-deno-ts/src-tauri/game.db differ diff --git a/crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs b/crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs index 85b773b..d375c59 100644 --- a/crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs +++ b/crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs @@ -5,8 +5,9 @@ use std::{ }; use eyre::bail; +use lanspread_compat::eti::get_games; use lanspread_db::db::{Game, GameDB}; -use lanspread_peer::{PeerCommand, PeerEvent, start_peer}; +use lanspread_peer::{EtiGamesLoader, PeerCommand, PeerEvent, start_peer_with_eti_loader}; use tauri::{AppHandle, Emitter as _, Manager}; use tauri_plugin_shell::{ShellExt, process::Command}; use tokio::sync::{RwLock, mpsc::UnboundedSender}; @@ -520,12 +521,33 @@ pub fn run() { }; if !games_folder.is_empty() { + // Create ETI games loader that can access the Tauri resource + let app_handle_for_loader = app_handle_clone.clone(); + let eti_games_loader: EtiGamesLoader = Arc::new(move || { + let app_handle = app_handle_for_loader.clone(); + // Use tokio's block_in_place to call async function from sync context + tokio::task::block_in_place(move || { + tokio::runtime::Handle::current().block_on(async move { + // Resolve the game.db resource path + match app_handle.path().resolve("game.db", tauri::path::BaseDirectory::Resource) { + Ok(game_db_path) => { + get_games(&game_db_path).await + } + Err(e) => { + log::error!("Failed to resolve game.db resource: {e}"); + Err(eyre::eyre!("Failed to resolve game.db resource: {e}")) + } + } + }) + }) + }); + // Only start peer system when we have a valid games directory - match start_peer(games_folder, tx_peer_event_clone) { + match start_peer_with_eti_loader(games_folder, tx_peer_event_clone, Some(eti_games_loader)) { Ok(peer_ctrl) => { let state = app_handle_clone.state::(); *state.peer_ctrl.write().await = Some(peer_ctrl); - log::info!("Peer system initialized successfully with games directory"); + log::info!("Peer system initialized successfully with games directory and ETI loader"); // Start peer discovery and request games from other peers request_games(state); diff --git a/crates/lanspread-tauri-deno-ts/src-tauri/tauri.conf.json b/crates/lanspread-tauri-deno-ts/src-tauri/tauri.conf.json index 12a0f28..f2b4321 100644 --- a/crates/lanspread-tauri-deno-ts/src-tauri/tauri.conf.json +++ b/crates/lanspread-tauri-deno-ts/src-tauri/tauri.conf.json @@ -33,6 +33,9 @@ ], "externalBin": [ "binaries/unrar" + ], + "resources": [ + "game.db" ] } }