This commit is contained in:
2025-11-08 17:27:01 +01:00
parent ade0c3fbc4
commit 6845a7d6fe
5 changed files with 120 additions and 6 deletions
+2
View File
@@ -62,6 +62,8 @@ impl From<EtiGame> for Game {
size: (eti_game.game_size * 1024.0 * 1024.0 * 1024.0) as u64, size: (eti_game.game_size * 1024.0 * 1024.0 * 1024.0) as u64,
thumbnail: None, thumbnail: None,
installed: false, installed: false,
eti_game_version: None,
local_version: None,
} }
} }
} }
+29
View File
@@ -6,6 +6,31 @@ use std::{collections::HashMap, fmt, path::Path};
use bytes::Bytes; use bytes::Bytes;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Read version from version.ini file
/// # Errors
/// Returns error if file cannot be read or parsed
pub fn read_version_from_ini(game_dir: &Path) -> eyre::Result<Option<String>> {
let version_file = game_dir.join("version.ini");
if !version_file.exists() {
return Ok(None);
}
let content = std::fs::read_to_string(&version_file)?;
let version = content.trim().to_string();
// Validate format (YYYYMMDD)
if version.len() == 8 && version.chars().all(|c| c.is_ascii_digit()) {
Ok(Some(version))
} else {
tracing::warn!(
"Invalid version format in {}: {}",
version_file.display(),
version
);
Ok(None)
}
}
/// A game /// A game
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub struct Game { pub struct Game {
@@ -31,6 +56,10 @@ pub struct Game {
pub thumbnail: Option<Bytes>, pub thumbnail: Option<Bytes>,
/// only relevant for client (yeah... I know) /// only relevant for client (yeah... I know)
pub installed: bool, pub installed: bool,
/// ETI game version from version.ini (YYYYMMDD format) (server)
pub eti_game_version: Option<String>,
/// Local game version from version.ini (YYYYMMDD format)
pub local_version: Option<String>,
} }
impl fmt::Debug for Game { impl fmt::Debug for Game {
+13
View File
@@ -65,6 +65,19 @@ async fn prepare_game_db(cli: &Cli) -> eyre::Result<GameDB> {
// filter out games that the server does not have in game_dir // filter out games that the server does not have in game_dir
games.retain(|game| cli.game_dir.join(&game.id).is_dir()); games.retain(|game| cli.game_dir.join(&game.id).is_dir());
// read version.ini files and update eti_game_version
for game in &mut games {
let game_dir = cli.game_dir.join(&game.id);
if let Ok(version) = lanspread_db::db::read_version_from_ini(&game_dir) {
game.eti_game_version = version;
if let Some(ref version) = game.eti_game_version {
tracing::debug!("Read version for game {}: {}", game.id, version);
}
} else {
tracing::warn!("Failed to read version.ini for game: {}", game.id);
}
}
let mut game_db = GameDB::from(games); let mut game_db = GameDB::from(games);
game_db.add_thumbnails(&cli.thumbs_dir); game_db.add_thumbnails(&cli.thumbs_dir);
@@ -160,6 +160,21 @@ fn set_game_install_state_from_path(game_db: &mut GameDB, path: &Path, installed
log::debug!("Set {game} to uninstalled"); log::debug!("Set {game} to uninstalled");
} }
game.installed = installed; game.installed = installed;
// Read local version.ini if installed
if installed {
if let Ok(version) = lanspread_db::db::read_version_from_ini(path) {
game.local_version = version;
if let Some(ref version) = game.local_version {
log::debug!("Read local version for game {}: {}", game.id, version);
}
} else {
log::warn!("Failed to read local version.ini for game: {}", game.id);
}
} else {
// Clear local version when uninstalled
game.local_version = None;
}
} }
} }
+61 -6
View File
@@ -26,6 +26,8 @@ interface Game {
thumbnail: Uint8Array; thumbnail: Uint8Array;
installed: boolean; installed: boolean;
install_status: InstallStatus; install_status: InstallStatus;
eti_game_version?: string;
local_version?: string;
} }
const App = () => { const App = () => {
@@ -204,6 +206,47 @@ const App = () => {
} }
}; };
const updateGame = async (id: string) => {
console.log(`🎯 Updating game with id=${id}`);
try {
const success = await invoke('install_game', { id });
if (success) {
console.log(`✅ Game update for id=${id} started...`);
// update install status in gameItems for this game
setGameItems(prev => prev.map(item => item.id === id
? { ...item, install_status: InstallStatus.CheckingServer }
: item));
} else {
// game is already being installed
console.warn(`🚧 Game with id=${id} is already being updated`);
}
} catch (error) {
console.error('❌ Error updating game:', error);
}
};
const needsUpdate = (game: Game): boolean => {
if (!game.installed) return false;
// Check if server has a version and we have a local version
const serverVersion = game.eti_game_version;
const localVersion = game.local_version;
// If we don't have local version but server has one, we need update
if (!localVersion && serverVersion) {
return true;
}
// If we have both versions, compare them numerically
if (localVersion && serverVersion) {
const localNum = parseInt(localVersion, 10);
const serverNum = parseInt(serverVersion, 10);
return serverNum > localNum;
}
return false;
};
const dialogGameDir = async () => { const dialogGameDir = async () => {
const file = await open({ const file = await open({
multiple: false, multiple: false,
@@ -259,14 +302,26 @@ const App = () => {
<span className="size-text">{(item.size / 1024 / 1024 / 1024).toFixed(1)} GB</span> <span className="size-text">{(item.size / 1024 / 1024 / 1024).toFixed(1)} GB</span>
</div> </div>
<div className="play-button" <div className="play-button"
onClick={() => item.installed onClick={() => {
? runGame(item.id) if (!item.installed) {
: installGame(item.id)}> installGame(item.id);
{item.installed ? 'Play' } else if (needsUpdate(item)) {
: item.install_status === InstallStatus.CheckingServer ? 'Checking server...' updateGame(item.id);
} else {
runGame(item.id);
}
}}>
{!item.installed
? item.install_status === InstallStatus.CheckingServer ? 'Checking server...'
: item.install_status === InstallStatus.Downloading ? 'Downloading...' : item.install_status === InstallStatus.Downloading ? 'Downloading...'
: item.install_status === InstallStatus.Unpacking ? 'Unpacking...' : item.install_status === InstallStatus.Unpacking ? 'Unpacking...'
: 'Install'} : 'Install'
: needsUpdate(item)
? item.install_status === InstallStatus.CheckingServer ? 'Checking server...'
: item.install_status === InstallStatus.Downloading ? 'Downloading...'
: item.install_status === InstallStatus.Unpacking ? 'Unpacking...'
: 'Update'
: 'Play'}
</div> </div>
</div> </div>
); );