Compare commits

...

5 Commits

Author SHA1 Message Date
DecDuck
27a14d1c32 feat: update ci 2025-12-03 14:32:10 +11:00
DecDuck
a1b98efe3d feat: re-enable multipart downloads 2025-12-03 13:56:13 +11:00
DecDuck
510013d4fa feat: torrential setup 2025-12-03 11:54:20 +11:00
DecDuck
fe4f295fcd fix: style and capability 2025-12-01 08:42:26 +11:00
DecDuck
1f809063ef fix: fetching versions from server 2025-12-01 08:35:43 +11:00
14 changed files with 40 additions and 42 deletions

View File

@@ -120,8 +120,8 @@ jobs:
APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
NO_STRIP: true
with:
tagName: v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version.
releaseName: "Auto-release v__VERSION__"
tagName: ${{ github.ref_name }} # the action automatically replaces \_\_VERSION\_\_ with the app version.
releaseName: "Auto-release ${{ github.ref_name }}"
releaseBody: "See the assets to download this version and install. This release was created automatically."
releaseDraft: false
prerelease: true

View File

@@ -38,7 +38,7 @@
>
<dt>
<DisclosureButton
class="flex w-full items-center justify-between text-left text-gray-900 dark:text-white"
class="flex w-full items-center justify-between text-left text-white"
>
<span class="text-sm font-semibold font-display">{{
nav.name
@@ -73,9 +73,9 @@
alt=""
/>
</div>
<div class="truncate inline-flex items-center gap-x-2">
<div class="flex flex-col gap-x-2">
<p
class="text-sm whitespace-nowrap font-display font-semibold"
class="truncate text-sm whitespace-nowrap font-display font-semibold"
>
{{ item.label }}
</p>

2
src-tauri/Cargo.lock generated
View File

@@ -1339,7 +1339,7 @@ dependencies = [
[[package]]
name = "drop-app"
version = "0.3.4"
version = "0.0.0"
dependencies = [
"atomic-instant-full",
"bitcode",

View File

@@ -1,6 +1,5 @@
[package]
name = "drop-app"
version = "0.3.4"
description = "The client application for the open-source, self-hosted game distribution platform Drop"
authors = ["Drop OSS"]
edition = "2024"

View File

@@ -57,7 +57,7 @@ impl Display for ApplicationDownloadError {
format_size(*required, BINARY),
format_size(*available, BINARY),
),
ApplicationDownloadError::Communication(error) => write!(f, "{error}"),
ApplicationDownloadError::Communication(error) => write!(f, "{error:?}"),
ApplicationDownloadError::Lock => write!(
f,
"failed to acquire lock. Something has gone very wrong internally. Please restart the application"

View File

@@ -36,7 +36,7 @@ use crate::downloads::validate::validate_game_chunk;
use crate::library::{on_game_complete, push_game_update, set_partially_installed};
use crate::state::GameStatusManager;
use super::download_logic::download_game_bucket;
use super::download_logic::download_game_chunk;
use super::drop_data::DropData;
static RETRY_COUNT: usize = 3;
@@ -283,6 +283,7 @@ impl GameDownloadAgent {
for (index, length) in chunk.lengths.iter().enumerate() {
let drop = DownloadDrop {
filename: raw_path.to_string(),
id: chunk.ids[index].clone(),
start: file_running_offset,
length: *length,
checksum: chunk.checksums[index].clone(),
@@ -465,7 +466,7 @@ impl GameDownloadAgent {
// 3 attempts
for i in 0..RETRY_COUNT {
let loop_progress_handle = progress_handle.clone();
match download_game_bucket(
match download_game_chunk(
&bucket,
download_context,
&self.control_flag,

View File

@@ -3,7 +3,7 @@ use std::io::Read;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::sync::Arc;
use std::time::Instant;
use std::time::{Duration, Instant};
use std::{
fs::{File, OpenOptions},
io::{self, BufWriter, Seek, SeekFrom, Write},
@@ -105,7 +105,8 @@ impl<'a> DropDownloadPipeline<'a, Response, File> {
})
}
fn copy(&mut self) -> Result<bool, io::Error> {
fn copy(&mut self) -> Result<(bool, usize), io::Error> {
let mut total_copied = 0;
let mut copy_buffer = [0u8; MAX_PACKET_LENGTH];
for (index, drop) in self.drops.iter().enumerate() {
let destination = self
@@ -127,13 +128,14 @@ impl<'a> DropDownloadPipeline<'a, Response, File> {
})?;
remaining -= size;
last_bump += size;
// total_copied += size;
destination.write_all(&copy_buffer[0..size])?;
if last_bump > BUMP_SIZE {
last_bump -= BUMP_SIZE;
if self.control_flag.get() == DownloadThreadControlFlag::Stop {
return Ok(false);
return Ok((false, 0));
}
}
@@ -143,11 +145,11 @@ impl<'a> DropDownloadPipeline<'a, Response, File> {
}
if self.control_flag.get() == DownloadThreadControlFlag::Stop {
return Ok(false);
return Ok((false, 0));
}
}
Ok(true)
Ok((true, total_copied))
}
#[allow(dead_code)]
@@ -167,7 +169,7 @@ impl<'a> DropDownloadPipeline<'a, Response, File> {
}
}
pub fn download_game_bucket(
pub fn download_game_chunk(
bucket: &DownloadBucket,
ctx: &DownloadContext,
control_flag: &DownloadThreadControl,
@@ -183,14 +185,13 @@ pub fn download_game_bucket(
let header = generate_authorization_header();
let url = generate_url(&["/api/v2/client/chunk"], &[])
.map_err(ApplicationDownloadError::Communication)?;
let body = ChunkBody::create(ctx, &bucket.drops);
let mut bits = vec!["/api/v1/depot/", &bucket.game_id, &bucket.version];
bits.extend(bucket.drops.iter().map(|v| v.id.as_str()));
let url = generate_url(&bits, &[]).unwrap();
let response = DROP_CLIENT_SYNC
.post(url)
.json(&body)
.get(url)
//.json(&body)
.header("Authorization", header)
.send()
.map_err(|e| ApplicationDownloadError::Communication(e.into()))?;
@@ -210,7 +211,6 @@ pub fn download_game_bucket(
RemoteAccessError::UnparseableResponse(raw_res),
));
}
let lengths = response
.headers()
.get("Content-Lengths")
@@ -232,9 +232,7 @@ pub fn download_game_bucket(
RemoteAccessError::InvalidResponse(DropServerError {
status_code: 400,
status_message: "Server Error".to_owned(),
message: format!(
"invalid number of Content-Lengths recieved: {i}, {lengths}"
),
message: format!("invalid number of Content-Lengths recieved: {i}, {lengths}"),
}),
));
};
@@ -264,7 +262,7 @@ pub fn download_game_bucket(
DropDownloadPipeline::new(response, bucket.drops.clone(), control_flag, progress)
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
let completed = pipeline
let (completed, _) = pipeline
.copy()
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
if !completed {

View File

@@ -6,6 +6,7 @@ use std::path::PathBuf;
// Drops go in buckets
pub struct DownloadDrop {
pub index: usize,
pub id: String,
pub filename: String,
pub path: PathBuf,
pub start: usize,

View File

@@ -10,7 +10,7 @@ use download_manager::{
progress_object::ProgressHandle,
},
};
use log::debug;
use log::{debug, info};
use md5::Context;
use crate::downloads::manifest::DropValidateContext;
@@ -52,6 +52,7 @@ pub fn validate_game_chunk(
let res = hex::encode(hasher.finalize().0);
if res != ctx.checksum {
info!("{} doesn't match", ctx.path.display());
return Ok(false);
}

View File

@@ -9,7 +9,7 @@ use remote::{
utils::DROP_CLIENT_SYNC,
};
use serde::{Deserialize, Serialize};
use std::fs::remove_dir_all;
use std::fs::{exists, remove_dir_all};
use std::thread::spawn;
use tauri::AppHandle;
use utils::app_emit;
@@ -154,7 +154,7 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
let app_handle = app_handle.clone();
spawn(move || {
if let Err(e) = remove_dir_all(install_dir) {
if exists(install_dir.clone()).unwrap_or(false) && let Err(e) = remove_dir_all(install_dir) {
error!("{e}");
} else {
let mut db_handle = borrow_db_mut_checked();

View File

@@ -106,8 +106,8 @@ pub fn auth_initiate_logic(mode: String) -> Result<String, RemoteAccessError> {
name: format!("{} (Desktop)", hostname.display()),
platform: env::consts::OS.to_string(),
capabilities: HashMap::from([
("PeerAPI".to_owned(), CapabilityConfiguration {}),
("CloudSaves".to_owned(), CapabilityConfiguration {}),
("peerAPI".to_owned(), CapabilityConfiguration {}),
("cloudSaves".to_owned(), CapabilityConfiguration {}),
]),
mode,
};

View File

@@ -1,4 +1,6 @@
use database::{DB, interface::DatabaseImpls};
use log::info;
use url::Url;
use crate::{
@@ -9,10 +11,10 @@ pub fn generate_url<T: AsRef<str>>(
path_components: &[T],
query: &[(T, T)],
) -> Result<Url, RemoteAccessError> {
let mut base_url = DB.fetch_base_url();
for endpoint in path_components {
base_url = base_url.join(endpoint.as_ref())?;
}
let components = path_components.iter().map(|v| v.as_ref()).map(|v| v.trim_matches('/')).collect::<Vec<&str>>();
let mut base_url = DB
.fetch_base_url()
.join(&components.join("/"))?;
{
let mut queries = base_url.query_pairs_mut();
for (param, val) in query {

View File

@@ -1,7 +1,7 @@
use std::{
fs::{self, File},
io::Read,
sync::LazyLock,
sync::LazyLock, time::Duration,
};
use database::db::DATA_ROOT_DIR;
@@ -91,6 +91,7 @@ pub fn get_client_sync() -> reqwest::blocking::Client {
}
client
.use_rustls_tls()
.connect_timeout(Duration::from_millis(1500))
.build()
.expect("Failed to build synchronous client")
}

View File

@@ -224,11 +224,6 @@ pub async fn fetch_game_version_options_logic(
return Err(RemoteAccessError::InvalidResponse(err));
}
let raw = response.text().await?;
info!("{}", raw);
return Err(RemoteAccessError::CorruptedState);
let data: Vec<GameVersion> = response.json().await?;
let state_lock = state.lock();