Skip to content

Commit

Permalink
Simplify download logic by omitting callback
Browse files Browse the repository at this point in the history
  • Loading branch information
VilppeRiskidev committed Jan 16, 2025
1 parent 24dcf1a commit 8080030
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 94 deletions.
79 changes: 44 additions & 35 deletions src/components/views/DownloadModModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,31 +60,30 @@ import ProfileModList from '../../r2mm/mods/ProfileModList';
store: Store<any>
): Promise<void> {
return new Promise(async (resolve, reject) => {
const tsMod = combo.getMod();
const tsVersion = combo.getVersion();
const assignId = await store.dispatch(
'download/addDownload',
[`${tsMod.getName()} (${tsVersion.getVersionNumber().toString()})`]
[`${combo.getMod().getName()} (${combo.getVersion().getVersionNumber().toString()})`]
);
setTimeout(() => {
ThunderstoreDownloaderProvider.instance.download(profile.asImmutableProfile(), tsMod, tsVersion, ignoreCache, (progress: number, modName: string, status: number, err: R2Error | null) => {
try {
if (status === StatusEnum.FAILURE) {
store.commit('download/updateDownload', {assignId, failed: true});
if (err !== null) {
DownloadMixin.addSolutionsToError(err);
return reject(err);
setTimeout(async () => {
try {
const downloadedMods = await ThunderstoreDownloaderProvider.instance.download(profile.asImmutableProfile(), combo, ignoreCache, (progress: number, modName: string, status: number, err: R2Error | null) => {
try {
if (status === StatusEnum.FAILURE) {
store.commit('download/updateDownload', {assignId, failed: true});
if (err !== null) {
DownloadMixin.addSolutionsToError(err);
return reject(err);
}
} else if (status === StatusEnum.PENDING) {
store.commit('download/updateDownload', {assignId, progress, modName});
}
} else if (status === StatusEnum.PENDING) {
store.commit('download/updateDownload', {assignId, progress, modName});
} catch (e) {
return reject(e);
}
} catch (e) {
return reject(e);
}
}, async (downloadedMods: ThunderstoreCombo[]) => {
ProfileModList.requestLock(async () => {
});
await ProfileModList.requestLock(async () => {
for (const combo of downloadedMods) {
try {
await DownloadModModal.installModAfterDownload(profile, combo.getMod(), combo.getVersion());
Expand All @@ -103,7 +102,9 @@ import ProfileModList from '../../r2mm/mods/ProfileModList';
}
return resolve();
});
});
} catch (e) {
store.commit('error/handleError', R2Error.fromThrownValue(e));
}
}, 1);
});
}
Expand All @@ -117,26 +118,34 @@ import ProfileModList from '../../r2mm/mods/ProfileModList';
);
this.$store.commit('download/setIsModProgressModalOpen', true);
setTimeout(() => {
ThunderstoreDownloaderProvider.instance.download(this.profile.asImmutableProfile(), tsMod, tsVersion, this.ignoreCache, (progress: number, modName: string, status: number, err: R2Error | null) => {
try {
if (status === StatusEnum.FAILURE) {
this.$store.commit('download/setIsModProgressModalOpen', false);
this.$store.commit('download/updateDownload', {assignId, failed: true});
if (err !== null) {
DownloadMixin.addSolutionsToError(err);
throw err;
const tsCombo = new ThunderstoreCombo();
tsCombo.setMod(tsMod);
tsCombo.setVersion(tsVersion);
setTimeout(async () => {
try {
const downloadedMods = await ThunderstoreDownloaderProvider.instance.download(this.profile.asImmutableProfile(), tsCombo, this.ignoreCache, (progress: number, modName: string, status: number, err: R2Error | null) => {
try {
if (status === StatusEnum.FAILURE) {
this.$store.commit('download/setIsModProgressModalOpen', false);
this.$store.commit('download/updateDownload', {assignId, failed: true});
if (err !== null) {
DownloadMixin.addSolutionsToError(err);
throw err;
}
} else if (status === StatusEnum.PENDING) {
this.$store.commit('download/updateDownload', {assignId, progress, modName});
}
} else if (status === StatusEnum.PENDING) {
this.$store.commit('download/updateDownload', {assignId, progress, modName});
} catch (e) {
this.$store.commit('error/handleError', R2Error.fromThrownValue(e));
}
} catch (e) {
this.$store.commit('error/handleError', R2Error.fromThrownValue(e));
}
}, async (downloadedMods) => {
});
await this.downloadCompletedCallback(downloadedMods);
this.$store.commit('download/setIsModProgressModalOpen', false);
});
} catch (e) {
this.$store.commit('error/handleError', R2Error.fromThrownValue(e));
}
}, 1);
}
Expand Down
20 changes: 10 additions & 10 deletions src/providers/ror2/downloading/ThunderstoreDownloaderProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,17 @@ export default abstract class ThunderstoreDownloaderProvider {
/**
* A top-level method to download the latest version of a mod including its dependencies.
*
* @param profile The profile the mod is downloaded for (needed to prevent dependencies from updating existing mods).
* @param mod The mod to be downloaded.
* @param modVersion The version of the mod to download.
* @param ignoreCache Download mod even if it already exists in the cache.
* @param callback Callback to show the current state of the downloads.
* @param completedCallback Callback to perform final actions against. Only called if {@param callback} has not returned a failed status.
* @param profile The profile the mod is downloaded for (needed to prevent dependencies from updating existing mods).
* @param combo The combo to be downloaded.
* @param ignoreCache Download mod even if it already exists in the cache.
* @param totalProgressCallback Callback to show the combined state of all the downloads.
*/
public abstract download(profile: ImmutableProfile, mod: ThunderstoreMod, modVersion: ThunderstoreVersion,
ignoreCache: boolean,
callback: (progress: number, modName: string, status: number, err: R2Error | null) => void,
completedCallback: (modList: ThunderstoreCombo[]) => void): void;
public abstract download(
profile: ImmutableProfile,
combo: ThunderstoreCombo,
ignoreCache: boolean,
totalProgressCallback: (progress: number, modName: string, status: number, err: R2Error | null) => void
): Promise<ThunderstoreCombo[]>;

/**
* A top-level method to download exact versions of exported mods.
Expand Down
93 changes: 44 additions & 49 deletions src/r2mm/downloading/BetterThunderstoreDownloader.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import ManifestV2 from "src/model/ManifestV2";
import ThunderstoreVersion from '../../model/ThunderstoreVersion';
import ThunderstoreMod from '../../model/ThunderstoreMod';
import StatusEnum from '../../model/enums/StatusEnum';
import axios, { AxiosResponse } from 'axios';
import ManifestV2 from "../../model/ManifestV2";
import ThunderstoreCombo from '../../model/ThunderstoreCombo';
import ZipExtract from '../installing/ZipExtract';
import R2Error from '../../model/errors/R2Error';
Expand Down Expand Up @@ -83,68 +82,64 @@ export default class BetterThunderstoreDownloader extends ThunderstoreDownloader
});
}

public async download(profile: ImmutableProfile, mod: ThunderstoreMod, modVersion: ThunderstoreVersion,
ignoreCache: boolean,
callback: (progress: number, modName: string, status: number, err: R2Error | null) => void,
completedCallback: (modList: ThunderstoreCombo[]) => void) {
public async download(
profile: ImmutableProfile,
combo: ThunderstoreCombo,
ignoreCache: boolean,
totalProgressCallback: (progress: number, modName: string, status: number, err: R2Error | null) => void
): Promise<ThunderstoreCombo[]> {

const modList = await ProfileModList.getModList(profile);
if (modList instanceof R2Error) {
return callback(0, mod.getName(), StatusEnum.FAILURE, modList);
totalProgressCallback(0, combo.getMod().getName(), StatusEnum.FAILURE, modList);
throw modList;
}

let dependencies: ThunderstoreCombo[] = [];
await this.buildDependencySet(modVersion, dependencies, DependencySetBuilderMode.USE_EXACT_VERSION);
this.sortDependencyOrder(dependencies);
const combo = new ThunderstoreCombo();
combo.setMod(mod);
combo.setVersion(modVersion);

dependencies = await this.determineDependencyVersions(dependencies, combo, modVersion, modList);
let downloadableDependencySize = this.calculateInitialDownloadSize(dependencies);
let dependencies = await this.getDependenciesWithCorrectVersions(combo, modList);
const allModsToDownload = [...dependencies, combo];

let downloadCount = 0;
await this.downloadAndSave(combo, ignoreCache, async (progress: number, status: number, err: R2Error | null) => {
const singleModProgressCallback = (progress: number, status: number, err: R2Error | null) => {
if (status === StatusEnum.FAILURE) {
callback(0, mod.getName(), status, err);
} else if (status === StatusEnum.PENDING) {
callback(this.generateProgressPercentage(progress, downloadCount, downloadableDependencySize + 1), mod.getName(), status, err);
throw err;
}

let totalProgress: number;
if (status === StatusEnum.PENDING) {
totalProgress = this.generateProgressPercentage(progress, downloadCount, allModsToDownload.length);
} else if (status === StatusEnum.SUCCESS) {
totalProgress = this.generateProgressPercentage(100, downloadCount, allModsToDownload.length);
downloadCount += 1;
// If no dependencies, end here.
if (dependencies.length === 0) {
callback(100, mod.getName(), StatusEnum.PENDING, err);
completedCallback([combo]);
return;
}
} else {
console.error(`Ignore unknown status code "${status}"`);
return;
}
totalProgressCallback(totalProgress, combo.getMod().getName(), status, err);
}

// If dependencies, queue and download.
await this.queueDownloadDependencies(dependencies.entries(), ignoreCache, (progress: number, modName: string, status: number, err: R2Error | null) => {
if (status === StatusEnum.FAILURE) {
callback(0, modName, status, err);
} else if (status === StatusEnum.PENDING) {
callback(this.generateProgressPercentage(progress, downloadCount, downloadableDependencySize + 1), modName, status, err);
} else if (status === StatusEnum.SUCCESS) {
callback(this.generateProgressPercentage(progress, downloadCount, downloadableDependencySize + 1), modName, StatusEnum.PENDING, err);
downloadCount += 1;
if (downloadCount >= dependencies.length + 1) {
callback(100, modName, StatusEnum.PENDING, err);
completedCallback([...dependencies, combo]);
}
}
});
for (const combo of allModsToDownload) {
if (!ignoreCache && await this.isVersionAlreadyDownloaded(combo)) {
totalProgressCallback(100, combo.getMod().getName(), StatusEnum.SUCCESS, null);
continue;
}
})

try {
const response = await this._downloadCombo(combo, singleModProgressCallback);
await this._saveDownloadResponse(response, combo, singleModProgressCallback);
} catch(e) {
throw R2Error.fromThrownValue(e, `Failed to download mod ${combo.getVersion().getFullName()}`);
}
}
return allModsToDownload;
}

// If combo is a modpack, use the modpack's dependency versions. If it isn't, get the latest versions.
public async determineDependencyVersions(dependencies: ThunderstoreCombo[], combo: ThunderstoreCombo, modVersion: ThunderstoreVersion, modList: ManifestV2[]) {
let isModpack = combo.getMod().getCategories().find(value => value === "Modpacks") !== undefined;
if (isModpack) {
return dependencies;
}
dependencies = [];
await this.buildDependencySet(modVersion, dependencies, DependencySetBuilderMode.USE_LATEST_VERSION);
private async getDependenciesWithCorrectVersions(combo: ThunderstoreCombo, modList: ManifestV2[]) {
let dependencies: ThunderstoreCombo[] = [];
const isModpack = combo.getMod().getCategories().find(value => value === "Modpacks") !== undefined;
const versionMode = isModpack ? DependencySetBuilderMode.USE_EXACT_VERSION : DependencySetBuilderMode.USE_LATEST_VERSION;

await this.buildDependencySet(combo.getVersion(), dependencies, versionMode);
this.sortDependencyOrder(dependencies);
// #270: Remove already-installed dependencies to prevent updating.
return dependencies.filter(dep => modList.find(installed => installed.getName() === dep.getMod().getFullName()) === undefined);
Expand Down

0 comments on commit 8080030

Please sign in to comment.