mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-14 16:51:18 +10:00
chore(downloads): progress on more precise download control
Signed-off-by: quexeky <git@quexeky.dev>
This commit is contained in:
@ -7,14 +7,17 @@ use log::info;
|
|||||||
use rayon::ThreadPoolBuilder;
|
use rayon::ThreadPoolBuilder;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::fs::{create_dir_all, File};
|
use std::fs::{create_dir_all, File};
|
||||||
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Mutex;
|
use std::sync::mpsc::Sender;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use urlencoding::encode;
|
use urlencoding::encode;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use rustix::fs::{fallocate, FallocateFlags};
|
use rustix::fs::{fallocate, FallocateFlags};
|
||||||
|
|
||||||
use super::download_logic::download_game_chunk;
|
use super::download_logic::download_game_chunk;
|
||||||
|
use super::download_manager::DownloadManagerSignal;
|
||||||
use super::download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag};
|
use super::download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag};
|
||||||
use super::progress_object::ProgressObject;
|
use super::progress_object::ProgressObject;
|
||||||
|
|
||||||
@ -26,6 +29,7 @@ pub struct GameDownloadAgent {
|
|||||||
contexts: Mutex<Vec<DropDownloadContext>>,
|
contexts: Mutex<Vec<DropDownloadContext>>,
|
||||||
pub manifest: Mutex<Option<DropManifest>>,
|
pub manifest: Mutex<Option<DropManifest>>,
|
||||||
pub progress: ProgressObject,
|
pub progress: ProgressObject,
|
||||||
|
sender: Sender<DownloadManagerSignal>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -34,6 +38,8 @@ pub enum GameDownloadError {
|
|||||||
Checksum,
|
Checksum,
|
||||||
Setup(SetupError),
|
Setup(SetupError),
|
||||||
Lock,
|
Lock,
|
||||||
|
IoError(io::Error),
|
||||||
|
DownloadError
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -48,12 +54,14 @@ impl Display for GameDownloadError {
|
|||||||
GameDownloadError::Setup(error) => write!(f, "{:?}", error),
|
GameDownloadError::Setup(error) => write!(f, "{:?}", error),
|
||||||
GameDownloadError::Lock => write!(f, "Failed to acquire lock. Something has gone very wrong internally. Please restart the application"),
|
GameDownloadError::Lock => write!(f, "Failed to acquire lock. Something has gone very wrong internally. Please restart the application"),
|
||||||
GameDownloadError::Checksum => write!(f, "Checksum failed to validate for download"),
|
GameDownloadError::Checksum => write!(f, "Checksum failed to validate for download"),
|
||||||
|
GameDownloadError::IoError(error) => write!(f, "{}", error),
|
||||||
|
GameDownloadError::DownloadError => write!(f, "Download failed. See Download Manager status for specific error"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameDownloadAgent {
|
impl GameDownloadAgent {
|
||||||
pub fn new(id: String, version: String, target_download_dir: usize) -> Self {
|
pub fn new(id: String, version: String, target_download_dir: usize, sender: Sender<DownloadManagerSignal>) -> Self {
|
||||||
// Don't run by default
|
// Don't run by default
|
||||||
let control_flag = DownloadThreadControl::new(DownloadThreadControlFlag::Stop);
|
let control_flag = DownloadThreadControl::new(DownloadThreadControlFlag::Stop);
|
||||||
Self {
|
Self {
|
||||||
@ -64,11 +72,11 @@ impl GameDownloadAgent {
|
|||||||
target_download_dir,
|
target_download_dir,
|
||||||
contexts: Mutex::new(Vec::new()),
|
contexts: Mutex::new(Vec::new()),
|
||||||
progress: ProgressObject::new(0, 0),
|
progress: ProgressObject::new(0, 0),
|
||||||
|
sender
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocking
|
// Blocking
|
||||||
// Requires mutable self
|
|
||||||
pub fn setup_download(&self) -> Result<(), GameDownloadError> {
|
pub fn setup_download(&self) -> Result<(), GameDownloadError> {
|
||||||
self.ensure_manifest_exists()?;
|
self.ensure_manifest_exists()?;
|
||||||
info!("Ensured manifest exists");
|
info!("Ensured manifest exists");
|
||||||
@ -84,7 +92,7 @@ impl GameDownloadAgent {
|
|||||||
// Blocking
|
// Blocking
|
||||||
pub fn download(&self) -> Result<(), GameDownloadError> {
|
pub fn download(&self) -> Result<(), GameDownloadError> {
|
||||||
self.setup_download()?;
|
self.setup_download()?;
|
||||||
self.run();
|
self.run().map_err(|_| GameDownloadError::DownloadError)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -197,7 +205,7 @@ impl GameDownloadAgent {
|
|||||||
Err(GameDownloadError::Setup(SetupError::Context))
|
Err(GameDownloadError::Setup(SetupError::Context))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&self) {
|
pub fn run(&self) -> Result<(), ()> {
|
||||||
const DOWNLOAD_MAX_THREADS: usize = 4;
|
const DOWNLOAD_MAX_THREADS: usize = 4;
|
||||||
|
|
||||||
let pool = ThreadPoolBuilder::new()
|
let pool = ThreadPoolBuilder::new()
|
||||||
@ -205,22 +213,48 @@ impl GameDownloadAgent {
|
|||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let new_contexts = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let new_contexts_ref = new_contexts.clone();
|
||||||
|
|
||||||
pool.scope(move |scope| {
|
pool.scope(move |scope| {
|
||||||
let contexts = self.contexts.lock().unwrap();
|
let contexts = self.contexts.lock().unwrap();
|
||||||
|
|
||||||
|
|
||||||
for (index, context) in contexts.iter().enumerate() {
|
for (index, context) in contexts.iter().enumerate() {
|
||||||
let context = context.clone();
|
let context = context.clone();
|
||||||
let control_flag = self.control_flag.clone(); // Clone arcs
|
let control_flag = self.control_flag.clone(); // Clone arcs
|
||||||
let progress = self.progress.get(index); // Clone arcs
|
let progress = self.progress.get(index); // Clone arcs
|
||||||
|
let new_contexts_ref = new_contexts_ref.clone();
|
||||||
|
|
||||||
scope.spawn(move |_| {
|
scope.spawn(move |_| {
|
||||||
info!(
|
info!(
|
||||||
"starting download for file {} {}",
|
"starting download for file {} {}",
|
||||||
context.file_name, context.index
|
context.file_name, context.index
|
||||||
);
|
);
|
||||||
download_game_chunk(context, control_flag, progress).unwrap();
|
match download_game_chunk(context.clone(), control_flag, progress) {
|
||||||
|
Ok(res) => {
|
||||||
|
match res {
|
||||||
|
true => {},
|
||||||
|
false => new_contexts_ref.lock().unwrap().push(context),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
info!("GameDownloadError: {}", e);
|
||||||
|
self.sender.send(DownloadManagerSignal::Error(e)).unwrap();
|
||||||
|
new_contexts_ref.lock().unwrap().push(context);
|
||||||
|
},
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
info!("Acquiring lock");
|
||||||
|
if !new_contexts.lock().unwrap().is_empty() {
|
||||||
|
info!("New contexts not empty");
|
||||||
|
*self.contexts.lock().unwrap() = Arc::into_inner(new_contexts).unwrap().into_inner().unwrap();
|
||||||
|
info!("Contexts: {:?}", *self.contexts.lock().unwrap());
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
info!("Contexts: {:?}", *self.contexts.lock().unwrap());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use crate::{AppError, AppState};
|
use crate::{AppError, AppState};
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@ -32,7 +30,18 @@ pub fn get_current_game_download_progress(
|
|||||||
Some(progress) => Ok(progress),
|
Some(progress) => Ok(progress),
|
||||||
None => Err(AppError::DoesNotExist),
|
None => Err(AppError::DoesNotExist),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn stop_game_download(
|
||||||
|
state: tauri::State<'_, Mutex<AppState>>,
|
||||||
|
game_id: String
|
||||||
|
) {
|
||||||
|
state
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.download_manager
|
||||||
|
.cancel_download(game_id);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
fn use_download_agent(
|
fn use_download_agent(
|
||||||
|
|||||||
@ -171,12 +171,12 @@ pub fn download_game_chunk(
|
|||||||
content_length.unwrap().try_into().unwrap(),
|
content_length.unwrap().try_into().unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let completed = pipeline.copy().unwrap();
|
let completed = pipeline.copy().map_err(|e| GameDownloadError::IoError(e))?;
|
||||||
if !completed {
|
if !completed {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
let checksum = pipeline.finish().unwrap();
|
let checksum = pipeline.finish().map_err(|e| GameDownloadError::IoError(e))?;
|
||||||
|
|
||||||
let res = hex::encode(checksum.0);
|
let res = hex::encode(checksum.0);
|
||||||
if res != ctx.checksum {
|
if res != ctx.checksum {
|
||||||
|
|||||||
@ -91,7 +91,7 @@ pub enum GameDownloadStatus {
|
|||||||
Downloading,
|
Downloading,
|
||||||
Paused,
|
Paused,
|
||||||
Uninitialised,
|
Uninitialised,
|
||||||
Error(GameDownloadError),
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DownloadManagerBuilder {
|
impl DownloadManagerBuilder {
|
||||||
@ -143,9 +143,9 @@ impl DownloadManagerBuilder {
|
|||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
DownloadManagerSignal::Error(game_download_error) => {
|
DownloadManagerSignal::Error(e) => {
|
||||||
self.manage_error_signal(game_download_error);
|
self.manage_error_signal(e);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,6 +178,7 @@ impl DownloadManagerBuilder {
|
|||||||
id.clone(),
|
id.clone(),
|
||||||
version,
|
version,
|
||||||
target_download_dir,
|
target_download_dir,
|
||||||
|
self.sender.clone()
|
||||||
));
|
));
|
||||||
let agent_status = GameDownloadStatus::Uninitialised;
|
let agent_status = GameDownloadStatus::Uninitialised;
|
||||||
let interface_data = Arc::new(AgentInterfaceData {
|
let interface_data = Arc::new(AgentInterfaceData {
|
||||||
@ -217,11 +218,14 @@ impl DownloadManagerBuilder {
|
|||||||
|
|
||||||
info!("Spawning download");
|
info!("Spawning download");
|
||||||
spawn(move || {
|
spawn(move || {
|
||||||
let signal = match download_agent.download() {
|
match download_agent.download() {
|
||||||
Ok(_) => DownloadManagerSignal::Completed(download_agent.id.clone()),
|
Ok(_) => {
|
||||||
Err(e) => DownloadManagerSignal::Error(e),
|
sender.send(DownloadManagerSignal::Completed(download_agent.id.clone()));
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
todo!() // Account for if the setup_download function fails
|
||||||
|
},
|
||||||
};
|
};
|
||||||
sender.send(signal).unwrap();
|
|
||||||
});
|
});
|
||||||
info!("Finished spawning Download");
|
info!("Finished spawning Download");
|
||||||
|
|
||||||
@ -237,7 +241,7 @@ impl DownloadManagerBuilder {
|
|||||||
fn manage_error_signal(&self, error: GameDownloadError) {
|
fn manage_error_signal(&self, error: GameDownloadError) {
|
||||||
let current_status = self.current_game_interface.clone().unwrap();
|
let current_status = self.current_game_interface.clone().unwrap();
|
||||||
let mut lock = current_status.status.lock().unwrap();
|
let mut lock = current_status.status.lock().unwrap();
|
||||||
*lock = GameDownloadStatus::Error(error);
|
*lock = GameDownloadStatus::Error;
|
||||||
self.set_status(DownloadManagerStatus::Error);
|
self.set_status(DownloadManagerStatus::Error);
|
||||||
}
|
}
|
||||||
fn set_status(&self, status: DownloadManagerStatus) {
|
fn set_status(&self, status: DownloadManagerStatus) {
|
||||||
|
|||||||
@ -74,6 +74,12 @@ impl DownloadManager {
|
|||||||
))?;
|
))?;
|
||||||
self.command_sender.send(DownloadManagerSignal::Go)
|
self.command_sender.send(DownloadManagerSignal::Go)
|
||||||
}
|
}
|
||||||
|
pub fn cancel_download(
|
||||||
|
&self,
|
||||||
|
id: String
|
||||||
|
) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
pub fn edit(&self) -> MutexGuard<'_, VecDeque<Arc<AgentInterfaceData>>> {
|
pub fn edit(&self) -> MutexGuard<'_, VecDeque<Arc<AgentInterfaceData>>> {
|
||||||
self.download_queue.lock().unwrap()
|
self.download_queue.lock().unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user