Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ProfileInstallerProvider to use ImmutableProfile when enabling/disabling mods #1475

Merged
merged 7 commits into from
Oct 14, 2024
2 changes: 1 addition & 1 deletion src/components/profiles-modals/ImportProfileModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ export default class ImportProfileModal extends mixins(ProfilesMixin) {
await ProfileModList.updateMod(installedMod, this.activeProfile.asImmutableProfile(), async (modToDisable: ManifestV2) => {
// Need to enable temporarily so the manager doesn't think it's re-disabling a disabled mod.
modToDisable.enable();
await ProfileInstallerProvider.instance.disableMod(modToDisable, this.activeProfile);
await ProfileInstallerProvider.instance.disableMod(modToDisable, this.activeProfile.asImmutableProfile());
modToDisable.disable();
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/DownloadModModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ let assignId = 0;
await ProfileModList.updateMod(manifestMod, profile.asImmutableProfile(), async mod => {
mod.disable();
});
await ProfileInstallerProvider.instance.disableMod(manifestMod, profile);
await ProfileInstallerProvider.instance.disableMod(manifestMod, profile.asImmutableProfile());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ProviderUtils from '../../generic/ProviderUtils';
import ManifestV2 from '../../../model/ManifestV2';
import R2Error from '../../../model/errors/R2Error';
import Profile from '../../../model/Profile';
import Profile, { ImmutableProfile } from '../../../model/Profile';

export default abstract class ConflictManagementProvider {

Expand All @@ -22,7 +22,7 @@ export default abstract class ConflictManagementProvider {

public abstract overrideInstalledState(mod: ManifestV2, profile: Profile): Promise<R2Error | void>;

public abstract isFileActive(mod: ManifestV2, profile: Profile, file: string): Promise<boolean>;
public abstract isFileActive(mod: ManifestV2, profile: Profile|ImmutableProfile, file: string): Promise<boolean>;

}

Expand Down
27 changes: 3 additions & 24 deletions src/providers/ror2/installing/ProfileInstallerProvider.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import ProviderUtils from '../../generic/ProviderUtils';
import ManifestV2 from '../../../model/ManifestV2';
import R2Error from '../../../model/errors/R2Error';
import FileTree from '../../../model/file/FileTree';
import Profile from '../../../model/Profile';
import Profile, { ImmutableProfile } from '../../../model/Profile';

export default abstract class ProfileInstallerProvider {

Expand All @@ -28,37 +27,17 @@ export default abstract class ProfileInstallerProvider {
* Disable files to prevent the mod from loading.
* @param mod
*/
public abstract disableMod(mod: ManifestV2, profile: Profile): Promise<R2Error | void>;
public abstract disableMod(mod: ManifestV2, profile: ImmutableProfile): Promise<R2Error | void>;

/**
* Enable files to undo a disable operation.
* @param mod
*/
public abstract enableMod(mod: ManifestV2, profile: Profile): Promise<R2Error | void>;
public abstract enableMod(mod: ManifestV2, profile: ImmutableProfile): Promise<R2Error | void>;

/**
* Installs a mod to the profile.
* @param mod
*/
public abstract installMod(mod: ManifestV2, profile: Profile): Promise<R2Error | null>;

/**
* Applies either enabling or disabling under a shared method.
* Logic for both {@method enableMod} and {@method disableMod} should be handled here.
*
* @param mod
* @param tree A BepInExTree object to provide a list of all files provided in the mod.
* @param location The location of the mod within a BepInEx sub-folder.
* @param mode The ModMode number. {@class model/enums/ModMode}
*/
abstract applyModMode(mod: ManifestV2, tree: FileTree, profile: Profile, location: string, mode: number): Promise<R2Error | void>;

/**
* Get descendant files of a given location.
*
* For params, see {@method applyModMode}
* @param tree If tree is not provided, one is created on first call. This method is recursive.
* @param location
*/
abstract getDescendantFiles(tree: FileTree | null, location: string): Promise<string[]>;
}
6 changes: 3 additions & 3 deletions src/r2mm/installing/ConflictManagementProviderImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as path from 'path';
import yaml from 'yaml';
import ModFileTracker from '../../model/installing/ModFileTracker';
import StateTracker from '../../model/installing/StateTracker';
import Profile from '../../model/Profile';
import Profile, { ImmutableProfile } from '../../model/Profile';
import FileUtils from '../../utils/FileUtils';

export default class ConflictManagementProviderImpl extends ConflictManagementProvider {
Expand Down Expand Up @@ -98,7 +98,7 @@ export default class ConflictManagementProviderImpl extends ConflictManagementPr
} as StateTracker));
}

public async isFileActive(mod: ManifestV2, profile: Profile, file: string) {
public async isFileActive(mod: ManifestV2, profile: Profile|ImmutableProfile, file: string) {
const state = await this.getTotalState(profile);
for (const [stateFile, stateMod] of state.currentState) {
if (stateFile === file) {
Expand All @@ -108,7 +108,7 @@ export default class ConflictManagementProviderImpl extends ConflictManagementPr
return false;
}

private async getTotalState(profile: Profile): Promise<StateTracker> {
private async getTotalState(profile: Profile|ImmutableProfile): Promise<StateTracker> {
const totalStateFilePath = profile.joinToProfilePath("_state", "installation_state.yml");
let totalState: StateTracker = {
currentState: []
Expand Down
48 changes: 16 additions & 32 deletions src/r2mm/installing/profile_installers/GenericProfileInstaller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ProfileInstallerProvider from '../../../providers/ror2/installing/ProfileInstallerProvider';
import ManifestV2 from '../../../model/ManifestV2';
import Profile from '../../../model/Profile';
import Profile, { ImmutableProfile } from '../../../model/Profile';
import FileTree from '../../../model/file/FileTree';
import R2Error from '../../../model/errors/R2Error';
import ModLoaderPackageMapping from '../../../model/installing/ModLoaderPackageMapping';
Expand Down Expand Up @@ -35,7 +35,7 @@ export default class GenericProfileInstaller extends ProfileInstallerProvider {
this.legacyInstaller = new InstallRuleInstaller(this.rule);
}

private async applyModModeForSubdir(mod: ManifestV2, tree: FileTree, profile: Profile, location: string, mode: number): Promise<R2Error | void> {
private async applyModModeForSubdir(mod: ManifestV2, profile: ImmutableProfile, mode: number): Promise<R2Error | void> {
// TODO: Call through the installer interface. For now we hardcode the only known case because expanding the
// installer system is out of scope.
//
Expand Down Expand Up @@ -82,19 +82,21 @@ export default class GenericProfileInstaller extends ProfileInstallerProvider {
}
}

private async applyModModeForState(mod: ManifestV2, tree: FileTree, profile: Profile, location: string, mode: number): Promise<R2Error | void> {
private async applyModModeForState(mod: ManifestV2, profile: ImmutableProfile, mode: number): Promise<R2Error | void> {
profile.getProfilePath()
try {
const modStateFilePath = path.join(location, "_state", `${mod.getName()}-state.yml`);
const modStateFilePath = profile.joinToProfilePath("_state", `${mod.getName()}-state.yml`);
if (await FsProvider.instance.exists(modStateFilePath)) {
const fileContents = (await FsProvider.instance.readFile(modStateFilePath)).toString();
const tracker: ModFileTracker = yaml.parse(fileContents);
for (const [key, value] of tracker.files) {
if (await ConflictManagementProvider.instance.isFileActive(mod, profile, value)) {
if (await FsProvider.instance.exists(path.join(location, value))) {
await FsProvider.instance.unlink(path.join(location, value));
const filePath = profile.joinToProfilePath(value);
if (await FsProvider.instance.exists(filePath)) {
await FsProvider.instance.unlink(filePath);
}
if (mode === ModMode.ENABLED) {
await FsProvider.instance.copyFile(key, path.join(location, value));
await FsProvider.instance.copyFile(key, filePath);
}
}
}
Expand All @@ -104,41 +106,23 @@ export default class GenericProfileInstaller extends ProfileInstallerProvider {
}
}

async applyModMode(mod: ManifestV2, tree: FileTree, profile: Profile, location: string, mode: number): Promise<R2Error | void> {
const appliedState = await this.applyModModeForState(mod, tree, profile, location, mode);
private async applyModMode(mod: ManifestV2, profile: ImmutableProfile, mode: number): Promise<R2Error | void> {
const appliedState = await this.applyModModeForState(mod, profile, mode);
if (appliedState instanceof R2Error) {
return appliedState;
}
const appliedSub = await this.applyModModeForSubdir(mod, tree, profile, location, mode);
const appliedSub = await this.applyModModeForSubdir(mod, profile, mode);
if (appliedSub instanceof R2Error) {
return appliedSub;
}
}

async disableMod(mod: ManifestV2, profile: Profile): Promise<R2Error | void> {
return this.applyModMode(mod, new FileTree(), profile, profile.getProfilePath(), ModMode.DISABLED);
async disableMod(mod: ManifestV2, profile: ImmutableProfile): Promise<R2Error | void> {
return this.applyModMode(mod, profile, ModMode.DISABLED);
}

async enableMod(mod: ManifestV2, profile: Profile): Promise<R2Error | void> {
return this.applyModMode(mod, new FileTree(), profile, profile.getProfilePath(), ModMode.ENABLED);
}

async getDescendantFiles(tree: FileTree | null, location: string): Promise<string[]> {
const files: string[] = [];
if (tree === null) {
const newTree = await FileTree.buildFromLocation(location);
if (newTree instanceof R2Error) {
return files;
}
tree = newTree;
}
for (const directory of tree.getDirectories()) {
files.push(...(await this.getDescendantFiles(directory, path.join(location, directory.getDirectoryName()))));
}
tree.getFiles().forEach((file: string) => {
files.push(file);
})
return files;
async enableMod(mod: ManifestV2, profile: ImmutableProfile): Promise<R2Error | void> {
return this.applyModMode(mod, profile, ModMode.ENABLED);
}

async installForManifestV2(args: InstallArgs): Promise<R2Error | null> {
Expand Down
12 changes: 6 additions & 6 deletions src/store/modules/ProfileModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,15 @@ export default {
onProgress?: (mod: ManifestV2) => void,
}
) {
const profile = getters.activeProfileOrThrow;
const profile = getters.activeProfileOrThrow.asImmutableProfile();
await dispatch('disableModsFromProfile', {...params, profile});
},

async disableModsFromProfile(
{dispatch},
params: {
mods: ManifestV2[],
profile: Profile,
profile: ImmutableProfile,
onProgress?: (mod: ManifestV2) => void,
}
) {
Expand All @@ -211,7 +211,7 @@ export default {
}

// Update mod list status to mods.yml.
const updatedList = await ProfileModList.updateMods(mods, profile.asImmutableProfile(), (mod) => mod.disable());
const updatedList = await ProfileModList.updateMods(mods, profile, (mod) => mod.disable());
if (updatedList instanceof R2Error) {
throw updatedList;
} else {
Expand All @@ -235,15 +235,15 @@ export default {
onProgress?: (mod: ManifestV2) => void,
}
) {
const profile = getters.activeProfileOrThrow;
const profile = getters.activeProfileOrThrow.asImmutableProfile();
await dispatch('enableModsOnProfile', {...params, profile});
},

async enableModsOnProfile(
{dispatch},
params: {
mods: ManifestV2[],
profile: Profile,
profile: ImmutableProfile,
onProgress?: (mod: ManifestV2) => void,
}
) {
Expand All @@ -266,7 +266,7 @@ export default {
}

// Update mod list status to mods.yml.
const updatedList = await ProfileModList.updateMods(mods, profile.asImmutableProfile(), (mod) => mod.enable());
const updatedList = await ProfileModList.updateMods(mods, profile, (mod) => mod.enable());
if (updatedList instanceof R2Error) {
throw updatedList;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
installLogicBeforeEach
} from '../../../__utils__/InstallLogicUtils';
import R2Error from '../../../../../src/model/errors/R2Error';
import Profile from '../../../../../src/model/Profile';
import Profile, { ImmutableProfile } from '../../../../../src/model/Profile';
import ProfileInstallerProvider from '../../../../../src/providers/ror2/installing/ProfileInstallerProvider';


Expand Down Expand Up @@ -36,15 +36,16 @@ describe('ReturnOfModding Installer Tests', () => {

test('Disabling/enabling the mod loader does nothing', async () => {
const pkg = createManifest("ReturnOfModding", "ReturnOfModding");
const profile = new ImmutableProfile("TestProfile");
const loaders = ["version.dll", "d3d12.dll"];
await createFilesIntoProfile(loaders);

await ProfileInstallerProvider.instance.disableMod(pkg, Profile.getActiveProfile());
await ProfileInstallerProvider.instance.disableMod(pkg, profile);
await expectFilesToExistInProfile(loaders);

pkg.disable();

await ProfileInstallerProvider.instance.enableMod(pkg, Profile.getActiveProfile());
await ProfileInstallerProvider.instance.enableMod(pkg, profile);
await expectFilesToExistInProfile(loaders);
});

Expand Down Expand Up @@ -95,6 +96,7 @@ describe('ReturnOfModding Installer Tests', () => {

test('Disabling/enabling a mod renames files', async () => {
const pkg = createManifest("HelperFunctions", "Klehrik");
const profile = new ImmutableProfile("TestProfile");
const name = pkg.getName();
const files = [
`ReturnOfModding/plugins/${name}/main.lua`,
Expand All @@ -104,12 +106,12 @@ describe('ReturnOfModding Installer Tests', () => {
];
await createFilesIntoProfile(files);

await ProfileInstallerProvider.instance.disableMod(pkg, Profile.getActiveProfile());
await ProfileInstallerProvider.instance.disableMod(pkg, profile);
await expectFilesToExistInProfile(files.map((fileName) => `${fileName}.old`));

pkg.disable();

await ProfileInstallerProvider.instance.enableMod(pkg, Profile.getActiveProfile());
await ProfileInstallerProvider.instance.enableMod(pkg, profile);
await expectFilesToExistInProfile(files);
});
});
Loading