diff --git a/src/installers/BepInExInstaller.ts b/src/installers/BepInExInstaller.ts index f932c5f3b..74614a8ea 100644 --- a/src/installers/BepInExInstaller.ts +++ b/src/installers/BepInExInstaller.ts @@ -1,4 +1,4 @@ -import { InstallArgs, PackageInstaller } from "./PackageInstaller"; +import { InstallArgs, InstallerCapability, PackageInstaller } from "./PackageInstaller"; import path from "path"; import FsProvider from "../providers/generic/file/FsProvider"; import { MODLOADER_PACKAGES } from "../r2mm/installing/profile_installers/ModLoaderVariantRecord"; @@ -7,6 +7,15 @@ import { PackageLoader } from "../model/installing/PackageLoader"; const basePackageFiles = ["manifest.json", "readme.md", "icon.png"]; export class BepInExInstaller extends PackageInstaller { + async capability(): Promise { + return { + install: true, + uninstall: false, + enable: false, + disable: false, + } + } + /** * Handles installation of BepInEx */ @@ -39,4 +48,16 @@ export class BepInExInstaller extends PackageInstaller { } } } + + async uninstall(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async enable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async disable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } } diff --git a/src/installers/GodotMLInstaller.ts b/src/installers/GodotMLInstaller.ts index 71f0503d4..fef31953e 100644 --- a/src/installers/GodotMLInstaller.ts +++ b/src/installers/GodotMLInstaller.ts @@ -1,8 +1,17 @@ -import { InstallArgs, PackageInstaller } from "./PackageInstaller"; +import { InstallArgs, InstallerCapability, PackageInstaller } from "./PackageInstaller"; import path from "path"; import FsProvider from "../providers/generic/file/FsProvider"; export class GodotMLInstaller extends PackageInstaller { + async capability(): Promise { + return { + install: true, + uninstall: false, + enable: false, + disable: false, + } + } + /** * Handles installation of GodotML */ @@ -20,4 +29,16 @@ export class GodotMLInstaller extends PackageInstaller { await fs.copyFolder(copyFrom, copyTo); } } + + async uninstall(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async enable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async disable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } } diff --git a/src/installers/InstallRuleInstaller.ts b/src/installers/InstallRuleInstaller.ts index c23f560fb..5ce4f7243 100644 --- a/src/installers/InstallRuleInstaller.ts +++ b/src/installers/InstallRuleInstaller.ts @@ -1,4 +1,4 @@ -import { InstallArgs, PackageInstaller } from "./PackageInstaller"; +import { InstallArgs, InstallerCapability, PackageInstaller } from "./PackageInstaller"; import Profile from "../model/Profile"; import FsProvider from "../providers/generic/file/FsProvider"; import path from "path"; @@ -223,6 +223,15 @@ export class InstallRuleInstaller extends PackageInstaller { this.rule = rules; } + async capability(): Promise { + return { + install: true, + uninstall: false, + enable: false, + disable: false, + } + } + /** * Handles installation of packages according to the install rules defined * for it. @@ -267,4 +276,16 @@ export class InstallRuleInstaller extends PackageInstaller { } return Promise.resolve(undefined); } + + async uninstall(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async enable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async disable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } } diff --git a/src/installers/LovelyInstaller.ts b/src/installers/LovelyInstaller.ts index 9072c7102..3db66a8be 100644 --- a/src/installers/LovelyInstaller.ts +++ b/src/installers/LovelyInstaller.ts @@ -1,4 +1,4 @@ -import { InstallArgs, PackageInstaller } from "./PackageInstaller"; +import { InstallArgs, InstallerCapability, PackageInstaller } from "./PackageInstaller"; import { InstallRuleInstaller, addToStateFile } from "./InstallRuleInstaller"; import FsProvider from "../providers/generic/file/FsProvider"; import FileUtils from "../utils/FileUtils"; @@ -7,6 +7,15 @@ import R2Error from "../model/errors/R2Error"; import path from "path"; export class LovelyInstaller extends PackageInstaller { + async capability(): Promise { + return { + install: true, + uninstall: false, + enable: false, + disable: false, + } + } + async install(args: InstallArgs) { const { mod, @@ -43,9 +52,30 @@ export class LovelyInstaller extends PackageInstaller { await addToStateFile(mod, fileRelocations, profile); } + + async uninstall(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async enable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async disable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } } export class LovelyPluginInstaller extends PackageInstaller { + async capability(): Promise { + return { + install: true, + uninstall: false, + enable: false, + disable: false, + } + } + async install(args: InstallArgs) { const { mod, @@ -63,7 +93,7 @@ export class LovelyPluginInstaller extends PackageInstaller { if (srcTree instanceof R2Error) { throw R2Error; } - + const srcFiles = srcTree.getRecursiveFiles(); for (const srcFile of srcFiles) { const relFile = srcFile.replace(packagePath, ""); @@ -77,4 +107,16 @@ export class LovelyPluginInstaller extends PackageInstaller { await addToStateFile(mod, fileRelocations, profile); } + + async uninstall(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async enable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async disable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } } diff --git a/src/installers/MelonLoaderInstaller.ts b/src/installers/MelonLoaderInstaller.ts index 4b4a7372c..19ebfd504 100644 --- a/src/installers/MelonLoaderInstaller.ts +++ b/src/installers/MelonLoaderInstaller.ts @@ -1,10 +1,19 @@ -import { InstallArgs, PackageInstaller } from "./PackageInstaller"; +import { InstallArgs, InstallerCapability, PackageInstaller } from "./PackageInstaller"; import FsProvider from "../providers/generic/file/FsProvider"; import path from "path"; const basePackageFiles = ["manifest.json", "readme.md", "icon.png"]; export class MelonLoaderInstaller extends PackageInstaller { + async capability(): Promise { + return { + install: true, + uninstall: false, + enable: false, + disable: false, + } + } + /** * Handles installation of MelonLoader */ @@ -21,4 +30,16 @@ export class MelonLoaderInstaller extends PackageInstaller { } } } + + async uninstall(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async enable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async disable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } } diff --git a/src/installers/NorthstarInstaller.ts b/src/installers/NorthstarInstaller.ts index 9d3b4e31c..bd9edcd33 100644 --- a/src/installers/NorthstarInstaller.ts +++ b/src/installers/NorthstarInstaller.ts @@ -1,4 +1,4 @@ -import { InstallArgs, PackageInstaller } from './PackageInstaller'; +import { InstallArgs, InstallerCapability, PackageInstaller } from './PackageInstaller'; import path from 'path'; import FsProvider from '../providers/generic/file/FsProvider'; import { MODLOADER_PACKAGES } from '../r2mm/installing/profile_installers/ModLoaderVariantRecord'; @@ -7,6 +7,15 @@ import { PackageLoader } from '../model/installing/PackageLoader'; const basePackageFiles = ["manifest.json", "readme.md", "icon.png"]; export class NorthstarInstaller extends PackageInstaller { + async capability(): Promise { + return { + install: true, + uninstall: false, + enable: false, + disable: false, + } + } + /** * Handles installation of Northstar */ @@ -39,4 +48,16 @@ export class NorthstarInstaller extends PackageInstaller { } } } + + async uninstall(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async enable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async disable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } } diff --git a/src/installers/PackageInstaller.ts b/src/installers/PackageInstaller.ts index d7b56589e..bf814377d 100644 --- a/src/installers/PackageInstaller.ts +++ b/src/installers/PackageInstaller.ts @@ -8,9 +8,18 @@ export type InstallArgs = { packagePath: string; }; +export class InstallerCapability { + install: boolean = false; + uninstall: boolean = false; + enable: boolean = false; + disable: boolean = false; +} + export abstract class PackageInstaller { + abstract capability(): Promise; abstract install(args: InstallArgs): Promise; - // abstract disable(args: InstallArgs): Promise; // TODO: Implement - // abstract uninstall(): Promise; // TODO: Implement + abstract uninstall(args: InstallArgs): Promise; + abstract enable(args: InstallArgs): Promise; + abstract disable(args: InstallArgs): Promise; } diff --git a/src/installers/ReturnOfModdingInstaller.ts b/src/installers/ReturnOfModdingInstaller.ts index 5d29cd5c5..43aead6d6 100644 --- a/src/installers/ReturnOfModdingInstaller.ts +++ b/src/installers/ReturnOfModdingInstaller.ts @@ -1,4 +1,4 @@ -import { InstallArgs, PackageInstaller } from "./PackageInstaller"; +import { InstallArgs, InstallerCapability, PackageInstaller } from "./PackageInstaller"; import path from "path"; import FsProvider from "../providers/generic/file/FsProvider"; import { MODLOADER_PACKAGES } from "../r2mm/installing/profile_installers/ModLoaderVariantRecord"; @@ -7,6 +7,15 @@ import { PackageLoader } from "../model/installing/PackageLoader"; const basePackageFiles = ["manifest.json", "readme.md", "icon.png"]; export class ReturnOfModdingInstaller extends PackageInstaller { + async capability(): Promise { + return { + install: true, + uninstall: false, + enable: false, + disable: false, + } + } + /** * Handles installation of BepInEx */ @@ -39,4 +48,16 @@ export class ReturnOfModdingInstaller extends PackageInstaller { } } } + + async uninstall(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async enable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async disable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } } diff --git a/src/installers/ShimloaderInstaller.ts b/src/installers/ShimloaderInstaller.ts index 988ae2407..eb7eade5b 100644 --- a/src/installers/ShimloaderInstaller.ts +++ b/src/installers/ShimloaderInstaller.ts @@ -1,4 +1,4 @@ -import { InstallArgs, PackageInstaller } from "./PackageInstaller"; +import { InstallArgs, InstallerCapability, PackageInstaller } from "./PackageInstaller"; import path from "path"; import FsProvider from "../providers/generic/file/FsProvider"; import FileTree from "../model/file/FileTree"; @@ -7,6 +7,15 @@ import R2Error from "../model/errors/R2Error"; import { InstallRuleInstaller } from "./InstallRuleInstaller"; export class ShimloaderInstaller extends PackageInstaller { + async capability(): Promise { + return { + install: true, + uninstall: false, + enable: false, + disable: false, + } + } + /** * Handle installation of unreal-shimloader */ @@ -53,9 +62,30 @@ export class ShimloaderInstaller extends PackageInstaller { await fs.mkdirs(configDir); } } + + async uninstall(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async enable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async disable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } } export class ShimloaderPluginInstaller extends PackageInstaller { + async capability(): Promise { + return { + install: true, + uninstall: false, + enable: false, + disable: false, + } + } + readonly installer = new InstallRuleInstaller({ gameName: "none" as any, // This isn't acutally used for actual installation but needs some value rules: [ @@ -84,4 +114,16 @@ export class ShimloaderPluginInstaller extends PackageInstaller { async install(args: InstallArgs) { await this.installer.install(args); } + + async uninstall(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async enable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } + + async disable(args: InstallArgs): Promise { + throw new Error("Method not implemented."); + } } diff --git a/src/r2mm/installing/profile_installers/GenericProfileInstaller.ts b/src/r2mm/installing/profile_installers/GenericProfileInstaller.ts index 9d37c9d34..adc5ede9f 100644 --- a/src/r2mm/installing/profile_installers/GenericProfileInstaller.ts +++ b/src/r2mm/installing/profile_installers/GenericProfileInstaller.ts @@ -101,6 +101,31 @@ export default class GenericProfileInstaller extends ProfileInstallerProvider { } async applyModMode(mod: ManifestV2, tree: FileTree, profile: Profile, location: string, mode: number): Promise { + const installerId = GetInstallerIdForPlugin(GameManager.activeGame.packageLoader); + + if (installerId !== null) { + const cacheDirectory = path.join(PathResolver.MOD_ROOT, 'cache'); + const cachedLocationOfMod: string = path.join(cacheDirectory, mod.getName(), mod.getVersionNumber().toString()); + + const args: InstallArgs = { + mod: mod, + profile: profile, + packagePath: cachedLocationOfMod, + } + + const installer = PackageInstallers[installerId!]; + const capability = await installer.capability(); + + if (mode === ModMode.ENABLED && capability.enable) { + return await installer.enable(args); + } + + if (mode === ModMode.DISABLED && capability.disable) { + return await installer.disable(args); + } + } + + // The installer wasn't found or does not have the correct capability, fall back to legacy. const appliedState = await this.applyModModeForState(mod, tree, profile, location, mode); if (appliedState instanceof R2Error) { return appliedState; @@ -277,6 +302,27 @@ export default class GenericProfileInstaller extends ProfileInstallerProvider { } async uninstallMod(mod: ManifestV2, profile: Profile): Promise { + const installerId = GetInstallerIdForPlugin(GameManager.activeGame.packageLoader); + + if (installerId !== null) { + const cacheDirectory = path.join(PathResolver.MOD_ROOT, 'cache'); + const cachedLocationOfMod: string = path.join(cacheDirectory, mod.getName(), mod.getVersionNumber().toString()); + + const args: InstallArgs = { + mod: mod, + profile: profile, + packagePath: cachedLocationOfMod, + } + + const installer = PackageInstallers[installerId!]; + const capability = await installer.capability(); + + if (capability.uninstall) { + await installer.uninstall(args); + return null; + } + } + const uninstallState = await this.uninstallState(mod, profile); if (uninstallState instanceof R2Error) { return uninstallState;