UI & error fixes & QoL (#116)

* fix: use Arc<Error> instead of just ErrorKind

* fix: game status updates for UI

* fix: missing game version on push_game_update calls

* feat: wait if library load takes <300ms

* fix: clippy
This commit is contained in:
DecDuck
2025-08-15 22:56:49 +10:00
committed by GitHub
parent cb55ac2bf5
commit 17c375bcab
7 changed files with 90 additions and 107 deletions

View File

@ -484,19 +484,16 @@ impl GameDownloadAgent {
self.control_flag.set(DownloadThreadControlFlag::Go);
let status = ApplicationTransientStatus::Validating {
version_name: self.version.clone(),
};
let mut db_lock = borrow_db_mut_checked();
db_lock.applications.transient_statuses.insert(
self.metadata(),
ApplicationTransientStatus::Validating {
version_name: self.version.clone(),
},
);
push_game_update(
app_handle,
&self.metadata().id,
None,
GameStatusManager::fetch_state(&self.metadata().id, &db_lock),
);
db_lock
.applications
.transient_statuses
.insert(self.metadata(), status.clone());
push_game_update(app_handle, &self.metadata().id, None, (None, Some(status)));
}
pub fn validate(&self, app_handle: &AppHandle) -> Result<bool, ApplicationDownloadError> {

View File

@ -17,6 +17,7 @@ use std::fs::{Permissions, set_permissions};
use std::io::Read;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::sync::Arc;
use std::{
fs::{File, OpenOptions},
io::{self, BufWriter, Seek, SeekFrom, Write},
@ -191,12 +192,13 @@ pub fn download_game_bucket(
.to_str()
.unwrap();
for (i, raw_length) in lengths.split(",").enumerate() {
let length = raw_length.parse::<usize>().unwrap_or(0);
let Some(drop) = bucket.drops.get(i) else {
warn!("invalid number of Content-Lengths recieved: {}, {}", i, lengths);
warn!(
"invalid number of Content-Lengths recieved: {}, {}",
i, lengths
);
return Err(ApplicationDownloadError::DownloadError);
};
if drop.length != length {
@ -210,11 +212,11 @@ pub fn download_game_bucket(
let mut pipeline =
DropDownloadPipeline::new(response, bucket.drops.clone(), control_flag, progress)
.map_err(|e| ApplicationDownloadError::IoError(e.kind()))?;
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
let completed = pipeline
.copy()
.map_err(|e| ApplicationDownloadError::IoError(e.kind()))?;
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
if !completed {
return Ok(false);
}
@ -225,18 +227,20 @@ pub fn download_game_bucket(
for drop in bucket.drops.iter() {
let permissions = Permissions::from_mode(drop.permissions);
set_permissions(drop.path.clone(), permissions)
.map_err(|e| ApplicationDownloadError::IoError(e.kind()))?;
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
}
}
let checksums = pipeline
.finish()
.map_err(|e| ApplicationDownloadError::IoError(e.kind()))?;
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
for (index, drop) in bucket.drops.iter().enumerate() {
let res = hex::encode(**checksums.get(index).unwrap());
if res != drop.checksum {
return Err(ApplicationDownloadError::Checksum);
warn!("context didn't match... doing nothing because we will validate later.");
// return Ok(false);
// return Err(ApplicationDownloadError::Checksum);
}
}

View File

@ -14,6 +14,7 @@ use crate::database::models::data::{
ApplicationTransientStatus, DownloadableMetadata, GameDownloadStatus, GameVersion,
};
use crate::download_manager::download_manager_frontend::DownloadStatus;
use crate::error::drop_server_error::DropServerError;
use crate::error::library_error::LibraryError;
use crate::error::remote_access_error::RemoteAccessError;
use crate::games::state::{GameStatusManager, GameStatusWithTransient};
@ -89,7 +90,10 @@ pub async fn fetch_library_logic(
.await?;
if response.status() != 200 {
let err = response.json().await.unwrap();
let err = response.json().await.unwrap_or(DropServerError {
status_code: 500,
status_message: "Invalid response from server.".to_owned(),
});
warn!("{err:?}");
return Err(RemoteAccessError::InvalidResponse(err));
}
@ -358,8 +362,7 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
db_handle
.applications
.transient_statuses
.entry(meta.clone())
.and_modify(|v| *v = ApplicationTransientStatus::Uninstalling {});
.insert(meta.clone(), ApplicationTransientStatus::Uninstalling {});
push_game_update(
app_handle,
@ -393,8 +396,7 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
db_handle
.applications
.transient_statuses
.entry(meta.clone())
.and_modify(|v| *v = ApplicationTransientStatus::Uninstalling {});
.insert(meta.clone(), ApplicationTransientStatus::Uninstalling {});
drop(db_handle);
@ -412,8 +414,7 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
db_handle
.applications
.game_statuses
.entry(meta.id.clone())
.and_modify(|e| *e = GameDownloadStatus::Remote {});
.insert(meta.id.clone(), GameDownloadStatus::Remote {});
let _ = db_handle.applications.transient_statuses.remove(&meta);
push_game_update(
@ -425,8 +426,6 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
debug!("uninstalled game id {}", &meta.id);
app_handle.emit("update_library", ()).unwrap();
drop(db_handle);
}
});
} else {
@ -499,6 +498,7 @@ pub fn on_game_complete(
.game_statuses
.insert(meta.id.clone(), status.clone());
drop(db_handle);
app_handle
.emit(
&format!("update_game/{}", meta.id),
@ -519,6 +519,12 @@ pub fn push_game_update(
version: Option<GameVersion>,
status: GameStatusWithTransient,
) {
if let Some(GameDownloadStatus::Installed { .. } | GameDownloadStatus::SetupRequired { .. }) =
&status.0
&& version.is_none() {
panic!("pushed game for installed game that doesn't have version information");
}
app_handle
.emit(
&format!("update_game/{game_id}"),