Skip to content

Commit

Permalink
Merge pull request #1588 from ebkr/local-import-cleanup
Browse files Browse the repository at this point in the history
Cleaning up LocalModInstaller
  • Loading branch information
anttimaki authored Jan 15, 2025
2 parents 21ba621 + 16e713d commit f5137c6
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 41 deletions.
1 change: 0 additions & 1 deletion src/providers/ror2/installing/LocalModInstallerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export default abstract class LocalModInstallerProvider {
* @param zipFile Path to the zip file.
* @param callback Callback to report if the extraction was successful.
*/
public abstract extractToCache(profile: ImmutableProfile, zipFile: string, callback: (success: boolean, error: R2Error | null) => void): Promise<R2Error | void>;
public abstract extractToCacheWithManifestData(profile: ImmutableProfile, zipFile: string, manifest: ManifestV2, callback: (success: boolean, error: R2Error | null) => void): Promise<void>;
public abstract placeFileInCache(profile: ImmutableProfile, file: string, manifest: ManifestV2, callback: (success: boolean, error: R2Error | null) => void): Promise<void>;

Expand Down
73 changes: 33 additions & 40 deletions src/r2mm/installing/LocalModInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,59 +7,54 @@ import FsProvider from '../../providers/generic/file/FsProvider';
import PathResolver from '../manager/PathResolver';
import ProfileModList from '../mods/ProfileModList';
import LocalModInstallerProvider from '../../providers/ror2/installing/LocalModInstallerProvider';
import ZipProvider from '../../providers/generic/zip/ZipProvider';
import { ImmutableProfile } from '../../model/Profile';
import FileUtils from '../../utils/FileUtils';

export default class LocalModInstaller extends LocalModInstallerProvider {

public async extractToCache(profile: ImmutableProfile, zipFile: string, callback: (success: boolean, error: R2Error | null) => void) {
const result: Buffer | null = await ZipProvider.instance.readFile(zipFile,'manifest.json');
if (result !== null) {
const fileContents = result.toString();
try {
const parsed = JSON.parse(fileContents.trim());
const mod: R2Error | ManifestV2 = new ManifestV2().makeSafe(parsed);
if (mod instanceof R2Error) {
return mod;
}
return await this.extractToCacheWithManifestData(profile, zipFile, mod, callback);
} catch(e) {
const err: Error = e as Error;
return new R2Error('Failed to convert manifest to JSON', err.message, null);
}
private async initialiseCacheDirectory(manifest: ManifestV2): Promise<string> {
const cacheDirectory = path.join(PathResolver.MOD_ROOT, 'cache', manifest.getName(), manifest.getVersionNumber().toString());
if (await FsProvider.instance.exists(cacheDirectory)) {
await FileUtils.emptyDirectory(cacheDirectory);
} else {
return new R2Error('No manifest provided', 'No file found in zip with name "manifest.json". Contact the mod author, or create your own.', null);
await FileUtils.ensureDirectory(cacheDirectory);
}
return Promise.resolve();
return cacheDirectory;
}

private async initialiseCacheDirectory(manifest: ManifestV2) {
const cacheDirectory: string = path.join(PathResolver.MOD_ROOT, 'cache');
if (await FsProvider.instance.exists(path.join(cacheDirectory, manifest.getName(), manifest.getVersionNumber().toString()))) {
await FileUtils.emptyDirectory(path.join(cacheDirectory, manifest.getName(), manifest.getVersionNumber().toString()));
} else {
await FileUtils.ensureDirectory(path.join(cacheDirectory, manifest.getName(), manifest.getVersionNumber().toString()));
/**
* Store custom file into cache to indicate the mod is installed locally.
* When the mod is installed, the file also gets copied to profile, which can
* be used in troubleshooting, telling us that the version of the mod isn't
* necessarily the same that's available via Thunderstore API.
*/
private async writeManifestToCache(cacheDirectory: string, manifest: ManifestV2) {
const manifestPath: string = path.join(cacheDirectory, 'mm_v2_manifest.json');

if (await FsProvider.instance.exists(manifestPath)) {
try {
await FsProvider.instance.unlink(manifestPath);
} catch (e) {
throw R2Error.fromThrownValue(e, 'Failed to unlink manifest from cache');
}
}

await FsProvider.instance.writeFile(manifestPath, JSON.stringify(manifest));
}

public async extractToCacheWithManifestData(profile: ImmutableProfile, zipFile: string, manifest: ManifestV2, callback: (success: boolean, error: R2Error | null) => void) {
const cacheDirectory: string = path.join(PathResolver.MOD_ROOT, 'cache');
await this.initialiseCacheDirectory(manifest);
const cacheDirectory: string = await this.initialiseCacheDirectory(manifest);
await ZipExtract.extractOnly(
zipFile,
path.join(cacheDirectory, manifest.getName(), manifest.getVersionNumber().toString()),
cacheDirectory,
async success => {
if (success) {
if (await FsProvider.instance.exists(path.join(cacheDirectory, manifest.getName(), manifest.getVersionNumber().toString(), "mm_v2_manifest.json"))) {
try {
await FsProvider.instance.unlink(path.join(cacheDirectory, manifest.getName(), manifest.getVersionNumber().toString(), "mm_v2_manifest.json"));
} catch (e) {
const err: Error = e as Error;
callback(false, new R2Error("Failed to unlink manifest from cache", err.message, null));
}
try {
await this.writeManifestToCache(cacheDirectory, manifest);
} catch (e) {
callback(false, R2Error.fromThrownValue(e));
return Promise.resolve();
}
await FsProvider.instance.writeFile(path.join(cacheDirectory, manifest.getName(), manifest.getVersionNumber().toString(), "mm_v2_manifest.json"), JSON.stringify(manifest));
await ProfileInstallerProvider.instance.uninstallMod(manifest, profile);
const profileInstallResult = await ProfileInstallerProvider.instance.installMod(manifest, profile);
if (profileInstallResult instanceof R2Error) {
Expand All @@ -80,12 +75,10 @@ export default class LocalModInstaller extends LocalModInstallerProvider {

public async placeFileInCache(profile: ImmutableProfile, file: string, manifest: ManifestV2, callback: (success: boolean, error: (R2Error | null)) => void) {
try {
const cacheDirectory: string = path.join(PathResolver.MOD_ROOT, 'cache');
await this.initialiseCacheDirectory(manifest);
const modCacheDirectory = path.join(cacheDirectory, manifest.getName(), manifest.getVersionNumber().toString());
const cacheDirectory: string = await this.initialiseCacheDirectory(manifest);
const fileSafe = file.split("\\").join("/");
await FsProvider.instance.copyFile(fileSafe, path.join(modCacheDirectory, path.basename(fileSafe)));
await FsProvider.instance.writeFile(path.join(modCacheDirectory, "mm_v2_manifest.json"), JSON.stringify(manifest));
await FsProvider.instance.copyFile(fileSafe, path.join(cacheDirectory, path.basename(fileSafe)));
await this.writeManifestToCache(cacheDirectory, manifest);
await ProfileInstallerProvider.instance.uninstallMod(manifest, profile);
const profileInstallResult = await ProfileInstallerProvider.instance.installMod(manifest, profile);
if (profileInstallResult instanceof R2Error) {
Expand Down

0 comments on commit f5137c6

Please sign in to comment.