mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-13 16:22:43 +10:00
feat(download manager): Added generic download manager
Signed-off-by: quexeky <git@quexeky.dev>
This commit is contained in:
@ -1,7 +1,8 @@
|
|||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
|
import type { DownloadableMetadata } from "~/types";
|
||||||
|
|
||||||
export type QueueState = {
|
export type QueueState = {
|
||||||
queue: Array<{ id: string; status: string; progress: number | null }>;
|
queue: Array<{ meta: DownloadableMetadata; status: string; progress: number | null }>;
|
||||||
status: string;
|
status: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export type SerializedGameStatus = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const parseStatus = (status: SerializedGameStatus): GameStatus => {
|
export const parseStatus = (status: SerializedGameStatus): GameStatus => {
|
||||||
|
console.log(status);
|
||||||
if (status[0]) {
|
if (status[0]) {
|
||||||
return {
|
return {
|
||||||
type: status[0].type,
|
type: status[0].type,
|
||||||
@ -28,28 +29,29 @@ export const parseStatus = (status: SerializedGameStatus): GameStatus => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGame = async (id: string) => {
|
export const useGame = async (gameId: string) => {
|
||||||
if (!gameRegistry[id]) {
|
if (!gameRegistry[gameId]) {
|
||||||
const data: { game: Game; status: SerializedGameStatus } = await invoke(
|
const data: { game: Game; status: SerializedGameStatus } = await invoke(
|
||||||
"fetch_game",
|
"fetch_game",
|
||||||
{
|
{
|
||||||
id,
|
gameId,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
gameRegistry[id] = data.game;
|
gameRegistry[gameId] = data.game;
|
||||||
if (!gameStatusRegistry[id]) {
|
if (!gameStatusRegistry[gameId]) {
|
||||||
gameStatusRegistry[id] = ref(parseStatus(data.status));
|
gameStatusRegistry[gameId] = ref(parseStatus(data.status));
|
||||||
|
|
||||||
listen(`update_game/${id}`, (event) => {
|
listen(`update_game/${gameId}`, (event) => {
|
||||||
const payload: {
|
const payload: {
|
||||||
status: SerializedGameStatus;
|
status: SerializedGameStatus;
|
||||||
} = event.payload as any;
|
} = event.payload as any;
|
||||||
gameStatusRegistry[id].value = parseStatus(payload.status);
|
console.log(payload.status);
|
||||||
|
gameStatusRegistry[gameId].value = parseStatus(payload.status);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const game = gameRegistry[id];
|
const game = gameRegistry[gameId];
|
||||||
const status = gameStatusRegistry[id];
|
const status = gameStatusRegistry[gameId];
|
||||||
return { game, status };
|
return { game, status };
|
||||||
};
|
};
|
||||||
@ -14,19 +14,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<draggable v-model="queue.queue" @end="onEnd">
|
<draggable v-model="queue.queue" @end="onEnd">
|
||||||
<template #item="{ element }: { element: (typeof queue.value.queue)[0] }">
|
<template #item="{ element }: { element: (typeof queue.value.queue)[0] }">
|
||||||
<li v-if="games[element.id]" :key="element.id"
|
<li v-if="games[element.meta.id]" :key="element.meta.id"
|
||||||
class="mb-4 bg-zinc-900 rounded-lg flex flex-row justify-between gap-x-6 py-5 px-4">
|
class="mb-4 bg-zinc-900 rounded-lg flex flex-row justify-between gap-x-6 py-5 px-4">
|
||||||
<div class="w-full flex items-center max-w-md gap-x-4 relative">
|
<div class="w-full flex items-center max-w-md gap-x-4 relative">
|
||||||
<img class="size-24 flex-none bg-zinc-800 object-cover rounded" :src="games[element.id].cover" alt="" />
|
<img class="size-24 flex-none bg-zinc-800 object-cover rounded" :src="games[element.meta.id].cover" alt="" />
|
||||||
<div class="min-w-0 flex-auto">
|
<div class="min-w-0 flex-auto">
|
||||||
<p class="text-xl font-semibold text-zinc-100">
|
<p class="text-xl font-semibold text-zinc-100">
|
||||||
<NuxtLink :href="`/library/${element.id}`" class="">
|
<NuxtLink :href="`/library/${element.meta.id}`" class="">
|
||||||
<span class="absolute inset-x-0 -top-px bottom-0" />
|
<span class="absolute inset-x-0 -top-px bottom-0" />
|
||||||
{{ games[element.id].game.mName }}
|
{{ games[element.meta.id].game.mName }}
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 flex text-xs/5 text-gray-500">
|
<p class="mt-1 flex text-xs/5 text-gray-500">
|
||||||
{{ games[element.id].game.mShortDescription }}
|
{{ games[element.meta.id].game.mShortDescription }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<div class="h-2 bg-blue-600" :style="{ width: `${element.progress * 100}%` }" />
|
<div class="h-2 bg-blue-600" :style="{ width: `${element.progress * 100}%` }" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button @click="() => cancelGame(element.id)" class="group">
|
<button @click="() => cancelGame(element.meta)" class="group">
|
||||||
<XMarkIcon class="transition size-8 flex-none text-zinc-600 group-hover:text-zinc-300"
|
<XMarkIcon class="transition size-8 flex-none text-zinc-600 group-hover:text-zinc-300"
|
||||||
aria-hidden="true" />
|
aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
@ -57,7 +57,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { XMarkIcon } from "@heroicons/vue/20/solid";
|
import { XMarkIcon } from "@heroicons/vue/20/solid";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import type { Game, GameStatus } from "~/types";
|
import type { DownloadableMetadata, Game, GameStatus } from "~/types";
|
||||||
|
|
||||||
const windowWidth = ref(window.innerWidth);
|
const windowWidth = ref(window.innerWidth);
|
||||||
window.addEventListener('resize', (event) => {
|
window.addEventListener('resize', (event) => {
|
||||||
@ -81,7 +81,7 @@ function resetHistoryGraph() {
|
|||||||
stats.value = { time: 0, speed: 0 };
|
stats.value = { time: 0, speed: 0 };
|
||||||
}
|
}
|
||||||
function checkReset(v: QueueState) {
|
function checkReset(v: QueueState) {
|
||||||
const currentGame = v.queue.at(0);
|
const currentGame = v.queue.at(0)?.meta.id;
|
||||||
// If we're finished
|
// If we're finished
|
||||||
if (!currentGame && previousGameId.value) {
|
if (!currentGame && previousGameId.value) {
|
||||||
previousGameId.value = undefined;
|
previousGameId.value = undefined;
|
||||||
@ -92,14 +92,14 @@ function checkReset(v: QueueState) {
|
|||||||
if (!currentGame) return;
|
if (!currentGame) return;
|
||||||
// If we started a new download
|
// If we started a new download
|
||||||
if (currentGame && !previousGameId.value) {
|
if (currentGame && !previousGameId.value) {
|
||||||
previousGameId.value = currentGame.id;
|
previousGameId.value = currentGame;
|
||||||
resetHistoryGraph();
|
resetHistoryGraph();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If it's a different game now
|
// If it's a different game now
|
||||||
if (currentGame.id != previousGameId.value
|
if (currentGame != previousGameId.value
|
||||||
) {
|
) {
|
||||||
previousGameId.value = currentGame.id;
|
previousGameId.value = currentGame;
|
||||||
resetHistoryGraph();
|
resetHistoryGraph();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ watch(stats, (v) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function loadGamesForQueue(v: typeof queue.value) {
|
function loadGamesForQueue(v: typeof queue.value) {
|
||||||
for (const { id } of v.queue) {
|
for (const { meta: { id } } of v.queue) {
|
||||||
if (games.value[id]) return;
|
if (games.value[id]) return;
|
||||||
(async () => {
|
(async () => {
|
||||||
const gameData = await useGame(id);
|
const gameData = await useGame(id);
|
||||||
@ -137,8 +137,8 @@ async function onEnd(event: { oldIndex: number; newIndex: number }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cancelGame(id: string) {
|
async function cancelGame(meta: DownloadableMetadata) {
|
||||||
await invoke("cancel_game", { gameId: id });
|
await invoke("cancel_game", { meta });
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatKilobytes(bytes: number): string {
|
function formatKilobytes(bytes: number): string {
|
||||||
|
|||||||
9
src-tauri/Cargo.lock
generated
9
src-tauri/Cargo.lock
generated
@ -1018,6 +1018,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde-binary",
|
"serde-binary",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_with",
|
||||||
"shared_child",
|
"shared_child",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
@ -3856,9 +3857,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with"
|
name = "serde_with"
|
||||||
version = "3.11.0"
|
version = "3.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817"
|
checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -3874,9 +3875,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with_macros"
|
name = "serde_with_macros"
|
||||||
version = "3.11.0"
|
version = "3.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d"
|
checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|||||||
@ -45,6 +45,7 @@ boxcar = "0.2.7"
|
|||||||
umu-wrapper-lib = "0.1.0"
|
umu-wrapper-lib = "0.1.0"
|
||||||
tauri-plugin-autostart = "2.0.0"
|
tauri-plugin-autostart = "2.0.0"
|
||||||
shared_child = "1.0.1"
|
shared_child = "1.0.1"
|
||||||
|
serde_with = "3.12.0"
|
||||||
|
|
||||||
[dependencies.tauri]
|
[dependencies.tauri]
|
||||||
version = "2.1.1"
|
version = "2.1.1"
|
||||||
|
|||||||
@ -9,10 +9,11 @@ use directories::BaseDirs;
|
|||||||
use log::debug;
|
use log::debug;
|
||||||
use rustbreak::{DeSerError, DeSerializer, PathDatabase};
|
use rustbreak::{DeSerError, DeSerializer, PathDatabase};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
use serde_with::serde_as;
|
||||||
use tauri::AppHandle;
|
use tauri::AppHandle;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{download_manager::downloadable_metadata::DownloadableMetadata, library::push_game_update, process::process_manager::Platform, state::DownloadStatusManager, DB};
|
use crate::{download_manager::downloadable_metadata::DownloadableMetadata, library::push_game_update, process::process_manager::Platform, state::GameStatusManager, DB};
|
||||||
|
|
||||||
#[derive(serde::Serialize, Clone, Deserialize)]
|
#[derive(serde::Serialize, Clone, Deserialize)]
|
||||||
pub struct DatabaseAuth {
|
pub struct DatabaseAuth {
|
||||||
@ -24,7 +25,7 @@ pub struct DatabaseAuth {
|
|||||||
// Strings are version names for a particular game
|
// Strings are version names for a particular game
|
||||||
#[derive(Serialize, Clone, Deserialize)]
|
#[derive(Serialize, Clone, Deserialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum ApplicationStatus {
|
pub enum GameDownloadStatus {
|
||||||
Remote {},
|
Remote {},
|
||||||
SetupRequired {
|
SetupRequired {
|
||||||
version_name: String,
|
version_name: String,
|
||||||
@ -45,9 +46,9 @@ pub enum ApplicationTransientStatus {
|
|||||||
Running {},
|
Running {},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ApplicationVersion {
|
pub struct GameVersion {
|
||||||
pub version_index: usize,
|
pub version_index: usize,
|
||||||
pub version_name: String,
|
pub version_name: String,
|
||||||
pub launch_command: String,
|
pub launch_command: String,
|
||||||
@ -55,13 +56,15 @@ pub struct ApplicationVersion {
|
|||||||
pub platform: Platform,
|
pub platform: Platform,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
#[derive(Serialize, Clone, Deserialize)]
|
#[derive(Serialize, Clone, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DatabaseApplications {
|
pub struct DatabaseApplications {
|
||||||
pub install_dirs: Vec<String>,
|
pub install_dirs: Vec<String>,
|
||||||
// Guaranteed to exist if the game also exists in the app state map
|
// Guaranteed to exist if the game also exists in the app state map
|
||||||
pub statuses: HashMap<DownloadableMetadata, ApplicationStatus>,
|
pub game_statuses: HashMap<String, GameDownloadStatus>,
|
||||||
pub versions: HashMap<DownloadableMetadata, HashMap<String, ApplicationVersion>>,
|
pub game_versions: HashMap<String, HashMap<String, GameVersion>>,
|
||||||
|
pub installed_game_version: HashMap<String, DownloadableMetadata>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub transient_statuses: HashMap<DownloadableMetadata, ApplicationTransientStatus>,
|
pub transient_statuses: HashMap<DownloadableMetadata, ApplicationTransientStatus>,
|
||||||
@ -140,9 +143,10 @@ impl DatabaseImpls for DatabaseInterface {
|
|||||||
base_url: "".to_string(),
|
base_url: "".to_string(),
|
||||||
applications: DatabaseApplications {
|
applications: DatabaseApplications {
|
||||||
install_dirs: vec![games_base_dir.to_str().unwrap().to_string()],
|
install_dirs: vec![games_base_dir.to_str().unwrap().to_string()],
|
||||||
statuses: HashMap::new(),
|
game_statuses: HashMap::new(),
|
||||||
transient_statuses: HashMap::new(),
|
transient_statuses: HashMap::new(),
|
||||||
versions: HashMap::new(),
|
game_versions: HashMap::new(),
|
||||||
|
installed_game_version: HashMap::new(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
debug!(
|
debug!(
|
||||||
@ -222,15 +226,15 @@ pub fn fetch_download_dir_stats() -> Result<Vec<String>, String> {
|
|||||||
|
|
||||||
pub fn set_game_status<F: FnOnce(&mut RwLockWriteGuard<'_, Database>, &DownloadableMetadata)>(
|
pub fn set_game_status<F: FnOnce(&mut RwLockWriteGuard<'_, Database>, &DownloadableMetadata)>(
|
||||||
app_handle: &AppHandle,
|
app_handle: &AppHandle,
|
||||||
id: DownloadableMetadata,
|
meta: DownloadableMetadata,
|
||||||
setter: F,
|
setter: F,
|
||||||
) {
|
) {
|
||||||
let mut db_handle = DB.borrow_data_mut().unwrap();
|
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||||
setter(&mut db_handle, &id);
|
setter(&mut db_handle, &meta);
|
||||||
drop(db_handle);
|
drop(db_handle);
|
||||||
DB.save().unwrap();
|
DB.save().unwrap();
|
||||||
|
|
||||||
let status = DownloadStatusManager::fetch_state(&id);
|
let status = GameStatusManager::fetch_state(&meta.id);
|
||||||
|
|
||||||
push_game_update(app_handle, id, status);
|
push_game_update(app_handle, &meta, status);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,7 +130,7 @@ impl DownloadManager {
|
|||||||
}
|
}
|
||||||
pub fn cancel(&self, meta: DownloadableMetadata) {
|
pub fn cancel(&self, meta: DownloadableMetadata) {
|
||||||
self.command_sender
|
self.command_sender
|
||||||
.send(DownloadManagerSignal::Remove(meta))
|
.send(DownloadManagerSignal::Cancel(meta))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
pub fn rearrange(&self, current_index: usize, new_index: usize) {
|
pub fn rearrange(&self, current_index: usize, new_index: usize) {
|
||||||
|
|||||||
@ -117,6 +117,7 @@ impl DownloadManagerBuilder {
|
|||||||
let mut download_thread_lock = self.current_download_thread.lock().unwrap();
|
let mut download_thread_lock = self.current_download_thread.lock().unwrap();
|
||||||
*download_thread_lock = None;
|
*download_thread_lock = None;
|
||||||
drop(download_thread_lock);
|
drop(download_thread_lock);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop_and_wait_current_download(&self) {
|
fn stop_and_wait_current_download(&self) {
|
||||||
@ -179,12 +180,13 @@ impl DownloadManagerBuilder {
|
|||||||
info!("Got signal Queue");
|
info!("Got signal Queue");
|
||||||
let meta = download_agent.metadata();
|
let meta = download_agent.metadata();
|
||||||
|
|
||||||
|
info!("Meta: {:?}", meta);
|
||||||
|
|
||||||
if self.download_queue.exists(meta.clone()) {
|
if self.download_queue.exists(meta.clone()) {
|
||||||
info!("Download with same ID already exists");
|
info!("Download with same ID already exists");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let download_agent = generate_downloadable(meta.clone());
|
|
||||||
download_agent.on_initialised(&self.app_handle);
|
download_agent.on_initialised(&self.app_handle);
|
||||||
self.download_queue.append(meta.clone());
|
self.download_queue.append(meta.clone());
|
||||||
self.download_agent_registry.insert(meta, download_agent);
|
self.download_agent_registry.insert(meta, download_agent);
|
||||||
@ -194,9 +196,15 @@ impl DownloadManagerBuilder {
|
|||||||
|
|
||||||
fn manage_go_signal(&mut self) {
|
fn manage_go_signal(&mut self) {
|
||||||
info!("Got signal Go");
|
info!("Got signal Go");
|
||||||
if !(self.download_agent_registry.is_empty()) { return; }
|
if self.download_agent_registry.is_empty() {
|
||||||
|
info!("Download agent registry: {:?}", self.download_agent_registry.len());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if self.current_download_agent.is_some() { return; }
|
if self.current_download_agent.is_some() {
|
||||||
|
info!("Current download agent: {:?}", self.current_download_agent.as_ref().unwrap().metadata());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
info!("Current download queue: {:?}", self.download_queue.read());
|
info!("Current download queue: {:?}", self.download_queue.read());
|
||||||
|
|
||||||
@ -220,11 +228,11 @@ impl DownloadManagerBuilder {
|
|||||||
let app_handle = self.app_handle.clone();
|
let app_handle = self.app_handle.clone();
|
||||||
|
|
||||||
*download_thread_lock = Some(spawn(move || {
|
*download_thread_lock = Some(spawn(move || {
|
||||||
match download_agent.download() {
|
match download_agent.download(&app_handle) {
|
||||||
// Ok(true) is for completed and exited properly
|
// Ok(true) is for completed and exited properly
|
||||||
Ok(true) => {
|
Ok(true) => {
|
||||||
download_agent.on_complete(&app_handle);
|
download_agent.on_complete(&app_handle);
|
||||||
sender.send(DownloadManagerSignal::Completed(download_agent.metadata()));
|
sender.send(DownloadManagerSignal::Completed(download_agent.metadata())).unwrap();
|
||||||
},
|
},
|
||||||
// Ok(false) is for incomplete but exited properly
|
// Ok(false) is for incomplete but exited properly
|
||||||
Ok(false) => {
|
Ok(false) => {
|
||||||
@ -257,6 +265,7 @@ impl DownloadManagerBuilder {
|
|||||||
self.remove_and_cleanup_front_download(&meta);
|
self.remove_and_cleanup_front_download(&meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.push_ui_queue_update();
|
||||||
self.sender.send(DownloadManagerSignal::Go).unwrap();
|
self.sender.send(DownloadManagerSignal::Go).unwrap();
|
||||||
}
|
}
|
||||||
fn manage_error_signal(&mut self, error: ApplicationDownloadError) {
|
fn manage_error_signal(&mut self, error: ApplicationDownloadError) {
|
||||||
@ -279,19 +288,38 @@ impl DownloadManagerBuilder {
|
|||||||
current_download.on_cancelled(&self.app_handle);
|
current_download.on_cancelled(&self.app_handle);
|
||||||
self.stop_and_wait_current_download();
|
self.stop_and_wait_current_download();
|
||||||
|
|
||||||
|
self.download_queue.pop_front();
|
||||||
|
|
||||||
self.cleanup_current_download();
|
self.cleanup_current_download();
|
||||||
|
info!("Current donwload queue: {:?}", self.download_queue.read());
|
||||||
|
}
|
||||||
|
// TODO: Collapse these two into a single if statement somehow
|
||||||
|
else {
|
||||||
|
if let Some(download_agent) = self.download_agent_registry.get(meta) {
|
||||||
|
info!("Object exists in registry");
|
||||||
|
let index = self.download_queue.get_by_meta(meta);
|
||||||
|
if let Some(index) = index {
|
||||||
|
download_agent.on_cancelled(&self.app_handle);
|
||||||
|
let _ = self.download_queue.edit().remove(index).unwrap();
|
||||||
|
let removed = self.download_agent_registry.remove(meta);
|
||||||
|
info!("Removed {:?} from queue {:?}", removed.and_then(|x| Some(x.metadata())), self.download_queue.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if let Some(download_agent) = self.download_agent_registry.get(meta) {
|
if let Some(download_agent) = self.download_agent_registry.get(meta) {
|
||||||
|
info!("Object exists in registry");
|
||||||
let index = self.download_queue.get_by_meta(meta);
|
let index = self.download_queue.get_by_meta(meta);
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
download_agent.on_cancelled(&self.app_handle);
|
download_agent.on_cancelled(&self.app_handle);
|
||||||
let queue_handle = self.download_queue.edit().remove(index);
|
let _ = self.download_queue.edit().remove(index).unwrap();
|
||||||
self.download_agent_registry.remove(meta);
|
let removed = self.download_agent_registry.remove(meta);
|
||||||
|
info!("Removed {:?} from queue {:?}", removed.and_then(|x| Some(x.metadata())), self.download_queue.read());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.push_ui_queue_update();
|
||||||
}
|
}
|
||||||
fn uninstall_application(&mut self, meta: &DownloadableMetadata) {
|
fn uninstall_application(&mut self, meta: &DownloadableMetadata) {
|
||||||
let download_agent = match self.download_agent_registry.get(meta) {
|
let download_agent = match self.download_agent_registry.get(meta) {
|
||||||
@ -304,24 +332,25 @@ impl DownloadManagerBuilder {
|
|||||||
fn push_ui_stats_update(&self, kbs: usize, time: usize) {
|
fn push_ui_stats_update(&self, kbs: usize, time: usize) {
|
||||||
let event_data = StatsUpdateEvent { speed: kbs, time };
|
let event_data = StatsUpdateEvent { speed: kbs, time };
|
||||||
|
|
||||||
self.app_handle.emit("update_stats", event_data);
|
self.app_handle.emit("update_stats", event_data).unwrap();
|
||||||
}
|
}
|
||||||
fn push_ui_queue_update(&self) {
|
fn push_ui_queue_update(&self) {
|
||||||
let registry = &self.download_agent_registry;
|
let queue = &self.download_queue.read();
|
||||||
let queue_objs = registry
|
let queue_objs = queue
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(key, val)| QueueUpdateEventQueueData {
|
.map(|(key)| {
|
||||||
|
let val = self.download_agent_registry.get(key).unwrap();
|
||||||
|
QueueUpdateEventQueueData {
|
||||||
meta: DownloadableMetadata::clone(&key),
|
meta: DownloadableMetadata::clone(&key),
|
||||||
status: val.status(),
|
status: val.status(),
|
||||||
progress: val.progress().get_progress()
|
progress: val.progress().get_progress()
|
||||||
})
|
}})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let event_data = QueueUpdateEvent {
|
let event_data = QueueUpdateEvent {
|
||||||
queue: queue_objs,
|
queue: queue_objs,
|
||||||
status: self.status.lock().unwrap().clone(),
|
|
||||||
};
|
};
|
||||||
self.app_handle.emit("update_queue", event_data);
|
self.app_handle.emit("update_queue", event_data).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::sync::{mpsc::Sender, Arc};
|
use std::{fmt::{self, Debug}, sync::{mpsc::Sender, Arc}};
|
||||||
|
|
||||||
use tauri::AppHandle;
|
use tauri::AppHandle;
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub trait Downloadable: Send + Sync {
|
pub trait Downloadable: Send + Sync {
|
||||||
fn download(&self) -> Result<bool, ApplicationDownloadError>;
|
fn download(&self, app_handle: &AppHandle) -> Result<bool, ApplicationDownloadError>;
|
||||||
fn progress(&self) -> Arc<ProgressObject>;
|
fn progress(&self) -> Arc<ProgressObject>;
|
||||||
fn control_flag(&self) -> DownloadThreadControl;
|
fn control_flag(&self) -> DownloadThreadControl;
|
||||||
fn status(&self) -> DownloadStatus;
|
fn status(&self) -> DownloadStatus;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone, Copy)]
|
||||||
pub enum DownloadType {
|
pub enum DownloadType {
|
||||||
Game,
|
Game,
|
||||||
Tool,
|
Tool,
|
||||||
@ -9,13 +9,14 @@ pub enum DownloadType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DownloadableMetadata {
|
pub struct DownloadableMetadata {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub version: String,
|
pub version: Option<String>,
|
||||||
pub download_type: DownloadType
|
pub download_type: DownloadType
|
||||||
}
|
}
|
||||||
impl DownloadableMetadata {
|
impl DownloadableMetadata {
|
||||||
pub fn new(id: String, version: String, download_type: DownloadType) -> Self {
|
pub fn new(id: String, version: Option<String>, download_type: DownloadType) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
version,
|
version,
|
||||||
|
|||||||
@ -1,22 +1,24 @@
|
|||||||
use crate::auth::generate_authorization_header;
|
use crate::auth::generate_authorization_header;
|
||||||
use crate::db::{set_game_status, DatabaseImpls};
|
use crate::db::{set_game_status, GameDownloadStatus, ApplicationTransientStatus, DatabaseImpls};
|
||||||
use crate::download_manager::application_download_error::ApplicationDownloadError;
|
use crate::download_manager::application_download_error::ApplicationDownloadError;
|
||||||
use crate::download_manager::download_manager::{DownloadManagerSignal, DownloadStatus};
|
use crate::download_manager::download_manager::{DownloadManagerSignal, DownloadStatus};
|
||||||
use crate::download_manager::download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag};
|
use crate::download_manager::download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag};
|
||||||
use crate::download_manager::downloadable::Downloadable;
|
use crate::download_manager::downloadable::Downloadable;
|
||||||
use crate::download_manager::downloadable_metadata::DownloadableMetadata;
|
use crate::download_manager::downloadable_metadata::{DownloadType, DownloadableMetadata};
|
||||||
use crate::download_manager::progress_object::{ProgressHandle, ProgressObject};
|
use crate::download_manager::progress_object::{ProgressHandle, ProgressObject};
|
||||||
use crate::downloads::manifest::{DropDownloadContext, DropManifest};
|
use crate::downloads::manifest::{DropDownloadContext, DropManifest};
|
||||||
|
use crate::library::{on_game_complete, push_game_update};
|
||||||
use crate::remote::RemoteAccessError;
|
use crate::remote::RemoteAccessError;
|
||||||
use crate::DB;
|
use crate::DB;
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use rayon::ThreadPoolBuilder;
|
use rayon::ThreadPoolBuilder;
|
||||||
use tauri::Emitter;
|
use tauri::{AppHandle, Emitter};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::{create_dir_all, File};
|
use std::fs::{create_dir_all, remove_dir_all, File};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::thread::spawn;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use urlencoding::encode;
|
use urlencoding::encode;
|
||||||
|
|
||||||
@ -36,10 +38,9 @@ pub struct GameDownloadAgent {
|
|||||||
pub progress: Arc<ProgressObject>,
|
pub progress: Arc<ProgressObject>,
|
||||||
sender: Sender<DownloadManagerSignal>,
|
sender: Sender<DownloadManagerSignal>,
|
||||||
pub stored_manifest: StoredManifest,
|
pub stored_manifest: StoredManifest,
|
||||||
|
status: Mutex<DownloadStatus>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl GameDownloadAgent {
|
impl GameDownloadAgent {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: String,
|
id: String,
|
||||||
@ -70,6 +71,7 @@ impl GameDownloadAgent {
|
|||||||
progress: Arc::new(ProgressObject::new(0, 0, sender.clone())),
|
progress: Arc::new(ProgressObject::new(0, 0, sender.clone())),
|
||||||
sender,
|
sender,
|
||||||
stored_manifest,
|
stored_manifest,
|
||||||
|
status: Mutex::new(DownloadStatus::Queued),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,10 +89,14 @@ impl GameDownloadAgent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Blocking
|
// Blocking
|
||||||
pub fn download(&self) -> Result<bool, ApplicationDownloadError> {
|
pub fn download(&self, app_handle: &AppHandle) -> Result<bool, ApplicationDownloadError> {
|
||||||
|
info!("Setting up download");
|
||||||
self.setup_download()?;
|
self.setup_download()?;
|
||||||
|
info!("Setting progress object params");
|
||||||
self.set_progress_object_params();
|
self.set_progress_object_params();
|
||||||
|
info!("Running");
|
||||||
let timer = Instant::now();
|
let timer = Instant::now();
|
||||||
|
push_game_update(app_handle, &self.metadata(), (None, Some(ApplicationTransientStatus::Downloading { version_name: self.version.clone() })));
|
||||||
let res = self.run().map_err(|_| ApplicationDownloadError::DownloadError);
|
let res = self.run().map_err(|_| ApplicationDownloadError::DownloadError);
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
@ -242,6 +248,7 @@ impl GameDownloadAgent {
|
|||||||
|
|
||||||
pool.scope(|scope| {
|
pool.scope(|scope| {
|
||||||
for (index, context) in self.contexts.lock().unwrap().iter().enumerate() {
|
for (index, context) in self.contexts.lock().unwrap().iter().enumerate() {
|
||||||
|
info!("Running index {}", index);
|
||||||
let completed_indexes = completed_indexes_loop_arc.clone();
|
let completed_indexes = completed_indexes_loop_arc.clone();
|
||||||
|
|
||||||
let progress = self.progress.get(index); // Clone arcs
|
let progress = self.progress.get(index); // Clone arcs
|
||||||
@ -305,8 +312,9 @@ impl GameDownloadAgent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Downloadable for GameDownloadAgent {
|
impl Downloadable for GameDownloadAgent {
|
||||||
fn download(&self) -> Result<bool, ApplicationDownloadError> {
|
fn download(&self, app_handle: &AppHandle) -> Result<bool, ApplicationDownloadError> {
|
||||||
self.download()
|
*self.status.lock().unwrap() = DownloadStatus::Downloading;
|
||||||
|
self.download(app_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress(&self) -> Arc<ProgressObject> {
|
fn progress(&self) -> Arc<ProgressObject> {
|
||||||
@ -318,14 +326,20 @@ impl Downloadable for GameDownloadAgent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn metadata(&self) -> DownloadableMetadata {
|
fn metadata(&self) -> DownloadableMetadata {
|
||||||
todo!()
|
DownloadableMetadata {
|
||||||
|
id: self.id.clone(),
|
||||||
|
version: Some(self.version.clone()),
|
||||||
|
download_type: DownloadType::Game,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_initialised(&self, _app_handle: &tauri::AppHandle) {
|
fn on_initialised(&self, _app_handle: &tauri::AppHandle) {
|
||||||
|
*self.status.lock().unwrap() = DownloadStatus::Queued;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_error(&self, app_handle: &tauri::AppHandle, error: ApplicationDownloadError) {
|
fn on_error(&self, app_handle: &tauri::AppHandle, error: ApplicationDownloadError) {
|
||||||
|
*self.status.lock().unwrap() = DownloadStatus::Error;
|
||||||
app_handle
|
app_handle
|
||||||
.emit("download_error", error.to_string())
|
.emit("download_error", error.to_string())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -339,22 +353,87 @@ impl Downloadable for GameDownloadAgent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_complete(&self, app_handle: &tauri::AppHandle) {
|
fn on_complete(&self, app_handle: &tauri::AppHandle) {
|
||||||
todo!()
|
on_game_complete(&self.metadata(), self.stored_manifest.base_path.to_string_lossy().to_string(), app_handle).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_incomplete(&self, app_handle: &tauri::AppHandle) {
|
fn on_incomplete(&self, app_handle: &tauri::AppHandle) {
|
||||||
todo!()
|
*self.status.lock().unwrap() = DownloadStatus::Queued;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_cancelled(&self, app_handle: &tauri::AppHandle) {
|
fn on_cancelled(&self, app_handle: &tauri::AppHandle) {
|
||||||
todo!()
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_uninstall(&self, app_handle: &tauri::AppHandle) {
|
fn on_uninstall(&self, app_handle: &tauri::AppHandle) {
|
||||||
todo!()
|
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||||
|
let metadata = self.metadata();
|
||||||
|
db_handle
|
||||||
|
.applications
|
||||||
|
.transient_statuses
|
||||||
|
.entry(metadata.clone())
|
||||||
|
.and_modify(|v| *v = ApplicationTransientStatus::Uninstalling {});
|
||||||
|
|
||||||
|
push_game_update(
|
||||||
|
app_handle,
|
||||||
|
&metadata,
|
||||||
|
(None, Some(ApplicationTransientStatus::Uninstalling {})),
|
||||||
|
);
|
||||||
|
|
||||||
|
let previous_state = db_handle.applications.game_statuses.get(&metadata.id).cloned();
|
||||||
|
if previous_state.is_none() {
|
||||||
|
info!("uninstall job doesn't have previous state, failing silently");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let previous_state = previous_state.unwrap();
|
||||||
|
if let Some((version_name, install_dir)) = match previous_state {
|
||||||
|
GameDownloadStatus::Installed {
|
||||||
|
version_name,
|
||||||
|
install_dir,
|
||||||
|
} => Some((version_name, install_dir)),
|
||||||
|
GameDownloadStatus::SetupRequired {
|
||||||
|
version_name,
|
||||||
|
install_dir,
|
||||||
|
} => Some((version_name, install_dir)),
|
||||||
|
_ => None,
|
||||||
|
} {
|
||||||
|
db_handle
|
||||||
|
.applications
|
||||||
|
.transient_statuses
|
||||||
|
.entry(metadata.clone())
|
||||||
|
.and_modify(|v| *v = ApplicationTransientStatus::Uninstalling {});
|
||||||
|
drop(db_handle);
|
||||||
|
|
||||||
|
let sender = self.sender.clone();
|
||||||
|
let app_handle = app_handle.clone();
|
||||||
|
spawn(move || match remove_dir_all(install_dir) {
|
||||||
|
Err(e) => {
|
||||||
|
sender
|
||||||
|
.send(DownloadManagerSignal::Error(ApplicationDownloadError::IoError(
|
||||||
|
e.kind(),
|
||||||
|
)))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||||
|
db_handle.applications.transient_statuses.remove(&metadata);
|
||||||
|
db_handle
|
||||||
|
.applications
|
||||||
|
.game_statuses
|
||||||
|
.entry(metadata.id.clone())
|
||||||
|
.and_modify(|e| *e = GameDownloadStatus::Remote {});
|
||||||
|
drop(db_handle);
|
||||||
|
DB.save().unwrap();
|
||||||
|
|
||||||
|
info!("uninstalled game id {}", metadata.id);
|
||||||
|
|
||||||
|
push_game_update(&app_handle, &metadata, (Some(GameDownloadStatus::Remote {}), None));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn status(&self) -> DownloadStatus {
|
fn status(&self) -> DownloadStatus {
|
||||||
todo!()
|
self.status.lock().unwrap().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,8 +47,8 @@ pub fn move_game_in_queue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn cancel_game(state: tauri::State<'_, Mutex<AppState>>, game_id: DownloadableMetadata) {
|
pub fn cancel_game(state: tauri::State<'_, Mutex<AppState>>, meta: DownloadableMetadata) {
|
||||||
state.lock().unwrap().download_manager.cancel(Arc::new(game_id))
|
state.lock().unwrap().download_manager.cancel(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -22,7 +22,7 @@ use auth::{
|
|||||||
};
|
};
|
||||||
use cleanup::{cleanup_and_exit, quit};
|
use cleanup::{cleanup_and_exit, quit};
|
||||||
use db::{
|
use db::{
|
||||||
add_download_dir, delete_download_dir, fetch_download_dir_stats, DatabaseInterface, ApplicationStatus,
|
add_download_dir, delete_download_dir, fetch_download_dir_stats, DatabaseInterface, GameDownloadStatus,
|
||||||
DATA_ROOT_DIR,
|
DATA_ROOT_DIR,
|
||||||
};
|
};
|
||||||
use download_manager::download_manager::DownloadManager;
|
use download_manager::download_manager::DownloadManager;
|
||||||
@ -33,7 +33,7 @@ use downloads::download_commands::*;
|
|||||||
use http::Response;
|
use http::Response;
|
||||||
use http::{header::*, response::Builder as ResponseBuilder};
|
use http::{header::*, response::Builder as ResponseBuilder};
|
||||||
use library::{
|
use library::{
|
||||||
fetch_game, fetch_game_status, fetch_game_verion_options, fetch_library, uninstall_game, Game,
|
fetch_game, fetch_game_status, fetch_game_verion_options, fetch_library, uninstall_game, Game
|
||||||
};
|
};
|
||||||
use log::{debug, info, warn, LevelFilter};
|
use log::{debug, info, warn, LevelFilter};
|
||||||
use log4rs::append::console::ConsoleAppender;
|
use log4rs::append::console::ConsoleAppender;
|
||||||
@ -82,7 +82,7 @@ pub struct User {
|
|||||||
pub struct AppState<'a> {
|
pub struct AppState<'a> {
|
||||||
status: AppStatus,
|
status: AppStatus,
|
||||||
user: Option<User>,
|
user: Option<User>,
|
||||||
games: HashMap<DownloadableMetadata, Game>,
|
games: HashMap<String, Game>,
|
||||||
|
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
download_manager: Arc<DownloadManager>,
|
download_manager: Arc<DownloadManager>,
|
||||||
@ -149,12 +149,12 @@ fn setup(handle: AppHandle) -> AppState<'static> {
|
|||||||
|
|
||||||
let db_handle = DB.borrow_data().unwrap();
|
let db_handle = DB.borrow_data().unwrap();
|
||||||
let mut missing_games = Vec::new();
|
let mut missing_games = Vec::new();
|
||||||
let statuses = db_handle.applications.statuses.clone();
|
let statuses = db_handle.applications.game_statuses.clone();
|
||||||
drop(db_handle);
|
drop(db_handle);
|
||||||
for (game_id, status) in statuses.into_iter() {
|
for (game_id, status) in statuses.into_iter() {
|
||||||
match status {
|
match status {
|
||||||
db::ApplicationStatus::Remote {} => {}
|
db::GameDownloadStatus::Remote {} => {}
|
||||||
db::ApplicationStatus::SetupRequired {
|
db::GameDownloadStatus::SetupRequired {
|
||||||
version_name: _,
|
version_name: _,
|
||||||
install_dir,
|
install_dir,
|
||||||
} => {
|
} => {
|
||||||
@ -163,7 +163,7 @@ fn setup(handle: AppHandle) -> AppState<'static> {
|
|||||||
missing_games.push(game_id);
|
missing_games.push(game_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
db::ApplicationStatus::Installed {
|
db::GameDownloadStatus::Installed {
|
||||||
version_name: _,
|
version_name: _,
|
||||||
install_dir,
|
install_dir,
|
||||||
} => {
|
} => {
|
||||||
@ -181,9 +181,9 @@ fn setup(handle: AppHandle) -> AppState<'static> {
|
|||||||
for game_id in missing_games {
|
for game_id in missing_games {
|
||||||
db_handle
|
db_handle
|
||||||
.applications
|
.applications
|
||||||
.statuses
|
.game_statuses
|
||||||
.entry(game_id)
|
.entry(game_id)
|
||||||
.and_modify(|v| *v = ApplicationStatus::Remote {});
|
.and_modify(|v| *v = GameDownloadStatus::Remote {});
|
||||||
}
|
}
|
||||||
drop(db_handle);
|
drop(db_handle);
|
||||||
info!("finished setup!");
|
info!("finished setup!");
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -6,12 +6,13 @@ use tauri::Emitter;
|
|||||||
use tauri::{AppHandle, Manager};
|
use tauri::{AppHandle, Manager};
|
||||||
use urlencoding::encode;
|
use urlencoding::encode;
|
||||||
|
|
||||||
use crate::db::DatabaseImpls;
|
use crate::db::{ApplicationTransientStatus, DatabaseImpls, GameDownloadStatus};
|
||||||
use crate::db::ApplicationVersion;
|
use crate::db::GameVersion;
|
||||||
use crate::db::ApplicationStatus;
|
use crate::download_manager::download_manager::DownloadStatus;
|
||||||
use crate::download_manager::download_manager::{DownloadManagerStatus, DownloadStatus};
|
|
||||||
use crate::download_manager::downloadable_metadata::DownloadableMetadata;
|
use crate::download_manager::downloadable_metadata::DownloadableMetadata;
|
||||||
use crate::process::process_manager::Platform;
|
use crate::process::process_manager::Platform;
|
||||||
|
use crate::remote::RemoteAccessError;
|
||||||
|
use crate::state::{GameStatusManager, GameStatusWithTransient};
|
||||||
use crate::remote::{DropServerError, RemoteAccessError};
|
use crate::remote::{DropServerError, RemoteAccessError};
|
||||||
use crate::state::{GameStatusManager, GameStatusWithTransient};
|
use crate::state::{GameStatusManager, GameStatusWithTransient};
|
||||||
use crate::{auth::generate_authorization_header, AppState, DB};
|
use crate::{auth::generate_authorization_header, AppState, DB};
|
||||||
@ -20,12 +21,13 @@ use crate::{auth::generate_authorization_header, AppState, DB};
|
|||||||
pub struct FetchGameStruct {
|
pub struct FetchGameStruct {
|
||||||
game: Game,
|
game: Game,
|
||||||
status: GameStatusWithTransient,
|
status: GameStatusWithTransient,
|
||||||
|
status: GameStatusWithTransient,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
meta: DownloadableMetadata,
|
id: String,
|
||||||
m_name: String,
|
m_name: String,
|
||||||
m_short_description: String,
|
m_short_description: String,
|
||||||
m_description: String,
|
m_description: String,
|
||||||
@ -39,7 +41,7 @@ pub struct Game {
|
|||||||
#[derive(serde::Serialize, Clone)]
|
#[derive(serde::Serialize, Clone)]
|
||||||
pub struct GameUpdateEvent {
|
pub struct GameUpdateEvent {
|
||||||
pub game_id: String,
|
pub game_id: String,
|
||||||
pub status: GameStatusWithTransient,
|
pub status: (Option<GameDownloadStatus>, Option<ApplicationTransientStatus>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Clone)]
|
#[derive(Serialize, Clone)]
|
||||||
@ -52,7 +54,6 @@ pub struct QueueUpdateEventQueueData {
|
|||||||
#[derive(serde::Serialize, Clone)]
|
#[derive(serde::Serialize, Clone)]
|
||||||
pub struct QueueUpdateEvent {
|
pub struct QueueUpdateEvent {
|
||||||
pub queue: Vec<QueueUpdateEventQueueData>,
|
pub queue: Vec<QueueUpdateEventQueueData>,
|
||||||
pub status: DownloadManagerStatus,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, Clone)]
|
#[derive(serde::Serialize, Clone)]
|
||||||
@ -99,12 +100,12 @@ fn fetch_library_logic(app: AppHandle) -> Result<Vec<Game>, RemoteAccessError> {
|
|||||||
let mut db_handle = DB.borrow_data_mut().unwrap();
|
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||||
|
|
||||||
for game in games.iter() {
|
for game in games.iter() {
|
||||||
handle.games.insert(game.meta.clone(), game.clone());
|
handle.games.insert(game.id.clone(), game.clone());
|
||||||
if !db_handle.applications.statuses.contains_key(&game.meta) {
|
if !db_handle.applications.game_statuses.contains_key(&game.id) {
|
||||||
db_handle
|
db_handle
|
||||||
.applications
|
.applications
|
||||||
.statuses
|
.game_statuses
|
||||||
.insert(game.meta.clone(), ApplicationStatus::Remote {});
|
.insert(game.id.clone(), GameDownloadStatus::Remote {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,15 +120,15 @@ pub fn fetch_library(app: AppHandle) -> Result<Vec<Game>, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_game_logic(
|
fn fetch_game_logic(
|
||||||
meta: DownloadableMetadata,
|
id: String,
|
||||||
app: tauri::AppHandle,
|
app: tauri::AppHandle,
|
||||||
) -> Result<FetchGameStruct, RemoteAccessError> {
|
) -> Result<FetchGameStruct, RemoteAccessError> {
|
||||||
let state = app.state::<Mutex<AppState>>();
|
let state = app.state::<Mutex<AppState>>();
|
||||||
let mut state_handle = state.lock().unwrap();
|
let mut state_handle = state.lock().unwrap();
|
||||||
|
|
||||||
let game = state_handle.games.get(&meta);
|
let game = state_handle.games.get(&id);
|
||||||
if let Some(game) = game {
|
if let Some(game) = game {
|
||||||
let status = DownloadStatusManager::fetch_state(&meta);
|
let status = GameStatusManager::fetch_state(&id);
|
||||||
|
|
||||||
let data = FetchGameStruct {
|
let data = FetchGameStruct {
|
||||||
game: game.clone(),
|
game: game.clone(),
|
||||||
@ -139,7 +140,7 @@ fn fetch_game_logic(
|
|||||||
|
|
||||||
let base_url = DB.fetch_base_url();
|
let base_url = DB.fetch_base_url();
|
||||||
|
|
||||||
let endpoint = base_url.join(&format!("/api/v1/game/{}", meta.id))?;
|
let endpoint = base_url.join(&format!("/api/v1/game/{}", id))?;
|
||||||
let header = generate_authorization_header();
|
let header = generate_authorization_header();
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
@ -158,18 +159,18 @@ fn fetch_game_logic(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let game = response.json::<Game>()?;
|
let game = response.json::<Game>()?;
|
||||||
state_handle.games.insert(meta.clone(), game.clone());
|
state_handle.games.insert(id.clone(), game.clone());
|
||||||
|
|
||||||
let mut db_handle = DB.borrow_data_mut().unwrap();
|
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||||
|
|
||||||
db_handle
|
db_handle
|
||||||
.applications
|
.applications
|
||||||
.statuses
|
.game_statuses
|
||||||
.entry(meta.clone())
|
.entry(id.clone())
|
||||||
.or_insert(ApplicationStatus::Remote {});
|
.or_insert(GameDownloadStatus::Remote {});
|
||||||
drop(db_handle);
|
drop(db_handle);
|
||||||
|
|
||||||
let status = DownloadStatusManager::fetch_state(&meta);
|
let status = GameStatusManager::fetch_state(&id);
|
||||||
|
|
||||||
let data = FetchGameStruct {
|
let data = FetchGameStruct {
|
||||||
game: game.clone(),
|
game: game.clone(),
|
||||||
@ -180,8 +181,8 @@ fn fetch_game_logic(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn fetch_game(id: DownloadableMetadata, app: tauri::AppHandle) -> Result<FetchGameStruct, String> {
|
pub fn fetch_game(game_id: String, app: tauri::AppHandle) -> Result<FetchGameStruct, String> {
|
||||||
let result = fetch_game_logic(id, app);
|
let result = fetch_game_logic(game_id, app);
|
||||||
|
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
return Err(result.err().unwrap().to_string());
|
return Err(result.err().unwrap().to_string());
|
||||||
@ -191,20 +192,20 @@ pub fn fetch_game(id: DownloadableMetadata, app: tauri::AppHandle) -> Result<Fet
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn fetch_game_status(meta: DownloadableMetadata) -> Result<ApplicationStatusWithTransient, String> {
|
pub fn fetch_game_status(id: String) -> Result<GameStatusWithTransient, String> {
|
||||||
let status = DownloadStatusManager::fetch_state(&meta);
|
let status = GameStatusManager::fetch_state(&id);
|
||||||
|
|
||||||
Ok(status)
|
Ok(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_game_verion_options_logic<'a>(
|
fn fetch_game_verion_options_logic<'a>(
|
||||||
meta: DownloadableMetadata,
|
game_id: String,
|
||||||
state: tauri::State<'_, Mutex<AppState>>,
|
state: tauri::State<'_, Mutex<AppState>>,
|
||||||
) -> Result<Vec<GameVersionOption>, RemoteAccessError> {
|
) -> Result<Vec<GameVersionOption>, RemoteAccessError> {
|
||||||
let base_url = DB.fetch_base_url();
|
let base_url = DB.fetch_base_url();
|
||||||
|
|
||||||
let endpoint =
|
let endpoint =
|
||||||
base_url.join(format!("/api/v1/client/metadata/versions?id={}", meta.id).as_str())?;
|
base_url.join(format!("/api/v1/client/metadata/versions?id={}", game_id).as_str())?;
|
||||||
let header = generate_authorization_header();
|
let header = generate_authorization_header();
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
@ -233,51 +234,49 @@ fn fetch_game_verion_options_logic<'a>(
|
|||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn fetch_game_verion_options<'a>(
|
|
||||||
game_id: DownloadableMetadata,
|
|
||||||
state: tauri::State<'_, Mutex<AppState>>,
|
|
||||||
) -> Result<Vec<GameVersionOption>, String> {
|
|
||||||
fetch_game_verion_options_logic(game_id, state).map_err(|e| e.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn uninstall_game(
|
pub fn uninstall_game(
|
||||||
game_id: DownloadableMetadata,
|
game_id: String,
|
||||||
state: tauri::State<'_, Mutex<AppState>>,
|
state: tauri::State<'_, Mutex<AppState>>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let state_lock = state.lock().unwrap();
|
let state_lock = state.lock().unwrap();
|
||||||
state_lock.download_manager.uninstall_application(game_id);
|
let meta = get_current_meta(&game_id)?;
|
||||||
|
|
||||||
|
state_lock.download_manager.uninstall_application(meta);
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_game_update(app_handle: &AppHandle, meta: DownloadableMetadata, status: ApplicationStatusWithTransient) {
|
pub fn get_current_meta(game_id: &String) -> Result<DownloadableMetadata, String> {
|
||||||
app_handle
|
match DB.borrow_data().unwrap().applications.installed_game_version.get(game_id) {
|
||||||
.emit(
|
Some(meta) => Ok(meta.clone()),
|
||||||
&format!("update_game/{}", meta.id),
|
None => Err(String::from("Could not find installed version")),
|
||||||
GameUpdateEvent {
|
}
|
||||||
game_id: meta.id,
|
}
|
||||||
status,
|
|
||||||
},
|
#[tauri::command]
|
||||||
)
|
pub fn fetch_game_verion_options<'a>(
|
||||||
.unwrap();
|
game_id: String,
|
||||||
|
state: tauri::State<'_, Mutex<AppState>>,
|
||||||
|
) -> Result<Vec<GameVersionOption>, String> {
|
||||||
|
fetch_game_verion_options_logic(game_id, state).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_game_complete(
|
pub fn on_game_complete(
|
||||||
meta: DownloadableMetadata,
|
meta: &DownloadableMetadata,
|
||||||
install_dir: String,
|
install_dir: String,
|
||||||
app_handle: &AppHandle,
|
app_handle: &AppHandle,
|
||||||
) -> Result<(), RemoteAccessError> {
|
) -> Result<(), RemoteAccessError> {
|
||||||
// Fetch game version information from remote
|
// Fetch game version information from remote
|
||||||
let base_url = DB.fetch_base_url();
|
let base_url = DB.fetch_base_url();
|
||||||
|
if meta.version.is_none() { return Err(RemoteAccessError::GameNotFound) }
|
||||||
|
|
||||||
let endpoint = base_url.join(
|
let endpoint = base_url.join(
|
||||||
format!(
|
format!(
|
||||||
"/api/v1/client/metadata/version?id={}&version={}",
|
"/api/v1/client/metadata/version?id={}&version={}",
|
||||||
meta.id,
|
meta.id,
|
||||||
encode(&meta.version)
|
encode(meta.version.as_ref().unwrap())
|
||||||
)
|
)
|
||||||
.as_str(),
|
.as_str(),
|
||||||
)?;
|
)?;
|
||||||
@ -289,34 +288,31 @@ pub fn on_game_complete(
|
|||||||
.header("Authorization", header)
|
.header("Authorization", header)
|
||||||
.send()?;
|
.send()?;
|
||||||
|
|
||||||
if response.status() != 200 {
|
let data = response.json::<GameVersion>()?;
|
||||||
return Err(RemoteAccessError::InvalidResponse(
|
|
||||||
response.json::<DropServerError>().map_err(|e| {
|
|
||||||
RemoteAccessError::Generic(format!("failed to parse server error: {}", e))
|
|
||||||
})?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = response.json::<ApplicationVersion>()?;
|
|
||||||
|
|
||||||
let mut handle = DB.borrow_data_mut().unwrap();
|
let mut handle = DB.borrow_data_mut().unwrap();
|
||||||
handle
|
handle
|
||||||
.applications
|
.applications
|
||||||
.versions
|
.game_versions
|
||||||
.entry(meta.clone())
|
.entry(meta.id.clone())
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(meta.version.clone(), data.clone());
|
.insert(meta.version.clone().unwrap(), data.clone());
|
||||||
|
handle
|
||||||
|
.applications
|
||||||
|
.installed_game_version
|
||||||
|
.insert(meta.id.clone(), meta.clone());
|
||||||
|
|
||||||
drop(handle);
|
drop(handle);
|
||||||
DB.save().unwrap();
|
DB.save().unwrap();
|
||||||
|
|
||||||
let status = if data.setup_command.is_empty() {
|
let status = if data.setup_command.is_empty() {
|
||||||
ApplicationStatus::Installed {
|
GameDownloadStatus::Installed {
|
||||||
version_name: (*meta.version.clone()).to_string(),
|
version_name: meta.version.clone().unwrap(),
|
||||||
install_dir,
|
install_dir,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ApplicationStatus::SetupRequired {
|
GameDownloadStatus::SetupRequired {
|
||||||
version_name: (*meta.version.clone()).to_string(),
|
version_name: meta.version.clone().unwrap(),
|
||||||
install_dir,
|
install_dir,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -324,15 +320,15 @@ pub fn on_game_complete(
|
|||||||
let mut db_handle = DB.borrow_data_mut().unwrap();
|
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||||
db_handle
|
db_handle
|
||||||
.applications
|
.applications
|
||||||
.statuses
|
.game_statuses
|
||||||
.insert(meta.clone(), status.clone());
|
.insert(meta.id.clone(), status.clone());
|
||||||
drop(db_handle);
|
drop(db_handle);
|
||||||
DB.save().unwrap();
|
DB.save().unwrap();
|
||||||
app_handle
|
app_handle
|
||||||
.emit(
|
.emit(
|
||||||
&format!("update_game/{}", meta.id),
|
&format!("update_game/{}", meta.id),
|
||||||
GameUpdateEvent {
|
GameUpdateEvent {
|
||||||
game_id: (*meta.id.clone()).to_string(),
|
game_id: meta.id.clone(),
|
||||||
status: (Some(status), None),
|
status: (Some(status), None),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -340,3 +336,15 @@ pub fn on_game_complete(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_game_update(app_handle: &AppHandle, meta: &DownloadableMetadata, status: GameStatusWithTransient) {
|
||||||
|
app_handle
|
||||||
|
.emit(
|
||||||
|
&format!("update_game/{}", meta.id),
|
||||||
|
GameUpdateEvent {
|
||||||
|
game_id: meta.id.clone(),
|
||||||
|
status,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
@ -1,16 +1,26 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use crate::{download_manager::downloadable_metadata::DownloadableMetadata, AppState};
|
use crate::{db::GameDownloadStatus, download_manager::downloadable_metadata::{DownloadType, DownloadableMetadata}, AppState, DB};
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn launch_game(
|
pub fn launch_game(
|
||||||
game_id: DownloadableMetadata,
|
id: String,
|
||||||
state: tauri::State<'_, Mutex<AppState>>,
|
state: tauri::State<'_, Mutex<AppState>>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let state_lock = state.lock().unwrap();
|
let state_lock = state.lock().unwrap();
|
||||||
let mut process_manager_lock = state_lock.process_manager.lock().unwrap();
|
let mut process_manager_lock = state_lock.process_manager.lock().unwrap();
|
||||||
|
|
||||||
process_manager_lock.launch_process(game_id)?;
|
let version = match DB.borrow_data().unwrap().applications.game_statuses.get(&id).cloned() {
|
||||||
|
Some(GameDownloadStatus::Installed { version_name, install_dir }) => version_name,
|
||||||
|
Some(GameDownloadStatus::SetupRequired { version_name, install_dir }) => return Err(String::from("Game setup still required")),
|
||||||
|
_ => return Err(String::from("Game not installed"))
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let meta = DownloadableMetadata { id, version: Some(version), download_type: DownloadType::Game };
|
||||||
|
|
||||||
|
|
||||||
|
process_manager_lock.launch_process(meta)?;
|
||||||
|
|
||||||
drop(process_manager_lock);
|
drop(process_manager_lock);
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
|
|||||||
@ -15,7 +15,7 @@ use tauri::{AppHandle, Manager};
|
|||||||
use umu_wrapper_lib::command_builder::UmuCommandBuilder;
|
use umu_wrapper_lib::command_builder::UmuCommandBuilder;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{ApplicationStatus, ApplicationTransientStatus, DATA_ROOT_DIR}, download_manager::{downloadable::Downloadable, downloadable_metadata::DownloadableMetadata}, library::push_game_update, state::DownloadStatusManager, AppState, DB
|
db::{GameDownloadStatus, ApplicationTransientStatus, DATA_ROOT_DIR}, download_manager::{downloadable::Downloadable, downloadable_metadata::DownloadableMetadata}, library::push_game_update, state::GameStatusManager, AppState, DB
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ProcessManager<'a> {
|
pub struct ProcessManager<'a> {
|
||||||
@ -106,18 +106,18 @@ impl ProcessManager<'_> {
|
|||||||
let mut db_handle = DB.borrow_data_mut().unwrap();
|
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||||
db_handle.applications.transient_statuses.remove(&meta);
|
db_handle.applications.transient_statuses.remove(&meta);
|
||||||
|
|
||||||
let current_state = db_handle.applications.statuses.get(&meta).cloned();
|
let current_state = db_handle.applications.game_statuses.get(&meta.id).cloned();
|
||||||
if let Some(saved_state) = current_state {
|
if let Some(saved_state) = current_state {
|
||||||
if let ApplicationStatus::SetupRequired {
|
if let GameDownloadStatus::SetupRequired {
|
||||||
version_name,
|
version_name,
|
||||||
install_dir,
|
install_dir,
|
||||||
} = saved_state
|
} = saved_state
|
||||||
{
|
{
|
||||||
if let Ok(exit_code) = result {
|
if let Ok(exit_code) = result {
|
||||||
if exit_code.success() {
|
if exit_code.success() {
|
||||||
db_handle.applications.statuses.insert(
|
db_handle.applications.game_statuses.insert(
|
||||||
meta.clone(),
|
meta.id.clone(),
|
||||||
ApplicationStatus::Installed {
|
GameDownloadStatus::Installed {
|
||||||
version_name: version_name.to_string(),
|
version_name: version_name.to_string(),
|
||||||
install_dir: install_dir.to_string(),
|
install_dir: install_dir.to_string(),
|
||||||
},
|
},
|
||||||
@ -128,9 +128,9 @@ impl ProcessManager<'_> {
|
|||||||
}
|
}
|
||||||
drop(db_handle);
|
drop(db_handle);
|
||||||
|
|
||||||
let status = DownloadStatusManager::fetch_state(&meta);
|
let status = GameStatusManager::fetch_state(&meta.id);
|
||||||
|
|
||||||
push_game_update(&self.app_handle, meta.clone(), status);
|
push_game_update(&self.app_handle, &meta, status);
|
||||||
|
|
||||||
// TODO better management
|
// TODO better management
|
||||||
}
|
}
|
||||||
@ -148,18 +148,20 @@ impl ProcessManager<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut db_lock = DB.borrow_data_mut().unwrap();
|
let mut db_lock = DB.borrow_data_mut().unwrap();
|
||||||
|
info!("Launching process {:?} with games {:?}", meta, db_lock.applications.game_versions);
|
||||||
|
|
||||||
let game_status = db_lock
|
let game_status = db_lock
|
||||||
.applications
|
.applications
|
||||||
.statuses
|
.game_statuses
|
||||||
.get(&meta)
|
.get(&meta.id)
|
||||||
.ok_or("Game not installed")?;
|
.ok_or("Game not installed")?;
|
||||||
|
|
||||||
let status_metadata: Option<(&String, &String)> = match game_status {
|
let status_metadata: Option<(&String, &String)> = match game_status {
|
||||||
ApplicationStatus::Installed {
|
GameDownloadStatus::Installed {
|
||||||
version_name,
|
version_name,
|
||||||
install_dir,
|
install_dir,
|
||||||
} => Some((version_name, install_dir)),
|
} => Some((version_name, install_dir)),
|
||||||
ApplicationStatus::SetupRequired {
|
GameDownloadStatus::SetupRequired {
|
||||||
version_name,
|
version_name,
|
||||||
install_dir,
|
install_dir,
|
||||||
} => Some((version_name, install_dir)),
|
} => Some((version_name, install_dir)),
|
||||||
@ -174,18 +176,18 @@ impl ProcessManager<'_> {
|
|||||||
|
|
||||||
let game_version = db_lock
|
let game_version = db_lock
|
||||||
.applications
|
.applications
|
||||||
.versions
|
.game_versions
|
||||||
.get(&meta)
|
.get(&meta.id)
|
||||||
.ok_or("Invalid game ID".to_owned())?
|
.ok_or("Invalid game ID".to_owned())?
|
||||||
.get(version_name)
|
.get(version_name)
|
||||||
.ok_or("Invalid version name".to_owned())?;
|
.ok_or("Invalid version name".to_owned())?;
|
||||||
|
|
||||||
let raw_command: String = match game_status {
|
let raw_command: String = match game_status {
|
||||||
ApplicationStatus::Installed {
|
GameDownloadStatus::Installed {
|
||||||
version_name: _,
|
version_name: _,
|
||||||
install_dir: _,
|
install_dir: _,
|
||||||
} => game_version.launch_command.clone(),
|
} => game_version.launch_command.clone(),
|
||||||
ApplicationStatus::SetupRequired {
|
GameDownloadStatus::SetupRequired {
|
||||||
version_name: _,
|
version_name: _,
|
||||||
install_dir: _,
|
install_dir: _,
|
||||||
} => game_version.setup_command.clone(),
|
} => game_version.setup_command.clone(),
|
||||||
@ -210,7 +212,7 @@ impl ProcessManager<'_> {
|
|||||||
.create(true)
|
.create(true)
|
||||||
.open(
|
.open(
|
||||||
self.log_output_dir
|
self.log_output_dir
|
||||||
.join(format!("{}-{}-{}.log", meta.id, meta.version, current_time.timestamp())),
|
.join(format!("{}-{}-{}.log", meta.id.clone(), meta.version.clone().unwrap_or_default(), current_time.timestamp())),
|
||||||
)
|
)
|
||||||
.map_err(|v| v.to_string())?;
|
.map_err(|v| v.to_string())?;
|
||||||
|
|
||||||
@ -221,8 +223,8 @@ impl ProcessManager<'_> {
|
|||||||
.create(true)
|
.create(true)
|
||||||
.open(self.log_output_dir.join(format!(
|
.open(self.log_output_dir.join(format!(
|
||||||
"{}-{}-{}-error.log",
|
"{}-{}-{}-error.log",
|
||||||
meta.id,
|
meta.id.clone(),
|
||||||
meta.version,
|
meta.version.clone().unwrap_or_default(),
|
||||||
current_time.timestamp()
|
current_time.timestamp()
|
||||||
)))
|
)))
|
||||||
.map_err(|v| v.to_string())?;
|
.map_err(|v| v.to_string())?;
|
||||||
@ -255,7 +257,7 @@ impl ProcessManager<'_> {
|
|||||||
|
|
||||||
push_game_update(
|
push_game_update(
|
||||||
&self.app_handle,
|
&self.app_handle,
|
||||||
meta.clone(),
|
&meta,
|
||||||
(None, Some(ApplicationTransientStatus::Running {})),
|
(None, Some(ApplicationTransientStatus::Running {})),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +1,23 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{ApplicationStatus, ApplicationTransientStatus}, download_manager::downloadable_metadata::DownloadableMetadata, DB
|
db::{ApplicationTransientStatus, GameDownloadStatus}, download_manager::downloadable_metadata::{DownloadType, DownloadableMetadata}, fetch_state, DB
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type GameStatusWithTransient = (Option<ApplicationStatus>, Option<ApplicationTransientStatus>);
|
pub type GameStatusWithTransient = (Option<GameDownloadStatus>, Option<ApplicationTransientStatus>);
|
||||||
pub struct GameStatusManager {}
|
pub struct GameStatusManager {}
|
||||||
|
|
||||||
impl DownloadStatusManager {
|
impl GameStatusManager {
|
||||||
pub fn fetch_state(id: &DownloadableMetadata) -> ApplicationStatusWithTransient {
|
pub fn fetch_state(game_id: &String) -> GameStatusWithTransient {
|
||||||
let db_lock = DB.borrow_data().unwrap();
|
let db_lock = DB.borrow_data().unwrap();
|
||||||
GameStatusManager::fetch_state_with_db(game_id, &db_lock)
|
let online_state = match db_lock.applications.installed_game_version.get(game_id) {
|
||||||
}
|
Some(meta) => db_lock
|
||||||
pub fn fetch_state_with_db(
|
.applications
|
||||||
game_id: &String,
|
.transient_statuses
|
||||||
db_lock: &Database,
|
.get(meta)
|
||||||
) -> GameStatusWithTransient {
|
.cloned(),
|
||||||
let offline_state = db_lock.applications.statuses.get(game_id).cloned();
|
None => None,
|
||||||
let online_state = db_lock.applications.transient_statuses.get(game_id).cloned();
|
};
|
||||||
|
let offline_state = db_lock.applications.game_statuses.get(game_id).cloned();
|
||||||
|
drop(db_lock);
|
||||||
|
|
||||||
if online_state.is_some() {
|
if online_state.is_some() {
|
||||||
return (None, online_state);
|
return (None, online_state);
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use tauri::AppHandle;
|
||||||
|
|
||||||
use crate::download_manager::{application_download_error::ApplicationDownloadError, download_thread_control_flag::DownloadThreadControl, downloadable::Downloadable, downloadable_metadata::DownloadableMetadata, progress_object::ProgressObject};
|
use crate::download_manager::{application_download_error::ApplicationDownloadError, download_thread_control_flag::DownloadThreadControl, downloadable::Downloadable, downloadable_metadata::DownloadableMetadata, progress_object::ProgressObject};
|
||||||
|
|
||||||
pub struct ToolDownloadAgent {
|
pub struct ToolDownloadAgent {
|
||||||
@ -10,7 +12,7 @@ pub struct ToolDownloadAgent {
|
|||||||
progress: Arc<ProgressObject>,
|
progress: Arc<ProgressObject>,
|
||||||
}
|
}
|
||||||
impl Downloadable for ToolDownloadAgent {
|
impl Downloadable for ToolDownloadAgent {
|
||||||
fn download(&self) -> Result<bool, ApplicationDownloadError> {
|
fn download(&self, app_handle: &AppHandle) -> Result<bool, ApplicationDownloadError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
types.ts
13
types.ts
@ -59,3 +59,16 @@ export type GameStatus = {
|
|||||||
type: GameStatusEnum;
|
type: GameStatusEnum;
|
||||||
version_name?: string;
|
version_name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum DownloadableType {
|
||||||
|
Game = "Game",
|
||||||
|
Tool = "Tool",
|
||||||
|
DLC = "DLC",
|
||||||
|
Mod = "Mod"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DownloadableMetadata = {
|
||||||
|
id: string,
|
||||||
|
version: string,
|
||||||
|
downloadType: DownloadableType
|
||||||
|
}
|
||||||
9
utils/generateGameMeta.ts
Normal file
9
utils/generateGameMeta.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { type DownloadableMetadata, DownloadableType } from '~/types'
|
||||||
|
|
||||||
|
export default function generateGameMeta(gameId: string, version: string): DownloadableMetadata {
|
||||||
|
return {
|
||||||
|
id: gameId,
|
||||||
|
version,
|
||||||
|
downloadType: DownloadableType.Game
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user