mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-13 16:22:43 +10:00
refactor(download manager): rename files to what they contain
This commit is contained in:
@ -28,7 +28,6 @@ pub enum DatabaseGameStatus {
|
|||||||
Downloading,
|
Downloading,
|
||||||
Installed,
|
Installed,
|
||||||
Updating,
|
Updating,
|
||||||
|
|
||||||
Uninstalling,
|
Uninstalling,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,69 +1,20 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
any::Any,
|
||||||
|
collections::VecDeque,
|
||||||
sync::{
|
sync::{
|
||||||
mpsc::{channel, Receiver, Sender},
|
mpsc::{SendError, Sender},
|
||||||
Arc, Mutex,
|
Arc, Mutex, MutexGuard,
|
||||||
},
|
},
|
||||||
thread::spawn,
|
thread::JoinHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::{info, warn};
|
use log::info;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
download_agent::{GameDownloadAgent, GameDownloadError},
|
download_agent::{GameDownloadAgent, GameDownloadError},
|
||||||
download_manager_interface::{AgentInterfaceData, DownloadManager},
|
|
||||||
download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag},
|
|
||||||
progress_object::ProgressObject, queue::Queue,
|
progress_object::ProgressObject, queue::Queue,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Welcome to the download manager, the most overengineered, glorious piece of bullshit.
|
|
||||||
|
|
||||||
The download manager takes a queue of game_ids and their associated
|
|
||||||
GameDownloadAgents, and then, one-by-one, executes them. It provides an interface
|
|
||||||
to interact with the currently downloading agent, and manage the queue.
|
|
||||||
|
|
||||||
When the DownloadManager is initialised, it is designed to provide a reference
|
|
||||||
which can be used to provide some instructions (the DownloadManagerInterface),
|
|
||||||
but other than that, it runs without any sort of interruptions.
|
|
||||||
|
|
||||||
It does this by opening up two data structures. Primarily is the command_receiver,
|
|
||||||
and mpsc (multi-channel-single-producer) which allows commands to be sent from
|
|
||||||
the Interface, and queued up for the Manager to process.
|
|
||||||
|
|
||||||
These have been mapped in the DownloadManagerSignal docs.
|
|
||||||
|
|
||||||
The other way to interact with the DownloadManager is via the donwload_queue,
|
|
||||||
which is just a collection of ids which may be rearranged to suit
|
|
||||||
whichever download queue order is required.
|
|
||||||
|
|
||||||
+----------------------------------------------------------------------------+
|
|
||||||
| DO NOT ATTEMPT TO ADD OR REMOVE FROM THE QUEUE WITHOUT USING SIGNALS!! |
|
|
||||||
| THIS WILL CAUSE A DESYNC BETWEEN THE DOWNLOAD AGENT REGISTRY AND THE QUEUE |
|
|
||||||
| WHICH HAS NOT BEEN ACCOUNTED FOR |
|
|
||||||
+----------------------------------------------------------------------------+
|
|
||||||
|
|
||||||
This download queue does not actually own any of the GameDownloadAgents. It is
|
|
||||||
simply a id-based reference system. The actual Agents are stored in the
|
|
||||||
download_agent_registry HashMap, as ordering is no issue here. This is why
|
|
||||||
appending or removing from the download_queue must be done via signals.
|
|
||||||
|
|
||||||
Behold, my madness - quexeky
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub struct DownloadManagerBuilder {
|
|
||||||
download_agent_registry: HashMap<String, Arc<GameDownloadAgent>>,
|
|
||||||
download_queue: Queue,
|
|
||||||
command_receiver: Receiver<DownloadManagerSignal>,
|
|
||||||
sender: Sender<DownloadManagerSignal>,
|
|
||||||
progress: Arc<Mutex<Option<ProgressObject>>>,
|
|
||||||
status: Arc<Mutex<DownloadManagerStatus>>,
|
|
||||||
|
|
||||||
current_game_interface: Option<Arc<AgentInterfaceData>>, // Should be the only game download agent in the map with the "Go" flag
|
|
||||||
active_control_flag: Option<DownloadThreadControl>,
|
|
||||||
}
|
|
||||||
pub enum DownloadManagerSignal {
|
pub enum DownloadManagerSignal {
|
||||||
/// Resumes (or starts) the DownloadManager
|
/// Resumes (or starts) the DownloadManager
|
||||||
Go,
|
Go,
|
||||||
@ -86,7 +37,7 @@ pub enum DownloadManagerStatus {
|
|||||||
Downloading,
|
Downloading,
|
||||||
Paused,
|
Paused,
|
||||||
Empty,
|
Empty,
|
||||||
Error,
|
Error(GameDownloadError),
|
||||||
}
|
}
|
||||||
pub enum GameDownloadStatus {
|
pub enum GameDownloadStatus {
|
||||||
Downloading,
|
Downloading,
|
||||||
@ -95,174 +46,120 @@ pub enum GameDownloadStatus {
|
|||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DownloadManagerBuilder {
|
/// Accessible front-end for the DownloadManager
|
||||||
pub fn build() -> DownloadManager {
|
///
|
||||||
let queue = Queue::new();
|
/// The system works entirely through signals, both internally and externally,
|
||||||
let (command_sender, command_receiver) = channel();
|
/// all of which are accessible through the DownloadManagerSignal type, but
|
||||||
let active_progress = Arc::new(Mutex::new(None));
|
/// should not be used directly. Rather, signals are abstracted through this
|
||||||
let status = Arc::new(Mutex::new(DownloadManagerStatus::Empty));
|
/// interface.
|
||||||
|
///
|
||||||
let manager = Self {
|
/// The actual download queue may be accessed through the .edit() function,
|
||||||
download_agent_registry: HashMap::new(),
|
/// which provides raw access to the underlying queue.
|
||||||
download_queue: queue.clone(),
|
/// THIS EDITING IS BLOCKING!!!
|
||||||
command_receiver,
|
pub struct DownloadManager {
|
||||||
current_game_interface: None,
|
terminator: JoinHandle<Result<(), ()>>,
|
||||||
active_control_flag: None,
|
download_queue: Queue,
|
||||||
status: status.clone(),
|
progress: Arc<Mutex<Option<ProgressObject>>>,
|
||||||
sender: command_sender.clone(),
|
command_sender: Sender<DownloadManagerSignal>,
|
||||||
progress: active_progress.clone(),
|
}
|
||||||
};
|
pub struct AgentInterfaceData {
|
||||||
|
pub id: String,
|
||||||
let terminator = spawn(|| manager.manage_queue());
|
pub status: Mutex<GameDownloadStatus>,
|
||||||
|
}
|
||||||
DownloadManager::new(terminator, queue, active_progress, command_sender)
|
impl From<Arc<GameDownloadAgent>> for AgentInterfaceData {
|
||||||
}
|
fn from(value: Arc<GameDownloadAgent>) -> Self {
|
||||||
|
Self {
|
||||||
fn manage_queue(mut self) -> Result<(), ()> {
|
id: value.id.clone(),
|
||||||
loop {
|
status: Mutex::from(GameDownloadStatus::Uninitialised),
|
||||||
let signal = match self.command_receiver.recv() {
|
|
||||||
Ok(signal) => signal,
|
|
||||||
Err(e) => return Err(()),
|
|
||||||
};
|
|
||||||
|
|
||||||
match signal {
|
|
||||||
DownloadManagerSignal::Go => {
|
|
||||||
self.manage_go_signal();
|
|
||||||
}
|
|
||||||
DownloadManagerSignal::Stop => {
|
|
||||||
self.manage_stop_signal();
|
|
||||||
}
|
|
||||||
DownloadManagerSignal::Completed(game_id) => {
|
|
||||||
self.manage_completed_signal(game_id);
|
|
||||||
}
|
|
||||||
DownloadManagerSignal::Queue(game_id, version, target_download_dir) => {
|
|
||||||
self.manage_queue_signal(game_id, version, target_download_dir);
|
|
||||||
}
|
|
||||||
DownloadManagerSignal::Finish => {
|
|
||||||
if let Some(active_control_flag) = self.active_control_flag {
|
|
||||||
active_control_flag.set(DownloadThreadControlFlag::Stop)
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
DownloadManagerSignal::Error(e) => {
|
|
||||||
self.manage_error_signal(e);
|
|
||||||
},
|
|
||||||
DownloadManagerSignal::Cancel(id) => {
|
|
||||||
self.manage_cancel_signal(id);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn manage_stop_signal(&mut self) {
|
|
||||||
info!("Got signal 'Stop'");
|
impl DownloadManager {
|
||||||
if let Some(active_control_flag) = self.active_control_flag.clone() {
|
pub fn new(
|
||||||
active_control_flag.set(DownloadThreadControlFlag::Stop);
|
terminator: JoinHandle<Result<(), ()>>,
|
||||||
}
|
download_queue: Queue,
|
||||||
}
|
progress: Arc<Mutex<Option<ProgressObject>>>,
|
||||||
|
command_sender: Sender<DownloadManagerSignal>,
|
||||||
fn manage_completed_signal(&mut self, game_id: String) {
|
) -> Self {
|
||||||
info!("Got signal 'Completed'");
|
Self {
|
||||||
if let Some(interface) = &self.current_game_interface {
|
terminator,
|
||||||
// When if let chains are stabilised, combine these two statements
|
download_queue,
|
||||||
if interface.id == game_id {
|
progress,
|
||||||
info!("Popping consumed data");
|
command_sender,
|
||||||
self.download_queue.pop_front();
|
}
|
||||||
self.download_agent_registry.remove(&game_id);
|
}
|
||||||
self.active_control_flag = None;
|
|
||||||
*self.progress.lock().unwrap() = None;
|
pub fn queue_game(
|
||||||
}
|
&self,
|
||||||
}
|
id: String,
|
||||||
self.sender.send(DownloadManagerSignal::Go).unwrap();
|
version: String,
|
||||||
}
|
target_download_dir: usize,
|
||||||
|
) -> Result<(), SendError<DownloadManagerSignal>> {
|
||||||
fn manage_queue_signal(&mut self, id: String, version: String, target_download_dir: usize) {
|
info!("Adding game id {}", id);
|
||||||
info!("Got signal Queue");
|
self.command_sender.send(DownloadManagerSignal::Queue(
|
||||||
let download_agent = Arc::new(GameDownloadAgent::new(
|
id,
|
||||||
id.clone(),
|
version,
|
||||||
version,
|
target_download_dir,
|
||||||
target_download_dir,
|
))?;
|
||||||
self.sender.clone()
|
self.command_sender.send(DownloadManagerSignal::Go)
|
||||||
));
|
}
|
||||||
let agent_status = GameDownloadStatus::Uninitialised;
|
pub fn cancel_download(
|
||||||
let interface_data = AgentInterfaceData {
|
&self,
|
||||||
id,
|
game_id: String
|
||||||
status: Mutex::new(agent_status),
|
) {
|
||||||
};
|
self.command_sender.send(DownloadManagerSignal::Cancel(game_id)).unwrap();
|
||||||
self.download_agent_registry
|
}
|
||||||
.insert(interface_data.id.clone(), download_agent);
|
pub fn edit(&self) -> MutexGuard<'_, VecDeque<Arc<AgentInterfaceData>>> {
|
||||||
self.download_queue.append(interface_data);
|
self.download_queue.edit()
|
||||||
}
|
}
|
||||||
|
pub fn read_queue(&self) -> VecDeque<Arc<AgentInterfaceData>> {
|
||||||
fn manage_go_signal(&mut self) {
|
self.download_queue.read()
|
||||||
info!("Got signal 'Go'");
|
}
|
||||||
if self.active_control_flag.is_none() && !self.download_agent_registry.is_empty() {
|
pub fn get_current_game_download_progress(&self) -> Option<f64> {
|
||||||
info!("Starting download agent");
|
let progress_object = (*self.progress.lock().unwrap()).clone()?;
|
||||||
let download_agent = {
|
Some(progress_object.get_progress())
|
||||||
let front = self.download_queue.read().front().unwrap().clone();
|
}
|
||||||
self.download_agent_registry
|
pub fn rearrange_string(&self, id: String, new_index: usize) {
|
||||||
.get(&front.id)
|
let mut queue = self.edit();
|
||||||
.unwrap()
|
let current_index = get_index_from_id(&mut queue, id).unwrap();
|
||||||
.clone()
|
let to_move = queue.remove(current_index).unwrap();
|
||||||
};
|
queue.insert(new_index, to_move);
|
||||||
let download_agent_interface =
|
}
|
||||||
Arc::new(AgentInterfaceData::from(download_agent.clone()));
|
pub fn rearrange(&self, current_index: usize, new_index: usize) {
|
||||||
self.current_game_interface = Some(download_agent_interface);
|
let mut queue = self.edit();
|
||||||
|
let to_move = queue.remove(current_index).unwrap();
|
||||||
let progress_object = download_agent.progress.clone();
|
queue.insert(new_index, to_move);
|
||||||
*self.progress.lock().unwrap() = Some(progress_object);
|
}
|
||||||
|
pub fn remove_from_queue(&self, index: usize) {
|
||||||
let active_control_flag = download_agent.control_flag.clone();
|
self.edit().remove(index);
|
||||||
self.active_control_flag = Some(active_control_flag.clone());
|
}
|
||||||
|
pub fn remove_from_queue_string(&self, id: String) {
|
||||||
let sender = self.sender.clone();
|
let mut queue = self.edit();
|
||||||
|
let current_index = get_index_from_id(&mut queue, id).unwrap();
|
||||||
info!("Spawning download");
|
queue.remove(current_index);
|
||||||
spawn(move || {
|
}
|
||||||
match download_agent.download() {
|
pub fn pause_downloads(&self) -> Result<(), SendError<DownloadManagerSignal>> {
|
||||||
Ok(_) => {
|
self.command_sender.send(DownloadManagerSignal::Stop)
|
||||||
sender.send(DownloadManagerSignal::Completed(download_agent.id.clone())).unwrap();
|
}
|
||||||
},
|
pub fn resume_downloads(&self) -> Result<(), SendError<DownloadManagerSignal>> {
|
||||||
Err(e) => {
|
self.command_sender.send(DownloadManagerSignal::Go)
|
||||||
warn!("Download failed");
|
}
|
||||||
//todo!() // Account for if the setup_download function fails
|
pub fn ensure_terminated(self) -> Result<Result<(), ()>, Box<dyn Any + Send>> {
|
||||||
},
|
self.command_sender
|
||||||
};
|
.send(DownloadManagerSignal::Finish)
|
||||||
});
|
.unwrap();
|
||||||
info!("Finished spawning Download");
|
self.terminator.join()
|
||||||
|
}
|
||||||
active_control_flag.set(DownloadThreadControlFlag::Go);
|
}
|
||||||
self.set_status(DownloadManagerStatus::Downloading);
|
|
||||||
} else if let Some(active_control_flag) = self.active_control_flag.clone() {
|
/// Takes in the locked value from .edit() and attempts to
|
||||||
info!("Restarting current download");
|
/// get the index of whatever game_id is passed in
|
||||||
active_control_flag.set(DownloadThreadControlFlag::Go);
|
fn get_index_from_id(
|
||||||
} else {
|
queue: &mut MutexGuard<'_, VecDeque<Arc<AgentInterfaceData>>>,
|
||||||
info!("Nothing was set");
|
id: String,
|
||||||
}
|
) -> Option<usize> {
|
||||||
}
|
queue
|
||||||
fn manage_error_signal(&self, error: GameDownloadError) {
|
.iter()
|
||||||
let current_status = self.current_game_interface.clone().unwrap();
|
.position(|download_agent| download_agent.id == id)
|
||||||
let mut lock = current_status.status.lock().unwrap();
|
|
||||||
*lock = GameDownloadStatus::Error;
|
|
||||||
self.set_status(DownloadManagerStatus::Error);
|
|
||||||
}
|
|
||||||
fn manage_cancel_signal(&mut self, game_id: String) {
|
|
||||||
if let Some(current_flag) = &self.active_control_flag {
|
|
||||||
current_flag.set(DownloadThreadControlFlag::Stop);
|
|
||||||
self.active_control_flag = None;
|
|
||||||
*self.progress.lock().unwrap() = None;
|
|
||||||
}
|
|
||||||
self.download_agent_registry.remove(&game_id);
|
|
||||||
let mut lock = self.download_queue.edit();
|
|
||||||
let index = match lock.iter().position(|interface| interface.id == game_id) {
|
|
||||||
Some(index) => index,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
lock.remove(index);
|
|
||||||
self.sender.send(DownloadManagerSignal::Go).unwrap();
|
|
||||||
info!("{:?}", self.download_agent_registry.iter().map(|x| x.0.clone()).collect::<String>());
|
|
||||||
}
|
|
||||||
fn set_status(&self, status: DownloadManagerStatus) {
|
|
||||||
*self.status.lock().unwrap() = status;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
252
src-tauri/src/downloads/download_manager_builder.rs
Normal file
252
src-tauri/src/downloads/download_manager_builder.rs
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{
|
||||||
|
mpsc::{channel, Receiver, Sender},
|
||||||
|
Arc, Mutex,
|
||||||
|
},
|
||||||
|
thread::spawn,
|
||||||
|
};
|
||||||
|
|
||||||
|
use log::{error, info, warn};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
download_agent::{GameDownloadAgent, GameDownloadError},
|
||||||
|
download_manager::{
|
||||||
|
AgentInterfaceData, DownloadManager, DownloadManagerSignal, DownloadManagerStatus,
|
||||||
|
GameDownloadStatus,
|
||||||
|
},
|
||||||
|
download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag},
|
||||||
|
progress_object::ProgressObject,
|
||||||
|
queue::Queue,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Welcome to the download manager, the most overengineered, glorious piece of bullshit.
|
||||||
|
|
||||||
|
The download manager takes a queue of game_ids and their associated
|
||||||
|
GameDownloadAgents, and then, one-by-one, executes them. It provides an interface
|
||||||
|
to interact with the currently downloading agent, and manage the queue.
|
||||||
|
|
||||||
|
When the DownloadManager is initialised, it is designed to provide a reference
|
||||||
|
which can be used to provide some instructions (the DownloadManagerInterface),
|
||||||
|
but other than that, it runs without any sort of interruptions.
|
||||||
|
|
||||||
|
It does this by opening up two data structures. Primarily is the command_receiver,
|
||||||
|
and mpsc (multi-channel-single-producer) which allows commands to be sent from
|
||||||
|
the Interface, and queued up for the Manager to process.
|
||||||
|
|
||||||
|
These have been mapped in the DownloadManagerSignal docs.
|
||||||
|
|
||||||
|
The other way to interact with the DownloadManager is via the donwload_queue,
|
||||||
|
which is just a collection of ids which may be rearranged to suit
|
||||||
|
whichever download queue order is required.
|
||||||
|
|
||||||
|
+----------------------------------------------------------------------------+
|
||||||
|
| DO NOT ATTEMPT TO ADD OR REMOVE FROM THE QUEUE WITHOUT USING SIGNALS!! |
|
||||||
|
| THIS WILL CAUSE A DESYNC BETWEEN THE DOWNLOAD AGENT REGISTRY AND THE QUEUE |
|
||||||
|
| WHICH HAS NOT BEEN ACCOUNTED FOR |
|
||||||
|
+----------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
This download queue does not actually own any of the GameDownloadAgents. It is
|
||||||
|
simply a id-based reference system. The actual Agents are stored in the
|
||||||
|
download_agent_registry HashMap, as ordering is no issue here. This is why
|
||||||
|
appending or removing from the download_queue must be done via signals.
|
||||||
|
|
||||||
|
Behold, my madness - quexeky
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub struct DownloadManagerBuilder {
|
||||||
|
download_agent_registry: HashMap<String, Arc<GameDownloadAgent>>,
|
||||||
|
download_queue: Queue,
|
||||||
|
command_receiver: Receiver<DownloadManagerSignal>,
|
||||||
|
sender: Sender<DownloadManagerSignal>,
|
||||||
|
progress: Arc<Mutex<Option<ProgressObject>>>,
|
||||||
|
status: Arc<Mutex<DownloadManagerStatus>>,
|
||||||
|
|
||||||
|
current_game_interface: Option<Arc<AgentInterfaceData>>, // Should be the only game download agent in the map with the "Go" flag
|
||||||
|
active_control_flag: Option<DownloadThreadControl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DownloadManagerBuilder {
|
||||||
|
pub fn build() -> DownloadManager {
|
||||||
|
let queue = Queue::new();
|
||||||
|
let (command_sender, command_receiver) = channel();
|
||||||
|
let active_progress = Arc::new(Mutex::new(None));
|
||||||
|
let status = Arc::new(Mutex::new(DownloadManagerStatus::Empty));
|
||||||
|
|
||||||
|
let manager = Self {
|
||||||
|
download_agent_registry: HashMap::new(),
|
||||||
|
download_queue: queue.clone(),
|
||||||
|
command_receiver,
|
||||||
|
current_game_interface: None,
|
||||||
|
active_control_flag: None,
|
||||||
|
status: status.clone(),
|
||||||
|
sender: command_sender.clone(),
|
||||||
|
progress: active_progress.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let terminator = spawn(|| manager.manage_queue());
|
||||||
|
|
||||||
|
DownloadManager::new(terminator, queue, active_progress, command_sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn manage_queue(mut self) -> Result<(), ()> {
|
||||||
|
loop {
|
||||||
|
let signal = match self.command_receiver.recv() {
|
||||||
|
Ok(signal) => signal,
|
||||||
|
Err(_) => return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match signal {
|
||||||
|
DownloadManagerSignal::Go => {
|
||||||
|
self.manage_go_signal();
|
||||||
|
}
|
||||||
|
DownloadManagerSignal::Stop => {
|
||||||
|
self.manage_stop_signal();
|
||||||
|
}
|
||||||
|
DownloadManagerSignal::Completed(game_id) => {
|
||||||
|
self.manage_completed_signal(game_id);
|
||||||
|
}
|
||||||
|
DownloadManagerSignal::Queue(game_id, version, target_download_dir) => {
|
||||||
|
self.manage_queue_signal(game_id, version, target_download_dir);
|
||||||
|
}
|
||||||
|
DownloadManagerSignal::Finish => {
|
||||||
|
if let Some(active_control_flag) = self.active_control_flag {
|
||||||
|
active_control_flag.set(DownloadThreadControlFlag::Stop)
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
DownloadManagerSignal::Error(e) => {
|
||||||
|
self.manage_error_signal(e);
|
||||||
|
}
|
||||||
|
DownloadManagerSignal::Cancel(id) => {
|
||||||
|
self.manage_cancel_signal(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn manage_stop_signal(&mut self) {
|
||||||
|
info!("Got signal 'Stop'");
|
||||||
|
if let Some(active_control_flag) = self.active_control_flag.clone() {
|
||||||
|
active_control_flag.set(DownloadThreadControlFlag::Stop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn manage_completed_signal(&mut self, game_id: String) {
|
||||||
|
info!("Got signal 'Completed'");
|
||||||
|
if let Some(interface) = &self.current_game_interface {
|
||||||
|
// When if let chains are stabilised, combine these two statements
|
||||||
|
if interface.id == game_id {
|
||||||
|
info!("Popping consumed data");
|
||||||
|
self.download_queue.pop_front();
|
||||||
|
self.download_agent_registry.remove(&game_id);
|
||||||
|
self.active_control_flag = None;
|
||||||
|
*self.progress.lock().unwrap() = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sender.send(DownloadManagerSignal::Go).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn manage_queue_signal(&mut self, id: String, version: String, target_download_dir: usize) {
|
||||||
|
info!("Got signal Queue");
|
||||||
|
let download_agent = Arc::new(GameDownloadAgent::new(
|
||||||
|
id.clone(),
|
||||||
|
version,
|
||||||
|
target_download_dir,
|
||||||
|
self.sender.clone(),
|
||||||
|
));
|
||||||
|
let agent_status = GameDownloadStatus::Uninitialised;
|
||||||
|
let interface_data = AgentInterfaceData {
|
||||||
|
id,
|
||||||
|
status: Mutex::new(agent_status),
|
||||||
|
};
|
||||||
|
self.download_agent_registry
|
||||||
|
.insert(interface_data.id.clone(), download_agent);
|
||||||
|
self.download_queue.append(interface_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn manage_go_signal(&mut self) {
|
||||||
|
info!("Got signal 'Go'");
|
||||||
|
if self.active_control_flag.is_none() && !self.download_agent_registry.is_empty() {
|
||||||
|
info!("Starting download agent");
|
||||||
|
let download_agent = {
|
||||||
|
let front = self.download_queue.read().front().unwrap().clone();
|
||||||
|
self.download_agent_registry.get(&front.id).unwrap().clone()
|
||||||
|
};
|
||||||
|
let download_agent_interface =
|
||||||
|
Arc::new(AgentInterfaceData::from(download_agent.clone()));
|
||||||
|
self.current_game_interface = Some(download_agent_interface);
|
||||||
|
|
||||||
|
let progress_object = download_agent.progress.clone();
|
||||||
|
*self.progress.lock().unwrap() = Some(progress_object);
|
||||||
|
|
||||||
|
let active_control_flag = download_agent.control_flag.clone();
|
||||||
|
self.active_control_flag = Some(active_control_flag.clone());
|
||||||
|
|
||||||
|
let sender = self.sender.clone();
|
||||||
|
|
||||||
|
info!("Spawning download");
|
||||||
|
spawn(move || {
|
||||||
|
match download_agent.download() {
|
||||||
|
Ok(_) => {
|
||||||
|
// TODO wrap this pattern in a macro
|
||||||
|
let result = sender
|
||||||
|
.send(DownloadManagerSignal::Completed(download_agent.id.clone()));
|
||||||
|
if let Err(err) = result {
|
||||||
|
error!("{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let result = sender.send(DownloadManagerSignal::Error(err));
|
||||||
|
if let Err(err) = result {
|
||||||
|
error!("{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
info!("Finished spawning Download");
|
||||||
|
|
||||||
|
active_control_flag.set(DownloadThreadControlFlag::Go);
|
||||||
|
self.set_status(DownloadManagerStatus::Downloading);
|
||||||
|
} else if let Some(active_control_flag) = self.active_control_flag.clone() {
|
||||||
|
info!("Restarting current download");
|
||||||
|
active_control_flag.set(DownloadThreadControlFlag::Go);
|
||||||
|
} else {
|
||||||
|
info!("Nothing was set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn manage_error_signal(&self, error: GameDownloadError) {
|
||||||
|
let current_status = self.current_game_interface.clone().unwrap();
|
||||||
|
let mut lock = current_status.status.lock().unwrap();
|
||||||
|
*lock = GameDownloadStatus::Error;
|
||||||
|
self.set_status(DownloadManagerStatus::Error(error));
|
||||||
|
}
|
||||||
|
fn manage_cancel_signal(&mut self, game_id: String) {
|
||||||
|
if let Some(current_flag) = &self.active_control_flag {
|
||||||
|
current_flag.set(DownloadThreadControlFlag::Stop);
|
||||||
|
self.active_control_flag = None;
|
||||||
|
*self.progress.lock().unwrap() = None;
|
||||||
|
}
|
||||||
|
self.download_agent_registry.remove(&game_id);
|
||||||
|
let mut lock = self.download_queue.edit();
|
||||||
|
let index = match lock.iter().position(|interface| interface.id == game_id) {
|
||||||
|
Some(index) => index,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
lock.remove(index);
|
||||||
|
self.sender.send(DownloadManagerSignal::Go).unwrap();
|
||||||
|
info!(
|
||||||
|
"{:?}",
|
||||||
|
self.download_agent_registry
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.0.clone())
|
||||||
|
.collect::<String>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fn set_status(&self, status: DownloadManagerStatus) {
|
||||||
|
*self.status.lock().unwrap() = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,135 +0,0 @@
|
|||||||
use std::{
|
|
||||||
any::Any,
|
|
||||||
collections::VecDeque,
|
|
||||||
sync::{
|
|
||||||
mpsc::{SendError, Sender},
|
|
||||||
Arc, Mutex, MutexGuard,
|
|
||||||
},
|
|
||||||
thread::JoinHandle,
|
|
||||||
};
|
|
||||||
|
|
||||||
use log::info;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
download_agent::GameDownloadAgent,
|
|
||||||
download_manager::{DownloadManagerSignal, GameDownloadStatus},
|
|
||||||
progress_object::ProgressObject, queue::Queue,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Accessible front-end for the DownloadManager
|
|
||||||
///
|
|
||||||
/// The system works entirely through signals, both internally and externally,
|
|
||||||
/// all of which are accessible through the DownloadManagerSignal type, but
|
|
||||||
/// should not be used directly. Rather, signals are abstracted through this
|
|
||||||
/// interface.
|
|
||||||
///
|
|
||||||
/// The actual download queue may be accessed through the .edit() function,
|
|
||||||
/// which provides raw access to the underlying queue.
|
|
||||||
/// THIS EDITING IS BLOCKING!!!
|
|
||||||
pub struct DownloadManager {
|
|
||||||
terminator: JoinHandle<Result<(), ()>>,
|
|
||||||
download_queue: Queue,
|
|
||||||
progress: Arc<Mutex<Option<ProgressObject>>>,
|
|
||||||
command_sender: Sender<DownloadManagerSignal>,
|
|
||||||
}
|
|
||||||
pub struct AgentInterfaceData {
|
|
||||||
pub id: String,
|
|
||||||
pub status: Mutex<GameDownloadStatus>,
|
|
||||||
}
|
|
||||||
impl From<Arc<GameDownloadAgent>> for AgentInterfaceData {
|
|
||||||
fn from(value: Arc<GameDownloadAgent>) -> Self {
|
|
||||||
Self {
|
|
||||||
id: value.id.clone(),
|
|
||||||
status: Mutex::from(GameDownloadStatus::Uninitialised),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DownloadManager {
|
|
||||||
pub fn new(
|
|
||||||
terminator: JoinHandle<Result<(), ()>>,
|
|
||||||
download_queue: Queue,
|
|
||||||
progress: Arc<Mutex<Option<ProgressObject>>>,
|
|
||||||
command_sender: Sender<DownloadManagerSignal>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
terminator,
|
|
||||||
download_queue,
|
|
||||||
progress,
|
|
||||||
command_sender,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn queue_game(
|
|
||||||
&self,
|
|
||||||
id: String,
|
|
||||||
version: String,
|
|
||||||
target_download_dir: usize,
|
|
||||||
) -> Result<(), SendError<DownloadManagerSignal>> {
|
|
||||||
info!("Adding game id {}", id);
|
|
||||||
self.command_sender.send(DownloadManagerSignal::Queue(
|
|
||||||
id,
|
|
||||||
version,
|
|
||||||
target_download_dir,
|
|
||||||
))?;
|
|
||||||
self.command_sender.send(DownloadManagerSignal::Go)
|
|
||||||
}
|
|
||||||
pub fn cancel_download(
|
|
||||||
&self,
|
|
||||||
game_id: String
|
|
||||||
) {
|
|
||||||
self.command_sender.send(DownloadManagerSignal::Cancel(game_id)).unwrap();
|
|
||||||
}
|
|
||||||
pub fn edit(&self) -> MutexGuard<'_, VecDeque<Arc<AgentInterfaceData>>> {
|
|
||||||
self.download_queue.edit()
|
|
||||||
}
|
|
||||||
pub fn read_queue(&self) -> VecDeque<Arc<AgentInterfaceData>> {
|
|
||||||
self.download_queue.read()
|
|
||||||
}
|
|
||||||
pub fn get_current_game_download_progress(&self) -> Option<f64> {
|
|
||||||
let progress_object = (*self.progress.lock().unwrap()).clone()?;
|
|
||||||
Some(progress_object.get_progress())
|
|
||||||
}
|
|
||||||
pub fn rearrange_string(&self, id: String, new_index: usize) {
|
|
||||||
let mut queue = self.edit();
|
|
||||||
let current_index = get_index_from_id(&mut queue, id).unwrap();
|
|
||||||
let to_move = queue.remove(current_index).unwrap();
|
|
||||||
queue.insert(new_index, to_move);
|
|
||||||
}
|
|
||||||
pub fn rearrange(&self, current_index: usize, new_index: usize) {
|
|
||||||
let mut queue = self.edit();
|
|
||||||
let to_move = queue.remove(current_index).unwrap();
|
|
||||||
queue.insert(new_index, to_move);
|
|
||||||
}
|
|
||||||
pub fn remove_from_queue(&self, index: usize) {
|
|
||||||
self.edit().remove(index);
|
|
||||||
}
|
|
||||||
pub fn remove_from_queue_string(&self, id: String) {
|
|
||||||
let mut queue = self.edit();
|
|
||||||
let current_index = get_index_from_id(&mut queue, id).unwrap();
|
|
||||||
queue.remove(current_index);
|
|
||||||
}
|
|
||||||
pub fn pause_downloads(&self) -> Result<(), SendError<DownloadManagerSignal>> {
|
|
||||||
self.command_sender.send(DownloadManagerSignal::Stop)
|
|
||||||
}
|
|
||||||
pub fn resume_downloads(&self) -> Result<(), SendError<DownloadManagerSignal>> {
|
|
||||||
self.command_sender.send(DownloadManagerSignal::Go)
|
|
||||||
}
|
|
||||||
pub fn ensure_terminated(self) -> Result<Result<(), ()>, Box<dyn Any + Send>> {
|
|
||||||
self.command_sender
|
|
||||||
.send(DownloadManagerSignal::Finish)
|
|
||||||
.unwrap();
|
|
||||||
self.terminator.join()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes in the locked value from .edit() and attempts to
|
|
||||||
/// get the index of whatever game_id is passed in
|
|
||||||
fn get_index_from_id(
|
|
||||||
queue: &mut MutexGuard<'_, VecDeque<Arc<AgentInterfaceData>>>,
|
|
||||||
id: String,
|
|
||||||
) -> Option<usize> {
|
|
||||||
queue
|
|
||||||
.iter()
|
|
||||||
.position(|download_agent| download_agent.id == id)
|
|
||||||
}
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
pub mod download_agent;
|
pub mod download_agent;
|
||||||
pub mod download_commands;
|
pub mod download_commands;
|
||||||
mod download_logic;
|
mod download_logic;
|
||||||
|
pub mod download_manager_builder;
|
||||||
pub mod download_manager;
|
pub mod download_manager;
|
||||||
pub mod download_manager_interface;
|
|
||||||
mod download_thread_control_flag;
|
mod download_thread_control_flag;
|
||||||
mod manifest;
|
mod manifest;
|
||||||
mod progress_object;
|
mod progress_object;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use std::{collections::VecDeque, sync::{Arc, Mutex, MutexGuard}};
|
use std::{collections::VecDeque, sync::{Arc, Mutex, MutexGuard}};
|
||||||
|
|
||||||
use super::download_manager_interface::AgentInterfaceData;
|
use super::download_manager::AgentInterfaceData;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Queue {
|
pub struct Queue {
|
||||||
|
|||||||
@ -10,13 +10,16 @@ mod tests;
|
|||||||
|
|
||||||
use crate::db::DatabaseImpls;
|
use crate::db::DatabaseImpls;
|
||||||
use auth::{auth_initiate, generate_authorization_header, recieve_handshake, retry_connect};
|
use auth::{auth_initiate, generate_authorization_header, recieve_handshake, retry_connect};
|
||||||
use db::{add_download_dir, delete_download_dir, fetch_download_dir_stats, DatabaseInterface, DATA_ROOT_DIR};
|
use db::{
|
||||||
|
add_download_dir, delete_download_dir, fetch_download_dir_stats, DatabaseInterface,
|
||||||
|
DATA_ROOT_DIR,
|
||||||
|
};
|
||||||
use downloads::download_commands::*;
|
use downloads::download_commands::*;
|
||||||
use downloads::download_manager::DownloadManagerBuilder;
|
use downloads::download_manager_builder::DownloadManagerBuilder;
|
||||||
use downloads::download_manager_interface::DownloadManager;
|
use downloads::download_manager::DownloadManager;
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
use http::{header::*, response::Builder as ResponseBuilder};
|
use http::{header::*, response::Builder as ResponseBuilder};
|
||||||
use library::{fetch_game, fetch_library, Game};
|
use library::{fetch_game, fetch_game_status, fetch_library, Game};
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use remote::{gen_drop_url, use_remote, RemoteAccessError};
|
use remote::{gen_drop_url, use_remote, RemoteAccessError};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -130,6 +133,7 @@ pub fn run() {
|
|||||||
add_download_dir,
|
add_download_dir,
|
||||||
delete_download_dir,
|
delete_download_dir,
|
||||||
fetch_download_dir_stats,
|
fetch_download_dir_stats,
|
||||||
|
fetch_game_status,
|
||||||
// Downloads
|
// Downloads
|
||||||
download_game,
|
download_game,
|
||||||
get_current_game_download_progress,
|
get_current_game_download_progress,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tauri::{AppHandle, Manager};
|
use tauri::{AppHandle, Manager};
|
||||||
|
|
||||||
|
use crate::db;
|
||||||
use crate::db::DatabaseGameStatus;
|
use crate::db::DatabaseGameStatus;
|
||||||
use crate::db::DatabaseImpls;
|
use crate::db::DatabaseImpls;
|
||||||
use crate::remote::RemoteAccessError;
|
use crate::remote::RemoteAccessError;
|
||||||
@ -109,3 +110,17 @@ pub fn fetch_game(id: String, app: tauri::AppHandle) -> Result<String, String> {
|
|||||||
|
|
||||||
Ok(result.unwrap())
|
Ok(result.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
|
||||||
|
return Ok(status);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user