mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-13 08:12:44 +10:00
restructing and renaming
This commit is contained in:
@ -11,15 +11,13 @@ use std::fs::{create_dir_all, File};
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GameDownloadManager {
|
||||
pub struct GameDownloadAgent {
|
||||
id: String,
|
||||
version: String,
|
||||
progress: Arc<AtomicUsize>,
|
||||
state: Mutex<GameDownloadState>,
|
||||
contexts: Mutex<Vec<DropDownloadContext>>,
|
||||
progress: ProgressChecker<DropDownloadContext>,
|
||||
pub manifest: Mutex<Option<DropManifest>>,
|
||||
}
|
||||
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||
@ -37,6 +35,7 @@ pub enum GameDownloadState {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum GameDownloadError {
|
||||
ManifestDownload,
|
||||
FailedContextGeneration,
|
||||
Status(u16),
|
||||
System(SystemError),
|
||||
}
|
||||
@ -49,14 +48,18 @@ pub enum SystemError {
|
||||
pub struct GameChunkCtx {
|
||||
chunk_id: usize,
|
||||
}
|
||||
impl GameDownloadManager {
|
||||
impl GameDownloadAgent {
|
||||
pub fn new(id: String, version: String) -> Self {
|
||||
Self {
|
||||
id,
|
||||
version,
|
||||
progress: Arc::new(AtomicUsize::new(0)),
|
||||
state: Mutex::from(GameDownloadState::Uninitialised),
|
||||
manifest: Mutex::new(None),
|
||||
progress: ProgressChecker::new(
|
||||
Box::new(download_logic::download_game_chunk),
|
||||
Arc::new(AtomicUsize::new(0)),
|
||||
),
|
||||
contexts: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
pub async fn queue(&self) -> Result<(), GameDownloadError> {
|
||||
@ -67,16 +70,12 @@ impl GameDownloadManager {
|
||||
self.ensure_manifest_exists().await
|
||||
}
|
||||
|
||||
pub fn begin_download(
|
||||
&self,
|
||||
max_threads: usize,
|
||||
contexts: Vec<DropDownloadContext>,
|
||||
) -> Result<(), GameDownloadError> {
|
||||
let progress = Arc::new(AtomicUsize::new(0));
|
||||
pub fn begin_download(&self, max_threads: usize) -> Result<(), GameDownloadError> {
|
||||
self.change_state(GameDownloadState::Downloading);
|
||||
let progress =
|
||||
ProgressChecker::new(Box::new(download_logic::download_game_chunk), progress);
|
||||
progress.run_contexts_parallel(contexts, max_threads);
|
||||
// TODO we're coping the whole context thing
|
||||
// It's not necessary, I just can't figure out to make the borrow checker happy
|
||||
self.progress
|
||||
.run_contexts_parallel(self.contexts.lock().unwrap().to_vec(), max_threads);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -130,41 +129,51 @@ impl GameDownloadManager {
|
||||
let mut lock = self.state.lock().unwrap();
|
||||
*lock = state;
|
||||
}
|
||||
}
|
||||
pub fn generate_job_contexts(
|
||||
manifest: &DropManifest,
|
||||
version: String,
|
||||
game_id: String,
|
||||
) -> Vec<DropDownloadContext> {
|
||||
let mut contexts = Vec::new();
|
||||
let base_path = DATA_ROOT_DIR.join("games").join(game_id.clone()).clone();
|
||||
create_dir_all(base_path.clone()).unwrap();
|
||||
info!("Generating contexts");
|
||||
for (raw_path, chunk) in manifest {
|
||||
let path = base_path.join(Path::new(raw_path));
|
||||
|
||||
let container = path.parent().unwrap();
|
||||
create_dir_all(container).unwrap();
|
||||
pub fn generate_job_contexts(
|
||||
&self,
|
||||
manifest: &DropManifest,
|
||||
version: String,
|
||||
game_id: String,
|
||||
) -> Result<(), GameDownloadError> {
|
||||
let mut contexts = Vec::new();
|
||||
let base_path = DATA_ROOT_DIR.join("games").join(game_id.clone()).clone();
|
||||
create_dir_all(base_path.clone()).unwrap();
|
||||
info!("Generating contexts");
|
||||
for (raw_path, chunk) in manifest {
|
||||
let path = base_path.join(Path::new(raw_path));
|
||||
|
||||
let file = File::create(path.clone()).unwrap();
|
||||
let mut running_offset = 0;
|
||||
let container = path.parent().unwrap();
|
||||
create_dir_all(container).unwrap();
|
||||
|
||||
for (i, length) in chunk.lengths.iter().enumerate() {
|
||||
contexts.push(DropDownloadContext {
|
||||
file_name: raw_path.to_string(),
|
||||
version: version.to_string(),
|
||||
offset: running_offset,
|
||||
index: i,
|
||||
game_id: game_id.to_string(),
|
||||
path: path.clone(),
|
||||
});
|
||||
running_offset += *length as u64;
|
||||
let file = File::create(path.clone()).unwrap();
|
||||
let mut running_offset = 0;
|
||||
|
||||
for (i, length) in chunk.lengths.iter().enumerate() {
|
||||
contexts.push(DropDownloadContext {
|
||||
file_name: raw_path.to_string(),
|
||||
version: version.to_string(),
|
||||
offset: running_offset,
|
||||
index: i,
|
||||
game_id: game_id.to_string(),
|
||||
path: path.clone(),
|
||||
});
|
||||
running_offset += *length as u64;
|
||||
}
|
||||
|
||||
if running_offset > 0 {
|
||||
fallocate(file, FallocateFlags::empty(), 0, running_offset).unwrap();
|
||||
}
|
||||
}
|
||||
info!("Finished generating");
|
||||
if let Ok(mut context_lock) = self.contexts.lock() {
|
||||
*context_lock = contexts;
|
||||
} else {
|
||||
return Err(GameDownloadError::FailedContextGeneration);
|
||||
}
|
||||
|
||||
fallocate(file, FallocateFlags::empty(), 0, running_offset).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
info!("Finished generating");
|
||||
contexts
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@ -176,22 +185,18 @@ pub async fn start_game_download(
|
||||
) -> Result<(), GameDownloadError> {
|
||||
info!("Triggered Game Download");
|
||||
|
||||
let download_manager = Arc::new(GameDownloadManager::new(
|
||||
game_id.clone(),
|
||||
game_version.clone(),
|
||||
));
|
||||
let download_agent = GameDownloadAgent::new(game_id.clone(), game_version.clone());
|
||||
|
||||
download_manager.ensure_manifest_exists().await?;
|
||||
download_agent.ensure_manifest_exists().await?;
|
||||
|
||||
let local_manifest = {
|
||||
let manifest = download_manager.manifest.lock().unwrap();
|
||||
let manifest = download_agent.manifest.lock().unwrap();
|
||||
(*manifest).clone().unwrap()
|
||||
};
|
||||
|
||||
let contexts = generate_job_contexts(&local_manifest, game_version.clone(), game_id);
|
||||
download_agent.generate_job_contexts(&local_manifest, game_version.clone(), game_id).unwrap();
|
||||
|
||||
download_manager
|
||||
.begin_download(max_threads, contexts)?;
|
||||
download_agent.begin_download(max_threads)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub type DropManifest = HashMap<String, DropChunk>;
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
@ -13,12 +13,12 @@ pub struct DropChunk {
|
||||
pub lengths: Vec<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct DropDownloadContext {
|
||||
pub file_name: String,
|
||||
pub version: String,
|
||||
pub index: usize,
|
||||
pub offset: u64,
|
||||
pub game_id: String,
|
||||
pub path: PathBuf
|
||||
}
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
mod manifest;
|
||||
pub mod progress;
|
||||
pub mod download_manager;
|
||||
pub mod download_agent;
|
||||
mod download_logic;
|
||||
@ -1,21 +1,26 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct ProgressChecker<T>
|
||||
where T: 'static + Send + Sync
|
||||
where
|
||||
T: 'static + Send + Sync,
|
||||
{
|
||||
counter: Arc<AtomicUsize>,
|
||||
f: Arc<Box<dyn Fn(T) + Send + Sync + 'static>>,
|
||||
}
|
||||
|
||||
impl<T> ProgressChecker<T>
|
||||
where T: Send + Sync
|
||||
where
|
||||
T: Send + Sync,
|
||||
{
|
||||
pub fn new(f: Box<dyn Fn(T) + Send + Sync + 'static>, counter_reference: Arc<AtomicUsize>) -> Self {
|
||||
pub fn new(
|
||||
f: Box<dyn Fn(T) + Send + Sync + 'static>,
|
||||
counter_reference: Arc<AtomicUsize>,
|
||||
) -> Self {
|
||||
Self {
|
||||
f: f.into(),
|
||||
counter: counter_reference
|
||||
counter: counter_reference,
|
||||
}
|
||||
}
|
||||
pub async fn run_contexts_sequentially_async(&self, contexts: Vec<T>) {
|
||||
@ -50,4 +55,4 @@ where T: Send + Sync
|
||||
pub fn get_progress_percentage<C: Into<f64>>(&self, capacity: C) -> f64 {
|
||||
(self.get_progress() as f64) / (capacity.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ use std::{
|
||||
use std::sync::Arc;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use crate::db::DatabaseImpls;
|
||||
use crate::downloads::download_manager::{start_game_download, GameDownloadManager};
|
||||
use crate::downloads::download_agent::{start_game_download, GameDownloadAgent};
|
||||
|
||||
#[derive(Clone, Copy, Serialize)]
|
||||
pub enum AppStatus {
|
||||
@ -47,7 +47,9 @@ pub struct AppState {
|
||||
status: AppStatus,
|
||||
user: Option<User>,
|
||||
games: HashMap<String, Game>,
|
||||
game_downloads: Vec<Arc<GameDownloadManager>>
|
||||
|
||||
#[serde(skip_serializing)]
|
||||
game_downloads: Vec<Arc<GameDownloadAgent>>
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
Reference in New Issue
Block a user