mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-14 16:51:18 +10:00
refactor(game status): transient vs synced state now defined
This commit is contained in:
@ -24,11 +24,8 @@ pub struct DatabaseAuth {
|
||||
// Strings are version names for a particular game
|
||||
#[derive(Serialize, Clone, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum DatabaseGameStatus {
|
||||
pub enum GameStatus {
|
||||
Remote {},
|
||||
Downloading {
|
||||
version_name: String,
|
||||
},
|
||||
SetupRequired {
|
||||
version_name: String,
|
||||
install_dir: String,
|
||||
@ -37,10 +34,14 @@ pub enum DatabaseGameStatus {
|
||||
version_name: String,
|
||||
install_dir: String,
|
||||
},
|
||||
Updating {
|
||||
version_name: String,
|
||||
},
|
||||
}
|
||||
|
||||
// Stuff that shouldn't be synced to disk
|
||||
#[derive(Clone, Serialize)]
|
||||
pub enum GameTransientStatus {
|
||||
Downloading { version_name: String },
|
||||
Uninstalling {},
|
||||
Updating { version_name: String },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
@ -58,8 +59,11 @@ pub struct GameVersion {
|
||||
pub struct DatabaseGames {
|
||||
pub install_dirs: Vec<String>,
|
||||
// Guaranteed to exist if the game also exists in the app state map
|
||||
pub games_statuses: HashMap<String, DatabaseGameStatus>,
|
||||
pub game_versions: HashMap<String, HashMap<String, GameVersion>>,
|
||||
pub statuses: HashMap<String, GameStatus>,
|
||||
pub versions: HashMap<String, HashMap<String, GameVersion>>,
|
||||
|
||||
#[serde(skip)]
|
||||
pub transient_statuses: HashMap<String, GameTransientStatus>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone, Deserialize)]
|
||||
@ -119,8 +123,9 @@ impl DatabaseImpls for DatabaseInterface {
|
||||
base_url: "".to_string(),
|
||||
games: DatabaseGames {
|
||||
install_dirs: vec![games_base_dir.to_str().unwrap().to_string()],
|
||||
games_statuses: HashMap::new(),
|
||||
game_versions: HashMap::new(),
|
||||
statuses: HashMap::new(),
|
||||
transient_statuses: HashMap::new(),
|
||||
versions: HashMap::new(),
|
||||
},
|
||||
};
|
||||
debug!(
|
||||
|
||||
@ -40,6 +40,11 @@ pub fn move_game_in_queue(
|
||||
.rearrange(old_index, new_index)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn cancel_game(state: tauri::State<'_, Mutex<AppState>>, game_id: String) {
|
||||
state.lock().unwrap().download_manager.cancel(game_id)
|
||||
}
|
||||
|
||||
/*
|
||||
#[tauri::command]
|
||||
pub fn get_current_write_speed(state: tauri::State<'_, Mutex<AppState>>) {}
|
||||
|
||||
@ -33,7 +33,10 @@ pub enum DownloadManagerSignal {
|
||||
/// download, sync everything to disk, and
|
||||
/// then exit
|
||||
Finish,
|
||||
/// Stops (but doesn't remove) current download
|
||||
Cancel,
|
||||
/// Removes a given game
|
||||
Remove(String),
|
||||
/// Any error which occurs in the agent
|
||||
Error(GameDownloadError),
|
||||
/// Pushes UI update
|
||||
@ -142,6 +145,11 @@ impl DownloadManager {
|
||||
.send(DownloadManagerSignal::Update)
|
||||
.unwrap();
|
||||
}
|
||||
pub fn cancel(&self, game_id: String) {
|
||||
self.command_sender
|
||||
.send(DownloadManagerSignal::Remove(game_id))
|
||||
.unwrap();
|
||||
}
|
||||
pub fn rearrange(&self, current_index: usize, new_index: usize) {
|
||||
if current_index == new_index {
|
||||
return;
|
||||
@ -159,8 +167,8 @@ impl DownloadManager {
|
||||
let mut queue = self.edit();
|
||||
let to_move = queue.remove(current_index).unwrap();
|
||||
queue.insert(new_index, to_move);
|
||||
|
||||
info!("new queue: {:?}", queue);
|
||||
drop(queue);
|
||||
|
||||
if needs_pause {
|
||||
self.command_sender.send(DownloadManagerSignal::Go).unwrap();
|
||||
|
||||
@ -2,7 +2,7 @@ use std::{
|
||||
collections::HashMap,
|
||||
sync::{
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Arc, Mutex,
|
||||
Arc, Mutex, RwLockWriteGuard,
|
||||
},
|
||||
thread::{spawn, JoinHandle},
|
||||
};
|
||||
@ -11,8 +11,9 @@ use log::{error, info};
|
||||
use tauri::{AppHandle, Emitter};
|
||||
|
||||
use crate::{
|
||||
db::DatabaseGameStatus,
|
||||
db::{Database, GameStatus, GameTransientStatus},
|
||||
library::{on_game_complete, GameUpdateEvent, QueueUpdateEvent, QueueUpdateEventQueueData},
|
||||
state::GameStatusManager,
|
||||
DB,
|
||||
};
|
||||
|
||||
@ -107,14 +108,18 @@ impl DownloadManagerBuilder {
|
||||
DownloadManager::new(terminator, queue, active_progress, command_sender)
|
||||
}
|
||||
|
||||
fn set_game_status(&self, id: String, status: DatabaseGameStatus) {
|
||||
fn set_game_status<F: FnOnce(&mut RwLockWriteGuard<'_, Database>, &String) -> ()>(
|
||||
&self,
|
||||
id: String,
|
||||
setter: F,
|
||||
) {
|
||||
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||
db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.insert(id.clone(), status.clone());
|
||||
setter(&mut db_handle, &id);
|
||||
drop(db_handle);
|
||||
DB.save().unwrap();
|
||||
|
||||
let status = GameStatusManager::fetch_state(&id);
|
||||
|
||||
self.app_handle
|
||||
.emit(
|
||||
&format!("update_game/{}", id),
|
||||
@ -208,10 +213,35 @@ impl DownloadManagerBuilder {
|
||||
self.stop_and_wait_current_download();
|
||||
return Ok(());
|
||||
}
|
||||
DownloadManagerSignal::Remove(game_id) => {
|
||||
self.manage_remove_game(game_id);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn manage_remove_game(&mut self, game_id: String) {
|
||||
if let Some(current_download) = &self.current_download_agent {
|
||||
if current_download.id == game_id {
|
||||
self.manage_cancel_signal();
|
||||
}
|
||||
}
|
||||
|
||||
let index = self.download_queue.get_by_id(game_id.clone()).unwrap();
|
||||
let mut queue_handle = self.download_queue.edit();
|
||||
queue_handle.remove(index);
|
||||
self.set_game_status(game_id, |db_handle, id| {
|
||||
db_handle.games.transient_statuses.remove(id);
|
||||
});
|
||||
drop(queue_handle);
|
||||
|
||||
if self.current_download_agent.is_none() {
|
||||
self.manage_go_signal();
|
||||
}
|
||||
|
||||
self.push_manager_update();
|
||||
}
|
||||
|
||||
fn manage_stop_signal(&mut self) {
|
||||
info!("Got signal 'Stop'");
|
||||
self.set_status(DownloadManagerStatus::Paused);
|
||||
@ -273,7 +303,12 @@ impl DownloadManagerBuilder {
|
||||
.insert(interface_data.id.clone(), download_agent);
|
||||
self.download_queue.append(interface_data);
|
||||
|
||||
self.set_game_status(id, DatabaseGameStatus::Downloading { version_name });
|
||||
self.set_game_status(id, |db, id| {
|
||||
db.games.transient_statuses.insert(
|
||||
id.to_string(),
|
||||
GameTransientStatus::Downloading { version_name },
|
||||
);
|
||||
});
|
||||
self.sender.send(DownloadManagerSignal::Update).unwrap();
|
||||
}
|
||||
|
||||
@ -344,10 +379,12 @@ impl DownloadManagerBuilder {
|
||||
// Set flags for download manager
|
||||
active_control_flag.set(DownloadThreadControlFlag::Go);
|
||||
self.set_status(DownloadManagerStatus::Downloading);
|
||||
self.set_game_status(
|
||||
self.current_download_agent.as_ref().unwrap().id.clone(),
|
||||
DatabaseGameStatus::Downloading { version_name },
|
||||
);
|
||||
self.set_game_status(agent_data.id.clone(), |db, id| {
|
||||
db.games.transient_statuses.insert(
|
||||
id.to_string(),
|
||||
GameTransientStatus::Downloading { version_name },
|
||||
);
|
||||
});
|
||||
|
||||
self.sender.send(DownloadManagerSignal::Update).unwrap();
|
||||
}
|
||||
@ -361,7 +398,9 @@ impl DownloadManagerBuilder {
|
||||
self.set_status(DownloadManagerStatus::Error(error));
|
||||
|
||||
let game_id = current_status.id.clone();
|
||||
self.set_game_status(game_id, DatabaseGameStatus::Remote {});
|
||||
self.set_game_status(game_id, |db_handle, id| {
|
||||
db_handle.games.transient_statuses.remove(id);
|
||||
});
|
||||
|
||||
self.sender.send(DownloadManagerSignal::Update).unwrap();
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ mod library;
|
||||
|
||||
mod process;
|
||||
mod remote;
|
||||
mod settings;
|
||||
mod state;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@ -178,6 +178,7 @@ pub fn run() {
|
||||
move_game_in_queue,
|
||||
pause_game_downloads,
|
||||
resume_game_downloads,
|
||||
cancel_game,
|
||||
// Processes
|
||||
launch_game,
|
||||
])
|
||||
|
||||
@ -5,18 +5,19 @@ use tauri::Emitter;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use urlencoding::encode;
|
||||
|
||||
use crate::db::DatabaseGameStatus;
|
||||
use crate::db::DatabaseImpls;
|
||||
use crate::db::GameVersion;
|
||||
use crate::db::{GameStatus, GameTransientStatus};
|
||||
use crate::downloads::download_manager::GameDownloadStatus;
|
||||
use crate::process::process_manager::Platform;
|
||||
use crate::remote::RemoteAccessError;
|
||||
use crate::state::{GameStatusManager, GameStatusWithTransient};
|
||||
use crate::{auth::generate_authorization_header, AppState, DB};
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct FetchGameStruct {
|
||||
game: Game,
|
||||
status: DatabaseGameStatus,
|
||||
status: GameStatusWithTransient,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
@ -36,7 +37,7 @@ pub struct Game {
|
||||
#[derive(serde::Serialize, Clone)]
|
||||
pub struct GameUpdateEvent {
|
||||
pub game_id: String,
|
||||
pub status: DatabaseGameStatus,
|
||||
pub status: (Option<GameStatus>, Option<GameTransientStatus>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
@ -61,6 +62,7 @@ pub struct GameVersionOption {
|
||||
setup_command: String,
|
||||
launch_command: String,
|
||||
delta: bool,
|
||||
umu_id_override: Option<String>,
|
||||
// total_size: usize,
|
||||
}
|
||||
|
||||
@ -89,11 +91,11 @@ fn fetch_library_logic(app: AppHandle) -> Result<Vec<Game>, RemoteAccessError> {
|
||||
|
||||
for game in games.iter() {
|
||||
handle.games.insert(game.id.clone(), game.clone());
|
||||
if !db_handle.games.games_statuses.contains_key(&game.id) {
|
||||
if !db_handle.games.statuses.contains_key(&game.id) {
|
||||
db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.insert(game.id.clone(), DatabaseGameStatus::Remote {});
|
||||
.statuses
|
||||
.insert(game.id.clone(), GameStatus::Remote {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,16 +118,11 @@ fn fetch_game_logic(
|
||||
|
||||
let game = state_handle.games.get(&id);
|
||||
if let Some(game) = game {
|
||||
let db_handle = DB.borrow_data().unwrap();
|
||||
let status = GameStatusManager::fetch_state(&id);
|
||||
|
||||
let data = FetchGameStruct {
|
||||
game: game.clone(),
|
||||
status: db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.get(&game.id)
|
||||
.unwrap()
|
||||
.clone(),
|
||||
status,
|
||||
};
|
||||
|
||||
return Ok(data);
|
||||
@ -158,28 +155,23 @@ fn fetch_game_logic(
|
||||
|
||||
db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.entry(id)
|
||||
.or_insert(DatabaseGameStatus::Remote {});
|
||||
.statuses
|
||||
.entry(id.clone())
|
||||
.or_insert(GameStatus::Remote {});
|
||||
drop(db_handle);
|
||||
|
||||
let status = GameStatusManager::fetch_state(&id);
|
||||
|
||||
let data = FetchGameStruct {
|
||||
game: game.clone(),
|
||||
status: db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.get(&game.id)
|
||||
.unwrap()
|
||||
.clone(),
|
||||
status,
|
||||
};
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fetch_game(
|
||||
id: String,
|
||||
app: tauri::AppHandle,
|
||||
) -> Result<FetchGameStruct, String> {
|
||||
pub fn fetch_game(id: String, app: tauri::AppHandle) -> Result<FetchGameStruct, String> {
|
||||
let result = fetch_game_logic(id, app);
|
||||
|
||||
if result.is_err() {
|
||||
@ -190,15 +182,8 @@ pub fn fetch_game(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fetch_game_status(id: String) -> Result<DatabaseGameStatus, String> {
|
||||
let db_handle = DB.borrow_data().unwrap();
|
||||
let status = db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.get(&id)
|
||||
.unwrap_or(&DatabaseGameStatus::Remote {})
|
||||
.clone();
|
||||
drop(db_handle);
|
||||
pub fn fetch_game_status(id: String) -> Result<GameStatusWithTransient, String> {
|
||||
let status = GameStatusManager::fetch_state(&id);
|
||||
|
||||
Ok(status)
|
||||
}
|
||||
@ -277,7 +262,7 @@ pub fn on_game_complete(
|
||||
let mut handle = DB.borrow_data_mut().unwrap();
|
||||
handle
|
||||
.games
|
||||
.game_versions
|
||||
.versions
|
||||
.entry(game_id.clone())
|
||||
.or_default()
|
||||
.insert(version_name.clone(), data.clone());
|
||||
@ -285,12 +270,12 @@ pub fn on_game_complete(
|
||||
DB.save().unwrap();
|
||||
|
||||
let status = if data.setup_command.is_empty() {
|
||||
DatabaseGameStatus::Installed {
|
||||
GameStatus::Installed {
|
||||
version_name,
|
||||
install_dir,
|
||||
}
|
||||
} else {
|
||||
DatabaseGameStatus::SetupRequired {
|
||||
GameStatus::SetupRequired {
|
||||
version_name,
|
||||
install_dir,
|
||||
}
|
||||
@ -299,14 +284,17 @@ pub fn on_game_complete(
|
||||
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||
db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.statuses
|
||||
.insert(game_id.clone(), status.clone());
|
||||
drop(db_handle);
|
||||
DB.save().unwrap();
|
||||
app_handle
|
||||
.emit(
|
||||
&format!("update_game/{}", game_id),
|
||||
GameUpdateEvent { game_id, status },
|
||||
GameUpdateEvent {
|
||||
game_id,
|
||||
status: (Some(status), None),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
db::{DatabaseGameStatus, DATA_ROOT_DIR},
|
||||
db::{GameStatus, DATA_ROOT_DIR},
|
||||
DB,
|
||||
};
|
||||
|
||||
@ -74,11 +74,11 @@ impl ProcessManager {
|
||||
let db_lock = DB.borrow_data().unwrap();
|
||||
let game_status = db_lock
|
||||
.games
|
||||
.games_statuses
|
||||
.statuses
|
||||
.get(&game_id)
|
||||
.ok_or("Game not installed")?;
|
||||
|
||||
let DatabaseGameStatus::Installed {
|
||||
let GameStatus::Installed {
|
||||
version_name,
|
||||
install_dir,
|
||||
} = game_status
|
||||
@ -88,7 +88,7 @@ impl ProcessManager {
|
||||
|
||||
let game_version = db_lock
|
||||
.games
|
||||
.game_versions
|
||||
.versions
|
||||
.get(&game_id)
|
||||
.ok_or("Invalid game ID".to_owned())?
|
||||
.get(version_name)
|
||||
|
||||
@ -1 +0,0 @@
|
||||
|
||||
31
src-tauri/src/state.rs
Normal file
31
src-tauri/src/state.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
db::{GameStatus, GameTransientStatus},
|
||||
DB,
|
||||
};
|
||||
|
||||
pub type GameStatusWithTransient = (
|
||||
Option<GameStatus>,
|
||||
Option<GameTransientStatus>,
|
||||
);
|
||||
pub struct GameStatusManager {}
|
||||
|
||||
impl GameStatusManager {
|
||||
pub fn fetch_state(game_id: &String) -> GameStatusWithTransient {
|
||||
let db_lock = DB.borrow_data().unwrap();
|
||||
let offline_state = db_lock.games.statuses.get(game_id).cloned();
|
||||
let online_state = db_lock.games.transient_statuses.get(game_id).cloned();
|
||||
drop(db_lock);
|
||||
|
||||
if online_state.is_some() {
|
||||
return (None, online_state);
|
||||
}
|
||||
|
||||
if offline_state.is_some() {
|
||||
return (offline_state, None);
|
||||
}
|
||||
|
||||
return (None, None);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user