diff --git a/desktop/src-tauri/src/downloads/download_files.rs b/desktop/src-tauri/src/downloads/download_files.rs new file mode 100644 index 00000000..ce52b9d1 --- /dev/null +++ b/desktop/src-tauri/src/downloads/download_files.rs @@ -0,0 +1,32 @@ +use crate::auth::generate_authorization_header; +use crate::DB; +use crate::db::DatabaseImpls; +use crate::downloads::manifest::DropDownloadContext; + + +pub fn download_game_chunk(ctx: DropDownloadContext) { + let base_url = DB.fetch_base_url(); + + let index = ctx.index; + let chunk = ctx.file_chunk; + + let client = reqwest::blocking::Client::new(); + let chunk_url = base_url.join( + &format!( + "/api/v1/client/user/library?gameId={}&versionName={}&filename={}&chunkIndex={}", + chunk.ids[index], + ctx.version, + ctx.file_name, + index + )).unwrap(); + + let header = generate_authorization_header(); + + let response = client + .get(chunk_url) + .header("Authorization", header) + .send() + .unwrap(); + println!("{}", response.text().unwrap()); + // Need to implement actual download logic +} \ No newline at end of file diff --git a/desktop/src-tauri/src/downloads/downloads.rs b/desktop/src-tauri/src/downloads/downloads.rs index 87781c7e..01cd219a 100644 --- a/desktop/src-tauri/src/downloads/downloads.rs +++ b/desktop/src-tauri/src/downloads/downloads.rs @@ -12,10 +12,4 @@ use crate::auth::generate_authorization_header; use crate::DB; use crate::db::DatabaseImpls; -#[tauri::command] -fn download_game(app: AppHandle, game_id: String) -> Result{ - todo!() -} - - diff --git a/desktop/src-tauri/src/downloads/game_download.rs b/desktop/src-tauri/src/downloads/game_download.rs index 867017d0..8af0a2b9 100644 --- a/desktop/src-tauri/src/downloads/game_download.rs +++ b/desktop/src-tauri/src/downloads/game_download.rs @@ -8,17 +8,18 @@ use versions::Version; use crate::{AppState, DB}; use crate::auth::generate_authorization_header; use crate::db::DatabaseImpls; -use crate::downloads::manifest::DropManifest; +use crate::downloads::download_files; +use crate::downloads::manifest::{DropDownloadContext, DropManifest}; use crate::downloads::progress::ProgressChecker; #[derive(Serialize, Deserialize)] -#[serde(rename_all="camelCase")] +#[serde(rename_all = "camelCase")] pub struct GameDownload { id: String, - version: Version, + version: String, progress: Arc, state: Mutex, - manifest: Option> + pub manifest: Mutex>, } #[derive(Serialize, Deserialize, Clone, Eq, PartialEq)] pub enum GameDownloadState { @@ -29,56 +30,57 @@ pub enum GameDownloadState { Finished, Stalled, Failed, - Cancelled + Cancelled, } -#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] pub enum GameDownloadError { - ManifestAlreadyExists, - ManifestDoesNotExist, ManifestDownloadError, - StatusError(u16) + StatusError(u16), + SystemError(SystemError), +} +#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)] +pub enum SystemError { + MutexLockFailed } #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Ord, PartialOrd)] -#[serde(rename_all="camelCase")] +#[serde(rename_all = "camelCase")] pub struct GameChunkCtx { chunk_id: usize, } - -#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)] -pub struct GameDownloadManifest { - // TODO: Implement game manifest -} - impl GameDownload { - pub fn new(id: String, version: Version) -> Self { + pub fn new(id: String, version: String) -> Self { Self { id, version, progress: Arc::new(AtomicUsize::new(0)), state: Mutex::from(GameDownloadState::Uninitialised), - manifest: None + manifest: Mutex::new(None), } } pub async fn queue(&self) -> Result<(), GameDownloadError> { self.change_state(GameDownloadState::Queued); - if self.manifest.is_none() { - return Ok(()) + if self.manifest.lock().unwrap().is_none() { + return Ok(()); } - self.download_manifest().await + self.ensure_manifest_exists().await } - pub async fn download(&self, max_threads: usize, contexts: Vec) -> Result<(), GameDownloadError> { + pub async fn download(&self, max_threads: usize, contexts: Vec) -> Result<(), GameDownloadError> { let progress = Arc::new(AtomicUsize::new(0)); self.change_state(GameDownloadState::Downloading); - let progress = ProgressChecker::new(Box::new(download_game_chunk), progress); + let progress = ProgressChecker::new(Box::new(download_files::download_game_chunk), progress); progress.run_contexts_parallel_async(contexts, max_threads).await; Ok(()) } - pub async fn download_manifest(&self) -> Result<(), GameDownloadError> { - if self.manifest.is_some() { - return Err(GameDownloadError::ManifestAlreadyExists); + pub async fn ensure_manifest_exists(&self) -> Result<(), GameDownloadError> { + if self.manifest.lock().unwrap().is_some() { + return Ok(()); } + self.download_manifest().await + } + + async fn download_manifest(&self) -> Result<(), GameDownloadError> { let base_url = DB.fetch_base_url(); let manifest_url = base_url .join( @@ -87,7 +89,7 @@ impl GameDownload { self.id, self.version.to_string() ) - .as_str() + .as_str() ) .unwrap(); @@ -107,25 +109,39 @@ impl GameDownload { return Err(GameDownloadError::StatusError(response.status().as_u16())); } - //info!("{}", response.text().await.unwrap()); - - info!("Parsed manifest: {:?}", response.json::().await.unwrap()); + let manifest_download = response.json::().await.unwrap(); + if let Ok(mut manifest) = self.manifest.lock() { + *manifest = Some(manifest_download) + } else { return Err(GameDownloadError::SystemError(SystemError::MutexLockFailed)); } Ok(()) } + pub fn change_state(&self, state: GameDownloadState) { let mut lock = self.state.lock().unwrap(); *lock = state; } } -impl GameDownloadManifest { - fn parse_to_chunks(&self) -> Vec { - todo!() +pub fn to_contexts(manifest: &DropManifest, version: String) -> Vec { + let mut contexts = Vec::new(); + let mut counter = 0; + let mut prev_key: String = String::new(); + for key in manifest { + contexts.push(DropDownloadContext { + file_chunk: Arc::new(key.1.clone()), + + file_name: key.0.clone(), + version: version.to_string(), + index: counter, + }); + if prev_key == *key.0 { + counter += 1; + } else { + prev_key = key.0.clone(); + counter = 0; + } } -} -fn download_game_chunk(ctx: GameChunkCtx) { - todo!(); - // Need to implement actual download logic + contexts } #[tauri::command] @@ -135,16 +151,23 @@ pub async fn start_game_download( max_threads: usize, state: tauri::State<'_, Mutex>, ) -> Result<(), GameDownloadError> { - info!("Triggered Game Download"); - let mut download = Arc::new(GameDownload::new(game_id, Version::from_str(&*game_version).unwrap())); - //let mut app_state = state.lock().unwrap(); + let mut download = Arc::new(GameDownload::new(game_id, game_version.clone())); - let tmp = download.clone(); - //let manifest = &tmp.manifest; - download.download_manifest().await + download.ensure_manifest_exists().await?; + + + let manifest = download.manifest.lock().unwrap(); + let local_manifest = (*manifest).clone().unwrap(); + + + let contexts = to_contexts(&local_manifest, game_version.clone()); + + let _ = download.download(max_threads, contexts).await; + + Ok(()) /* let Some(unlocked) = manifest else { return Err(GameDownloadError::ManifestDoesNotExist) }; diff --git a/desktop/src-tauri/src/downloads/manifest.rs b/desktop/src-tauri/src/downloads/manifest.rs index eb7a498e..362dc5a1 100644 --- a/desktop/src-tauri/src/downloads/manifest.rs +++ b/desktop/src-tauri/src/downloads/manifest.rs @@ -1,11 +1,20 @@ use std::collections::HashMap; +use std::sync::Arc; use serde::{Deserialize, Serialize}; pub type DropManifest = HashMap; #[derive(Serialize, Deserialize, Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct DropChunk { - permissions: usize, - ids: Vec, - checksums: Vec, - lengths: Vec + pub permissions: usize, + pub ids: Vec, + pub checksums: Vec, + pub lengths: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct DropDownloadContext { + pub file_chunk: Arc, + pub file_name: String, + pub version: String, + pub index: usize } \ No newline at end of file diff --git a/desktop/src-tauri/src/downloads/mod.rs b/desktop/src-tauri/src/downloads/mod.rs index 44fba89f..907902b2 100644 --- a/desktop/src-tauri/src/downloads/mod.rs +++ b/desktop/src-tauri/src/downloads/mod.rs @@ -1,4 +1,5 @@ mod downloads; mod manifest; pub mod progress; -pub mod game_download; \ No newline at end of file +pub mod game_download; +mod download_files; \ No newline at end of file diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index b4043c96..9759fde6 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -110,8 +110,6 @@ pub fn run() { // Library fetch_library, fetch_game, - // Downloads - start_game_download ]) .plugin(tauri_plugin_shell::init()) .setup(|app| {