mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-13 08:12:44 +10:00
Compare commits
2 Commits
main
...
52-feature
| Author | SHA1 | Date | |
|---|---|---|---|
| 96df57ac54 | |||
| 8069616f2b |
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "view",
|
"name": "view",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.3.3",
|
"version": "0.3.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nuxt generate",
|
"build": "nuxt generate",
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="grow w-full h-full flex items-center justify-center">
|
|
||||||
<div class="flex flex-col items-center">
|
|
||||||
<WrenchScrewdriverIcon
|
|
||||||
class="h-12 w-12 text-blue-600"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<div class="mt-3 text-center sm:mt-5">
|
|
||||||
<h1 class="text-3xl font-semibold font-display leading-6 text-zinc-100">
|
|
||||||
Under construction
|
|
||||||
</h1>
|
|
||||||
<div class="mt-4">
|
|
||||||
<p class="text-sm text-zinc-400 max-w-lg">
|
|
||||||
This page hasn't been implemented yet.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import {
|
|
||||||
WrenchScrewdriverIcon,
|
|
||||||
} from "@heroicons/vue/20/solid";
|
|
||||||
</script>
|
|
||||||
16
main/pages/community/index.vue
Normal file
16
main/pages/community/index.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mx-auto flex flex-col items-center gap-y-4 max-w-2xl py-32 sm:py-48 lg:py-56">
|
||||||
|
<div>
|
||||||
|
<Wordmark />
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="text-balance text-4xl font-bold font-display tracking-tight text-zinc-100 sm:text-6xl">
|
||||||
|
Under construction
|
||||||
|
</h1>
|
||||||
|
<p class="mt-6 text-lg leading-8 text-zinc-400">
|
||||||
|
Yes, we know. We're working on it <a class="text-white" target="_blank"
|
||||||
|
href="https://github.com/Drop-OSS/drop-app/issues/52">here.</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@ -1,25 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="grow w-full h-full flex items-center justify-center">
|
|
||||||
<div class="flex flex-col items-center">
|
|
||||||
<WrenchScrewdriverIcon
|
|
||||||
class="h-12 w-12 text-blue-600"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<div class="mt-3 text-center sm:mt-5">
|
|
||||||
<h1 class="text-3xl font-semibold font-display leading-6 text-zinc-100">
|
|
||||||
Under construction
|
|
||||||
</h1>
|
|
||||||
<div class="mt-4">
|
|
||||||
<p class="text-sm text-zinc-400 max-w-lg">
|
|
||||||
This page hasn't been implemented yet.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import {
|
|
||||||
WrenchScrewdriverIcon,
|
|
||||||
} from "@heroicons/vue/20/solid";
|
|
||||||
</script>
|
|
||||||
16
main/pages/news/index.vue
Normal file
16
main/pages/news/index.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mx-auto flex flex-col items-center gap-y-4 max-w-2xl py-32 sm:py-48 lg:py-56">
|
||||||
|
<div>
|
||||||
|
<Wordmark />
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="text-balance text-4xl font-bold font-display tracking-tight text-zinc-100 sm:text-6xl">
|
||||||
|
Under construction
|
||||||
|
</h1>
|
||||||
|
<p class="mt-6 text-lg leading-8 text-zinc-400">
|
||||||
|
Yes, we know. We're working on it <a class="text-white" target="_blank"
|
||||||
|
href="https://github.com/Drop-OSS/drop-app/issues/52">here.</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@ -1,23 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="grow w-full h-full flex items-center justify-center">
|
|
||||||
<div class="flex flex-col items-center">
|
|
||||||
<WrenchScrewdriverIcon
|
|
||||||
class="h-12 w-12 text-blue-600"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<div class="mt-3 text-center sm:mt-5">
|
|
||||||
<h1 class="text-3xl font-semibold font-display leading-6 text-zinc-100">
|
|
||||||
Under construction
|
|
||||||
</h1>
|
|
||||||
<div class="mt-4">
|
|
||||||
<p class="text-sm text-zinc-400 max-w-lg">
|
|
||||||
This page hasn't been implemented yet.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { WrenchScrewdriverIcon } from "@heroicons/vue/20/solid";
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@ -1284,7 +1284,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "drop-app"
|
name = "drop-app"
|
||||||
version = "0.3.3"
|
version = "0.3.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-instant-full",
|
"atomic-instant-full",
|
||||||
"bitcode",
|
"bitcode",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "drop-app"
|
name = "drop-app"
|
||||||
version = "0.3.3"
|
version = "0.3.2"
|
||||||
description = "The client application for the open-source, self-hosted game distribution platform Drop"
|
description = "The client application for the open-source, self-hosted game distribution platform Drop"
|
||||||
authors = ["Drop OSS"]
|
authors = ["Drop OSS"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|||||||
@ -23,7 +23,6 @@ pub enum RemoteAccessError {
|
|||||||
ManifestDownloadFailed(StatusCode, String),
|
ManifestDownloadFailed(StatusCode, String),
|
||||||
OutOfSync,
|
OutOfSync,
|
||||||
Cache(std::io::Error),
|
Cache(std::io::Error),
|
||||||
CorruptedState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for RemoteAccessError {
|
impl Display for RemoteAccessError {
|
||||||
@ -82,10 +81,6 @@ impl Display for RemoteAccessError {
|
|||||||
"server's and client's time are out of sync. Please ensure they are within at least 30 seconds of each other"
|
"server's and client's time are out of sync. Please ensure they are within at least 30 seconds of each other"
|
||||||
),
|
),
|
||||||
RemoteAccessError::Cache(error) => write!(f, "Cache Error: {error}"),
|
RemoteAccessError::Cache(error) => write!(f, "Cache Error: {error}"),
|
||||||
RemoteAccessError::CorruptedState => write!(
|
|
||||||
f,
|
|
||||||
"Drop encountered a corrupted internal state. Please report this to the developers, with details of reproduction."
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,8 +22,8 @@ use crate::remote::requests::generate_url;
|
|||||||
use crate::remote::utils::{DROP_CLIENT_ASYNC, DROP_CLIENT_SYNC};
|
use crate::remote::utils::{DROP_CLIENT_ASYNC, DROP_CLIENT_SYNC};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use rayon::ThreadPoolBuilder;
|
use rayon::ThreadPoolBuilder;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::HashMap;
|
||||||
use std::fs::{create_dir_all, OpenOptions};
|
use std::fs::{OpenOptions, create_dir_all};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
@ -242,8 +242,12 @@ impl GameDownloadAgent {
|
|||||||
|
|
||||||
let mut buckets = Vec::new();
|
let mut buckets = Vec::new();
|
||||||
|
|
||||||
let mut current_buckets = HashMap::<String, DownloadBucket>::new();
|
let mut current_bucket = DownloadBucket {
|
||||||
let mut current_bucket_sizes = HashMap::<String, usize>::new();
|
game_id: game_id.clone(),
|
||||||
|
version: self.version.clone(),
|
||||||
|
drops: Vec::new(),
|
||||||
|
};
|
||||||
|
let mut current_bucket_size = 0;
|
||||||
|
|
||||||
for (raw_path, chunk) in manifest {
|
for (raw_path, chunk) in manifest {
|
||||||
let path = base_path.join(Path::new(&raw_path));
|
let path = base_path.join(Path::new(&raw_path));
|
||||||
@ -278,41 +282,28 @@ impl GameDownloadAgent {
|
|||||||
|
|
||||||
buckets.push(DownloadBucket {
|
buckets.push(DownloadBucket {
|
||||||
game_id: game_id.clone(),
|
game_id: game_id.clone(),
|
||||||
version: chunk.version_name.clone(),
|
version: self.version.clone(),
|
||||||
drops: vec![drop],
|
drops: vec![drop],
|
||||||
});
|
});
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_bucket_size = current_bucket_sizes
|
if current_bucket_size + *length >= TARGET_BUCKET_SIZE
|
||||||
.entry(chunk.version_name.clone())
|
|
||||||
.or_insert_with(|| 0);
|
|
||||||
let c_version_name = chunk.version_name.clone();
|
|
||||||
let c_game_id = game_id.clone();
|
|
||||||
let current_bucket = current_buckets
|
|
||||||
.entry(chunk.version_name.clone())
|
|
||||||
.or_insert_with(|| DownloadBucket {
|
|
||||||
game_id: c_game_id,
|
|
||||||
version: c_version_name,
|
|
||||||
drops: vec![],
|
|
||||||
});
|
|
||||||
|
|
||||||
if *current_bucket_size + length >= TARGET_BUCKET_SIZE
|
|
||||||
&& !current_bucket.drops.is_empty()
|
&& !current_bucket.drops.is_empty()
|
||||||
{
|
{
|
||||||
// Move current bucket into list and make a new one
|
// Move current bucket into list and make a new one
|
||||||
buckets.push(current_bucket.clone());
|
buckets.push(current_bucket);
|
||||||
*current_bucket = DownloadBucket {
|
current_bucket = DownloadBucket {
|
||||||
game_id: game_id.clone(),
|
game_id: game_id.clone(),
|
||||||
version: chunk.version_name.clone(),
|
version: self.version.clone(),
|
||||||
drops: vec![],
|
drops: Vec::new(),
|
||||||
};
|
};
|
||||||
*current_bucket_size = 0;
|
current_bucket_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_bucket.drops.push(drop);
|
current_bucket.drops.push(drop);
|
||||||
*current_bucket_size += *length;
|
current_bucket_size += *length;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@ -321,10 +312,8 @@ impl GameDownloadAgent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_, bucket) in current_buckets.into_iter() {
|
if !current_bucket.drops.is_empty() {
|
||||||
if !bucket.drops.is_empty() {
|
buckets.push(current_bucket);
|
||||||
buckets.push(bucket);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("buckets: {}", buckets.len());
|
info!("buckets: {}", buckets.len());
|
||||||
@ -359,46 +348,27 @@ impl GameDownloadAgent {
|
|||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let buckets = self.buckets.lock().unwrap();
|
|
||||||
|
|
||||||
let mut download_contexts = HashMap::<String, DownloadContext>::new();
|
|
||||||
|
|
||||||
let versions = buckets
|
|
||||||
.iter()
|
|
||||||
.map(|e| &e.version)
|
|
||||||
.collect::<HashSet<_>>()
|
|
||||||
.into_iter().cloned()
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
|
|
||||||
info!("downloading across these versions: {versions:?}");
|
|
||||||
|
|
||||||
let completed_contexts = Arc::new(boxcar::Vec::new());
|
let completed_contexts = Arc::new(boxcar::Vec::new());
|
||||||
let completed_indexes_loop_arc = completed_contexts.clone();
|
let completed_indexes_loop_arc = completed_contexts.clone();
|
||||||
|
|
||||||
for version in versions {
|
let download_context = DROP_CLIENT_SYNC
|
||||||
let download_context = DROP_CLIENT_SYNC
|
.post(generate_url(&["/api/v2/client/context"], &[]).unwrap())
|
||||||
.post(generate_url(&["/api/v2/client/context"], &[]).unwrap())
|
.json(&ManifestBody {
|
||||||
.json(&ManifestBody {
|
game: self.id.clone(),
|
||||||
game: self.id.clone(),
|
version: self.version.clone(),
|
||||||
version: version.clone(),
|
})
|
||||||
})
|
.header("Authorization", generate_authorization_header())
|
||||||
.header("Authorization", generate_authorization_header())
|
.send()?;
|
||||||
.send()?;
|
|
||||||
|
|
||||||
if download_context.status() != 200 {
|
if download_context.status() != 200 {
|
||||||
return Err(RemoteAccessError::InvalidResponse(download_context.json()?));
|
return Err(RemoteAccessError::InvalidResponse(download_context.json()?));
|
||||||
}
|
|
||||||
|
|
||||||
let download_context = download_context.json::<DownloadContext>()?;
|
|
||||||
info!(
|
|
||||||
"download context: ({}) {}",
|
|
||||||
&version, download_context.context
|
|
||||||
);
|
|
||||||
download_contexts.insert(version, download_context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let download_contexts = &download_contexts;
|
let download_context = &download_context.json::<DownloadContext>()?;
|
||||||
|
|
||||||
|
info!("download context: {}", download_context.context);
|
||||||
|
|
||||||
|
let buckets = self.buckets.lock().unwrap();
|
||||||
pool.scope(|scope| {
|
pool.scope(|scope| {
|
||||||
let context_map = self.context_map.lock().unwrap();
|
let context_map = self.context_map.lock().unwrap();
|
||||||
for (index, bucket) in buckets.iter().enumerate() {
|
for (index, bucket) in buckets.iter().enumerate() {
|
||||||
@ -430,11 +400,6 @@ impl GameDownloadAgent {
|
|||||||
|
|
||||||
let sender = self.sender.clone();
|
let sender = self.sender.clone();
|
||||||
|
|
||||||
let download_context = download_contexts
|
|
||||||
.get(&bucket.version)
|
|
||||||
.ok_or(RemoteAccessError::CorruptedState)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
scope.spawn(move |_| {
|
scope.spawn(move |_| {
|
||||||
// 3 attempts
|
// 3 attempts
|
||||||
for i in 0..RETRY_COUNT {
|
for i in 0..RETRY_COUNT {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone)]
|
||||||
// Drops go in buckets
|
// Drops go in buckets
|
||||||
pub struct DownloadDrop {
|
pub struct DownloadDrop {
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
@ -14,7 +14,7 @@ pub struct DownloadDrop {
|
|||||||
pub permissions: u32,
|
pub permissions: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DownloadBucket {
|
pub struct DownloadBucket {
|
||||||
pub game_id: String,
|
pub game_id: String,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
|
|||||||
@ -64,7 +64,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::panic::PanicHookInfo;
|
use std::panic::PanicHookInfo;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
@ -109,7 +109,7 @@ fn create_new_compat_info() -> Option<CompatInfo> {
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
return None;
|
return None;
|
||||||
|
|
||||||
let has_umu_installed = UMU_LAUNCHER_EXECUTABLE.is_some();
|
let has_umu_installed = *UMU_LAUNCHER_EXECUTABLE == PathBuf::new();
|
||||||
Some(CompatInfo {
|
Some(CompatInfo {
|
||||||
umu_installed: has_umu_installed,
|
umu_installed: has_umu_installed,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use std::{
|
|||||||
sync::LazyLock,
|
sync::LazyLock,
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::{debug, info};
|
use log::debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AppState,
|
AppState,
|
||||||
@ -31,29 +31,29 @@ impl ProcessHandler for NativeGameLauncher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static UMU_LAUNCHER_EXECUTABLE: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
|
pub static UMU_LAUNCHER_EXECUTABLE: LazyLock<PathBuf> = LazyLock::new(|| {
|
||||||
let x = get_umu_executable();
|
let x = get_umu_executable();
|
||||||
info!("{:?}", &x);
|
println!("{:?}", &x);
|
||||||
x
|
x
|
||||||
});
|
});
|
||||||
const UMU_BASE_LAUNCHER_EXECUTABLE: &str = "umu-run";
|
const UMU_BASE_LAUNCHER_EXECUTABLE: &str = "umu-run";
|
||||||
const UMU_INSTALL_DIRS: [&str; 4] = ["/app/share", "/use/local/share", "/usr/share", "/opt"];
|
const UMU_INSTALL_DIRS: [&str; 4] = ["/app/share", "/use/local/share", "/usr/share", "/opt"];
|
||||||
|
|
||||||
fn get_umu_executable() -> Option<PathBuf> {
|
fn get_umu_executable() -> PathBuf {
|
||||||
if check_executable_exists(UMU_BASE_LAUNCHER_EXECUTABLE) {
|
if check_executable_exists(UMU_BASE_LAUNCHER_EXECUTABLE) {
|
||||||
return Some(PathBuf::from(UMU_BASE_LAUNCHER_EXECUTABLE));
|
return PathBuf::from(UMU_BASE_LAUNCHER_EXECUTABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
for dir in UMU_INSTALL_DIRS {
|
for dir in UMU_INSTALL_DIRS {
|
||||||
let p = PathBuf::from(dir).join(UMU_BASE_LAUNCHER_EXECUTABLE);
|
let p = PathBuf::from(dir).join(UMU_BASE_LAUNCHER_EXECUTABLE);
|
||||||
if check_executable_exists(&p) {
|
if check_executable_exists(&p) {
|
||||||
return Some(p);
|
return p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
PathBuf::new()
|
||||||
}
|
}
|
||||||
fn check_executable_exists<P: AsRef<OsStr>>(exec: P) -> bool {
|
fn check_executable_exists<P: AsRef<OsStr>>(exec: P) -> bool {
|
||||||
let has_umu_installed = Command::new(exec).stdout(Stdio::null()).output();
|
let has_umu_installed = Command::new(exec).stdout(Stdio::null()).spawn();
|
||||||
has_umu_installed.is_ok()
|
has_umu_installed.is_ok()
|
||||||
}
|
}
|
||||||
pub struct UMULauncher;
|
pub struct UMULauncher;
|
||||||
@ -79,7 +79,7 @@ impl ProcessHandler for UMULauncher {
|
|||||||
};
|
};
|
||||||
format!(
|
format!(
|
||||||
"GAMEID={game_id} {umu:?} \"{launch}\" {args}",
|
"GAMEID={game_id} {umu:?} \"{launch}\" {args}",
|
||||||
umu = UMU_LAUNCHER_EXECUTABLE.as_ref().unwrap(),
|
umu = &*UMU_LAUNCHER_EXECUTABLE,
|
||||||
launch = launch_command,
|
launch = launch_command,
|
||||||
args = args.join(" ")
|
args = args.join(" ")
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2.0.0",
|
"$schema": "https://schema.tauri.app/config/2.0.0",
|
||||||
"productName": "Drop Desktop Client",
|
"productName": "Drop Desktop Client",
|
||||||
"version": "0.3.3",
|
"version": "0.3.2",
|
||||||
"identifier": "dev.drop.client",
|
"identifier": "dev.drop.client",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "yarn --cwd main dev --port 1432",
|
"beforeDevCommand": "yarn --cwd main dev --port 1432",
|
||||||
|
|||||||
Reference in New Issue
Block a user