Fix overrides not being downloaded

This commit is contained in:
Wyvest 2026-02-23 05:56:01 -05:00
parent b5d6beada0
commit e745c75cc8
6 changed files with 122 additions and 46 deletions

View file

@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::path::PathBuf;
use onelauncher_core::api::cluster::dao::ClusterId;
use onelauncher_core::api::packages::modpack::data::ModpackArchive;
@ -21,6 +22,12 @@ pub trait OneClientApi {
#[taurpc(alias = "getVersions")]
async fn get_versions() -> LauncherResult<OnlineClusterManifest>;
#[taurpc(alias = "extractBundleOverrides")]
async fn extract_bundle_overrides(
bundle_path: PathBuf,
cluster_id: ClusterId,
) -> LauncherResult<()>;
#[taurpc(alias = "checkForUpdate")]
async fn check_for_update<R: Runtime>(
app_handle: AppHandle<R>,
@ -82,6 +89,30 @@ impl OneClientApi for OneClientApiImpl {
get_data_storage_versions().await
}
async fn extract_bundle_overrides(
self,
bundle_path: PathBuf,
cluster_id: ClusterId,
) -> LauncherResult<()> {
let cluster = onelauncher_core::api::cluster::dao::get_cluster_by_id(cluster_id)
.await?
.ok_or_else(|| {
onelauncher_core::error::LauncherError::from(anyhow::anyhow!(
"cluster with id {} not found",
cluster_id
))
})?;
onelauncher_core::api::packages::modpack::mrpack::copy_overrides_folder(
&cluster,
&bundle_path,
&None,
)
.await?;
Ok(())
}
async fn check_for_update<R: Runtime>(
self,
app_handle: AppHandle<R>,

View file

@ -303,25 +303,12 @@ export type VersionType =
*/
"old_beta"
const ARGS_MAP = { 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'oneclient':'{"getBundlesFor":["cluster_id"],"checkForUpdate":[],"installUpdate":[],"getVersions":[],"getClustersGroupedByMajor":[]}', 'debug':'{"getLocale":[],"getType":[],"getArch":[],"getFamily":[],"getOsVersion":[],"getPackageVersion":[],"getPlatform":[],"getBuildTimestamp":[],"isInDev":[],"getGitCommitHash":[],"openDevTools":[]}', 'core':'{"getWorlds":["id"],"setClusterStage":["id","stage"],"getDefaultUser":["fallback"],"setDefaultUser":["uuid"],"getClusterById":["id"],"getLogByName":["id","name"],"removePackage":["cluster_id","package_hash"],"getRunningProcessesByClusterId":["cluster_id"],"getPackage":["provider","slug"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"isClusterRunning":["cluster_id"],"getUser":["uuid"],"setDiscordRPCMessage":["message"],"getRunningProcesses":[],"killProcess":["pid"],"getLoadersForVersion":["mc_version"],"searchPackages":["provider","query"],"updateClusterById":["id","request"],"getClusters":[],"getGameVersions":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"launchCluster":["id","uuid","search_for_java"],"installModpack":["modpack","cluster_id"],"changeSkin":["access_token","skin_url","skin_variant"],"readSettings":[],"refreshAccount":["uuid"],"getMultiplePackages":["provider","slugs"],"openMsaLogin":[],"updateClusterProfile":["name","profile"],"open":["input"],"getUsers":[],"getScreenshots":["id"],"getProfileOrDefault":["name"],"removeUser":["uuid"],"getGlobalProfile":[],"createCluster":["options"],"getUsersFromAuthor":["provider","author"],"fetchMinecraftProfile":["uuid"],"removeCape":["access_token"],"convertUsernameUUID":["username_uuid"],"createSettingsProfile":["name"],"getLogs":["id"],"getPackageBody":["provider","body"],"refreshAccounts":[],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getLinkedPackages":["cluster_id"],"changeCape":["access_token","cape_uuid"],"writeSettings":["setting"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"fetchLoggedInProfile":["access_token"],"removeCluster":["id"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}' }
export type Router = { 'debug': { openDevTools: () => Promise<void>,
isInDev: () => Promise<boolean>,
getArch: () => Promise<string>,
getFamily: () => Promise<string>,
getLocale: () => Promise<string | null>,
getType: () => Promise<string>,
getPlatform: () => Promise<string>,
getOsVersion: () => Promise<string>,
getGitCommitHash: () => Promise<string>,
getBuildTimestamp: () => Promise<string>,
getPackageVersion: () => Promise<string> },
const ARGS_MAP = { 'core':'{"removeUser":["uuid"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"changeCape":["access_token","cape_uuid"],"getScreenshots":["id"],"setClusterStage":["id","stage"],"getRunningProcesses":[],"convertUsernameUUID":["username_uuid"],"getClusterById":["id"],"getRunningProcessesByClusterId":["cluster_id"],"getLoadersForVersion":["mc_version"],"getPackageBody":["provider","body"],"open":["input"],"getMultiplePackages":["provider","slugs"],"isClusterRunning":["cluster_id"],"removeCape":["access_token"],"getGlobalProfile":[],"setDiscordRPCMessage":["message"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"fetchLoggedInProfile":["access_token"],"readSettings":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"killProcess":["pid"],"refreshAccounts":[],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"getLogs":["id"],"updateClusterProfile":["name","profile"],"searchPackages":["provider","query"],"updateClusterById":["id","request"],"getWorlds":["id"],"getProfileOrDefault":["name"],"refreshAccount":["uuid"],"getUsers":[],"getLogByName":["id","name"],"removeCluster":["id"],"launchCluster":["id","uuid","search_for_java"],"openMsaLogin":[],"getPackage":["provider","slug"],"writeSettings":["setting"],"getClusters":[],"getUsersFromAuthor":["provider","author"],"getGameVersions":[],"getUser":["uuid"],"getLinkedPackages":["cluster_id"],"removePackage":["cluster_id","package_hash"],"createCluster":["options"],"getDefaultUser":["fallback"],"setDefaultUser":["uuid"],"installModpack":["modpack","cluster_id"],"fetchMinecraftProfile":["uuid"],"createSettingsProfile":["name"],"changeSkin":["access_token","skin_url","skin_variant"]}', 'events':'{"process":["event"],"ingress":["event"],"message":["event"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'debug':'{"getLocale":[],"getPlatform":[],"isInDev":[],"getGitCommitHash":[],"getOsVersion":[],"getFamily":[],"getBuildTimestamp":[],"getPackageVersion":[],"openDevTools":[],"getType":[],"getArch":[]}', 'oneclient':'{"getVersions":[],"installUpdate":[],"getClustersGroupedByMajor":[],"extractBundleOverrides":["bundle_path","cluster_id"],"checkForUpdate":[],"getBundlesFor":["cluster_id"]}' }
export type Router = { 'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> },
'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'oneclient': { getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]>,
getVersions: () => Promise<OnlineClusterManifest>,
checkForUpdate: () => Promise<Update | null>,
installUpdate: () => Promise<null> },
'core': { getClusters: () => Promise<ClusterModel[]>,
getClusterById: (id: number) => Promise<ClusterModel | null>,
removeCluster: (id: number) => Promise<null>,
@ -373,9 +360,23 @@ removeCape: (accessToken: string) => Promise<MojangFullPlayerProfile>,
convertUsernameUUID: (usernameUuid: string) => Promise<MowojangProfile>,
setDiscordRPCMessage: (message: string) => Promise<null>,
open: (input: string) => Promise<null> },
'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> } };
'oneclient': { getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]>,
getVersions: () => Promise<OnlineClusterManifest>,
extractBundleOverrides: (bundlePath: string, clusterId: number) => Promise<null>,
checkForUpdate: () => Promise<Update | null>,
installUpdate: () => Promise<null> },
'debug': { openDevTools: () => Promise<void>,
isInDev: () => Promise<boolean>,
getArch: () => Promise<string>,
getFamily: () => Promise<string>,
getLocale: () => Promise<string | null>,
getType: () => Promise<string>,
getPlatform: () => Promise<string>,
getOsVersion: () => Promise<string>,
getGitCommitHash: () => Promise<string>,
getBuildTimestamp: () => Promise<string>,
getPackageVersion: () => Promise<string> } };
export type { InferCommandOutput }

View file

@ -1,11 +1,11 @@
import type { ExternalPackage, ManagedVersionDependency, ModpackFile, Provider } from '@/bindings.gen';
import type { ExternalPackage, ManagedVersionDependency, ModpackArchive, ModpackFile, Provider } from '@/bindings.gen';
import { getModMetaDataName, Overlay } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { useCommandMut } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';
import { useNavigate } from '@tanstack/react-router';
import { useEffect, useImperativeHandle, useState } from 'react';
import { useEffect, useImperativeHandle, useRef, useState } from 'react';
import { DialogTrigger } from 'react-aria-components';
export interface DownloadModsRef {
@ -36,7 +36,7 @@ export function isManagedMod(mod: ModData): mod is ManagedModData {
return mod.managed === true;
}
export function DownloadMods({ modsPerCluster, ref }: { modsPerCluster: Record<string, Array<ModpackFile>>; ref: React.Ref<DownloadModsRef> }) {
export function DownloadMods({ modsPerCluster, bundlesPerCluster, ref }: { modsPerCluster: Record<string, Array<ModpackFile>>; bundlesPerCluster?: Record<string, Array<ModpackArchive>>; ref: React.Ref<DownloadModsRef> }) {
const navigate = useNavigate();
const [isOpen, setOpen] = useState<boolean>(false);
const [mods, setMods] = useState<ModDataArray>([]);
@ -89,7 +89,7 @@ export function DownloadMods({ modsPerCluster, ref }: { modsPerCluster: Record<s
<Button className="mb-4" isDisabled={mods.length === 0} onPress={() => setOpen(prev => !prev)}>Download Mods</Button>
<Overlay isDismissable={false} isOpen={isOpen}>
<DownloadingMods mods={mods} nextPath={nextPath} setOpen={setOpen} />
<DownloadingMods bundlesPerCluster={bundlesPerCluster} mods={mods} nextPath={nextPath} setOpen={setOpen} />
</Overlay>
</DialogTrigger>
);
@ -106,10 +106,12 @@ function downloadModsParallel(items: ModDataArray, limit: number, fn: (mod: ModD
return Promise.all(workers);
}
function DownloadingMods({ mods, setOpen, nextPath }: { mods: ModDataArray; setOpen: React.Dispatch<React.SetStateAction<boolean>>; nextPath: string }) {
function DownloadingMods({ mods, setOpen, nextPath, bundlesPerCluster }: { mods: ModDataArray; setOpen: React.Dispatch<React.SetStateAction<boolean>>; nextPath: string; bundlesPerCluster?: Record<string, Array<ModpackArchive>> }) {
const navigate = useNavigate();
const [downloadedMods, setDownloadedMods] = useState(0);
const [modName, setModName] = useState<string | null>(null);
const [allDone, setAllDone] = useState(false);
const hasStarted = useRef(false);
const download = useCommandMut(async (mod: ModData) => {
if (isManagedMod(mod)) {
if (mod.dependencies.length > 0)
@ -133,37 +135,72 @@ function DownloadingMods({ mods, setOpen, nextPath }: { mods: ModDataArray; setO
let useParallelModDownloading = setting('parallel_mod_downloading');
useEffect(() => {
if (hasStarted.current)
return;
hasStarted.current = true;
const downloadAll = async () => {
if (useParallelModDownloading)
await downloadModsParallel(mods, 10, async (mod) => {
setModName(mod.name);
try {
await download.mutateAsync(mod);
try {
if (useParallelModDownloading)
await downloadModsParallel(mods, 10, async (mod) => {
setModName(mod.name);
try {
await download.mutateAsync(mod);
}
catch (e) {
console.warn(`Failed to download mod ${mod.name}:`, e);
}
finally {
setDownloadedMods(prev => prev + 1);
}
}); else
for (const mod of mods) {
setModName(mod.name);
try {
await download.mutateAsync(mod);
}
catch (e) {
console.warn(`Failed to download mod ${mod.name}:`, e);
}
finally {
setDownloadedMods(prev => prev + 1);
}
}
finally {
setDownloadedMods(prev => prev + 1);
}
}); else
for (const mod of mods) {
setModName(mod.name);
try {
await download.mutateAsync(mod);
}
finally {
setDownloadedMods(prev => prev + 1);
// Extract overrides from enabled bundles after all mods are downloaded
if (bundlesPerCluster) {
setModName('Extracting overrides...');
for (const [clusterId, bundles] of Object.entries(bundlesPerCluster)) {
for (const bundle of bundles) {
if (bundle.manifest.enabled) {
try {
await bindings.oneclient.extractBundleOverrides(bundle.path, Number(clusterId));
}
catch (e) {
console.error(`Failed to extract overrides for bundle ${bundle.manifest.name}:`, e);
}
}
}
}
}
}
catch (e) {
console.error('Error during mod download:', e);
}
finally {
setAllDone(true);
}
};
downloadAll();
}, [mods]);
useEffect(() => {
if (downloadedMods >= mods.length) {
if (allDone) {
setOpen(false);
navigate({ to: nextPath });
}
}, [downloadedMods, mods, navigate, nextPath, setOpen]);
}, [allDone, navigate, nextPath, setOpen]);
return (
<Overlay.Dialog isDismissable={false}>

View file

@ -61,6 +61,13 @@ function RouteComponent() {
}, {} as Record<string, Array<ModpackFile>>),
);
const bundlesPerCluster = useMemo(() => {
return clusters.reduce((acc, cluster, i) => {
acc[cluster.id] = bundleQueries[i];
return acc;
}, {} as Record<string, Array<ModpackArchive>>);
}, [clusters, bundleQueries]);
const downloadModsRef = useRef<DownloadModsRef>(null);
return (
@ -82,7 +89,7 @@ function RouteComponent() {
</div>
<div className="hidden">
<DownloadMods modsPerCluster={modsPerCluster} ref={downloadModsRef} />
<DownloadMods bundlesPerCluster={bundlesPerCluster} modsPerCluster={modsPerCluster} ref={downloadModsRef} />
</div>
</div>
</OverlayScrollbarsComponent>

View file

@ -15,7 +15,7 @@ use crate::error::LauncherResult;
use crate::store::ingress::SubIngress;
pub mod data;
mod mrpack;
pub mod mrpack;
mod polymrpack;
#[cfg(test)]

View file

@ -256,7 +256,7 @@ pub(super) async fn download_and_link_packages(
Ok(())
}
pub(super) async fn copy_overrides_folder(
pub async fn copy_overrides_folder(
cluster: &clusters::Model,
archive_path: &PathBuf,
_ingress: &Option<SubIngress<'_>>,