load game.db
This commit is contained in:
@@ -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" }
|
||||
|
||||
@@ -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<dyn Fn() -> eyre::Result<Vec<EtiGame>> + Send + Sync>;
|
||||
use tokio::{
|
||||
fs::OpenOptions,
|
||||
io::{AsyncSeekExt, AsyncWriteExt},
|
||||
@@ -82,6 +86,15 @@ impl From<eyre::Report> for PeerError {
|
||||
pub fn start_peer(
|
||||
game_dir: String,
|
||||
tx_notify_ui: UnboundedSender<PeerEvent>,
|
||||
) -> eyre::Result<UnboundedSender<PeerCommand>> {
|
||||
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<PeerEvent>,
|
||||
eti_games_loader: Option<EtiGamesLoader>,
|
||||
) -> eyre::Result<UnboundedSender<PeerCommand>> {
|
||||
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<GameDB> {
|
||||
/// Load games from the ETI game.db resource file
|
||||
fn load_eti_games(eti_games_loader: Option<EtiGamesLoader>) -> eyre::Result<Vec<EtiGame>> {
|
||||
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<EtiGamesLoader>,
|
||||
) -> eyre::Result<GameDB> {
|
||||
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<Game> = 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<GameDB> {
|
||||
{
|
||||
// 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<RwLock<Option<String>>>,
|
||||
local_game_db: Arc<RwLock<Option<GameDB>>>,
|
||||
peer_game_db: Arc<RwLock<PeerGameDB>>,
|
||||
eti_games_loader: Option<EtiGamesLoader>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
struct PeerCtx {
|
||||
game_dir: Arc<RwLock<Option<String>>>,
|
||||
local_game_db: Arc<RwLock<Option<GameDB>>>,
|
||||
eti_games_loader: Option<EtiGamesLoader>,
|
||||
}
|
||||
|
||||
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<PeerCommand>,
|
||||
tx_notify_ui: UnboundedSender<PeerEvent>,
|
||||
) -> 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<PeerCommand>,
|
||||
tx_notify_ui: UnboundedSender<PeerEvent>,
|
||||
eti_games_loader: Option<EtiGamesLoader>,
|
||||
) -> 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");
|
||||
|
||||
Reference in New Issue
Block a user