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

Improve performance when uninstalling multiple mods at once #1155

Merged
merged 6 commits into from
Jan 22, 2024

Conversation

anttimaki
Copy link
Collaborator

This is probably best reviewed commit by commit.

There's three methods that control how mods are uninstalled:
uninstallModRequireConfirmation, which calls performUninstallMod
directly if the mod has no dependants, and uninstallMod if the mod
has dependencies. uninstallMod in turn calls performUninstallMod for
the mod and all the dependants separately. This commit does the
following changes:

- By default, performUninstallMod now calls updateModListAfterChange
  helper, which differs from the original implementation in that it
  also calls filterModList method
- Since uninstallModRequireConfirmation calls performUninstallMod, it
  no longer needs to call filterModList directly
- performUninstallMod also accepts a new boolean parameter which can be
  used to prevent it from calling updateModListAfterChange
- uninstallMod uses the said parameter to prevent the
  updateModListAfterChange getting called after each individual mod is
  uninstalled. It calls updateModListAfterChange itself directly in the
  end

As far as I can tell this has no ill effects to the end result, i.e.
the profile files that remain on the disk appear to be identical. The
only downside I'm aware of is that the modal that shows the list of
mods no longer gets updated during the process, so it might appear to
the user that nothing happens.

The benefit is that the process takes significantly less time when
uninstalling a mod with lots of dependants. Prior to changes
uninstalling BepInEx from a profile that contained 109 mods took 147
seconds, while after the changes it "only" takes 24 seconds.
This works as an indicator to the user that something is happening in
case the uninstallation of multiple mods takes a bit longer.
@anttimaki
Copy link
Collaborator Author

If this is seen as good idea, similar optimizations can be done to disabling/enabling mods.

@anttimaki
Copy link
Collaborator Author

Screenshot showing the disabled button and the indicator that shows which mod is currently being uninstalled.
image

Comment on lines 449 to 482
async uninstallMod(vueMod: any) {
let mod: ManifestV2 = new ManifestV2().fromReactive(vueMod);
try {
for (const dependant of Dependants.getDependantList(mod, this.modifiableModList)) {
const result = await this.performUninstallMod(dependant);
this.modBeingUninstalled = dependant.getName();
const result = await this.performUninstallMod(dependant, false);
if (result instanceof R2Error) {
this.$emit('error', result);
this.modBeingUninstalled = null;
return;
}
}
const result = await this.performUninstallMod(mod);
this.modBeingUninstalled = mod.getName();
const result = await this.performUninstallMod(mod, false);
if (result instanceof R2Error) {
this.$emit('error', result);
this.modBeingUninstalled = null;
return;
}
} catch (e) {
// Failed to uninstall mod.
const err: Error = e as Error;
this.$emit('error', err);
this.modBeingUninstalled = null;
LoggerProvider.instance.Log(LogSeverity.ACTION_STOPPED, `${err.name}\n-> ${err.message}`);
}
this.selectedManifestMod = null;
this.modBeingUninstalled = null;
const result: ManifestV2[] | R2Error = await ProfileModList.getModList(this.contextProfile!);
if (result instanceof R2Error) {
this.$emit('error', result);
return;
}
await this.$store.dispatch("updateModList", result);
const err = await ConflictManagementProvider.instance.resolveConflicts(result, this.contextProfile!);
if (err instanceof R2Error) {
this.$emit('error', err);
}
this.filterModList();
await this.updateModListAfterChange(result);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there are a handful of code paths here which could in theory apply changes without refreshing the mod list, would it make sense to always refresh it before returning?

I'll push that change on top of this before merging since it seems a bit less prone to state management bugs that way.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in b64c89c

Make sure the profile mod list is refreshed with the latest information
even if the uninstall logic exits midway.
Copy link
Collaborator

@MythicManiac MythicManiac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested this locally and everything seems to work as expected

Base automatically changed from profile-performance to develop January 22, 2024 22:57
@MythicManiac MythicManiac merged commit 3ad7761 into develop Jan 22, 2024
4 checks passed
@MythicManiac MythicManiac deleted the dependant-uninstall-performance branch January 22, 2024 23:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants