restructing and renaming

This commit is contained in:
DecDuck
2024-10-25 17:41:03 +11:00
parent a628fc1417
commit 7049673596
5 changed files with 80 additions and 68 deletions

View File

@ -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(())
}

View File

@ -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,
}

View File

@ -1,4 +1,4 @@
mod manifest;
pub mod progress;
pub mod download_manager;
pub mod download_agent;
mod download_logic;

View File

@ -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())
}
}
}

View File

@ -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]