Skip to content

Commit

Permalink
fix: prevent invalid enbs from being persisted on mod selection
Browse files Browse the repository at this point in the history
- improve startup logs by adding date

closes #518
  • Loading branch information
MattLish committed Jun 16, 2022
1 parent 2c0a3d9 commit 73a47b7
Show file tree
Hide file tree
Showing 15 changed files with 117 additions and 60 deletions.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
},
"nodemonConfig": {
"watch": [
"src/main/",
"src/services"
"src/**"
],
"ignore": [
"src/renderer/**"
],
"exec": "npm run start",
"ext": "*"
Expand Down
5 changes: 4 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import { logger } from "@/main/logger";
import { ErrorService } from "@/main/services/error.service";

// Ensure it's easy to tell where the logs for this application start
logger.debug("-".repeat(20));
const initialLog = `| ${new Date().toLocaleString()} |`;
logger.debug("-".repeat(initialLog.length));
logger.debug(initialLog);
logger.debug("-".repeat(initialLog.length));
autoUpdater.logger = require("electron-log");

// Scheme must be registered before the app is ready
Expand Down
2 changes: 1 addition & 1 deletion src/main/controllers/wabbajack/wabbajack.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export class WabbajackController {

@handle(WABBAJACK_EVENTS.GET_INSTALLED_MODPACKS)
async getInstalledModpacks() {
return this.wabbajackService.getInstalledWildlanderModpackPaths();
return this.wabbajackService.getInstalledCurrentModpackPaths();
}
}
28 changes: 24 additions & 4 deletions src/main/services/config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { USER_PREFERENCE_KEYS } from "@/shared/enums/userPreferenceKeys";
import { Resolution } from "@/Resolution";
import path from "path";
import { BindingScope, injectable } from "@loopback/context";
import { logger } from "@/main/logger";

export const appRoot = path.resolve(`${__dirname}/../../`);
export const isDevelopment = path.extname(appRoot) !== ".asar";
Expand All @@ -15,6 +16,13 @@ export interface UserPreferences {
[USER_PREFERENCE_KEYS.RESOLUTION]: Resolution;
}

type PreferenceWithValidator = {
[key in keyof UserPreferences]?: {
value: UserPreferences[keyof UserPreferences];
validate?: (...args: unknown[]) => boolean | Promise<boolean>;
};
};

export const userPreferences = new Store<UserPreferences>({
name: "userPreferences",
});
Expand Down Expand Up @@ -48,18 +56,30 @@ export class ConfigService {
}

setPreference(key: keyof UserPreferences | string, value: unknown) {
logger.debug(`Setting preference ${key} to ${value}`);
return userPreferences.set(key, value);
}

/**
Only set the values if they don't already exist
Set the value specified if the key doesn't exist or the current value is invalid
*/
setDefaultPreferences(preferences: Partial<UserPreferences>) {
for (const [key, value] of Object.entries(preferences)) {
if (!userPreferences.has(key)) {
async setDefaultPreferences(preferences: PreferenceWithValidator) {
logger.debug("Setting default user preferences");
logger.debug(`Current preferences`);
logger.debug(this.getPreferences().store);
for (const [key, { value, validate }] of Object.entries(preferences)) {
const valid = validate ? await validate() : true;
if (!valid) {
logger.warn(
`Current ${key} preference is invalid. Setting to default: ${value}`
);
}
if ((!userPreferences.has(key) || !valid) && value) {
this.setPreference(key, value);
}
}
logger.debug("New preferences");
logger.debug(this.getPreferences().store);
}

getPreferences() {
Expand Down
33 changes: 16 additions & 17 deletions src/main/services/enb.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,18 @@ export class EnbService {
return [...mappedEnbs, ...unmappedENBs];
}

/**
* Get the enb preference from the user preferences or just return the first one if it is invalid (e.g. the preference is no longer included)
*/
async getEnbPreference() {
const preference = this.configService.getPreference<string>(
return this.configService.getPreference<string>(
USER_PREFERENCE_KEYS.ENB_PROFILE
) as string;
}

if (preference && (await this.isInPresetList(preference))) {
return preference;
} else {
const preset = (await this.getENBPresets())[0].real;
logger.debug(
`Returning the first enb preset (${preset}) because there was either no preference or the preference was not in the list`
);
return preset;
}
async getDefaultPreference() {
return (await this.getENBPresets())[0].real;
}

isValid(preset: string) {
return this.isInPresetList(preset);
}

async isInPresetList(preset: string) {
Expand All @@ -83,7 +78,7 @@ export class EnbService {
return isInList;
}

async setEnbPreference(enb: string) {
async setEnbPreference(enb: string, sync = true) {
const previousEnb = this.configService.getPreference(
USER_PREFERENCE_KEYS.ENB_PROFILE
);
Expand All @@ -92,7 +87,7 @@ export class EnbService {
previousEnb
);
this.configService.setPreference(USER_PREFERENCE_KEYS.ENB_PROFILE, enb);
return this.copyENBFiles(enb);
return this.copyEnbFiles(enb, sync);
}

async backupOriginalENBs() {
Expand All @@ -114,7 +109,7 @@ export class EnbService {
logger.info("Restoring ENB presets");
const ENBBackupDirectory = `${this.configService.backupDirectory()}/ENB Presets`;
await copy(ENBBackupDirectory, this.enbDirectory(), { overwrite: true });
await this.copyENBFiles(
await this.copyEnbFiles(
userPreferences.get(USER_PREFERENCE_KEYS.ENB_PROFILE),
false
);
Expand Down Expand Up @@ -186,12 +181,16 @@ export class EnbService {
logger.info("Finished syncing ENB presets");
}

async copyCurrentEnbFiles(sync?: boolean) {
return this.copyEnbFiles(await this.getEnbPreference(), sync);
}

/**
* Copy all ENB files from an ENB preset
* @param profile - Must be the actual ENB profile name, not the friendly name. noENB will remove all ENB files.
* @param sync - Whether to sync the changes from Stock Game back to the ENB Preset directory
*/
async copyENBFiles(profile: string | "noENB", sync = true) {
async copyEnbFiles(profile: string | "noENB", sync = true) {
logger.info(`Copying ${profile} ENB Files prerequisite`);

const previousProfile =
Expand Down
2 changes: 1 addition & 1 deletion src/main/services/error.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { BindingScope, injectable } from "@loopback/context";
export class ErrorService {
async handleError(title: string, message: string) {
logger.error(`${title}: ${message}`);
console.trace();
logger.error(new Error().stack);
await dialog.showErrorBox(title, message);
}
}
20 changes: 13 additions & 7 deletions src/main/services/modOrganizer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,28 @@ export class ModOrganizerService {
* Return the current profile preference or the first if it is invalid
*/
async getProfilePreference() {
const preference = this.configService.getPreference<string>(
return this.configService.getPreference<string>(
USER_PREFERENCE_KEYS.PRESET
);
if (preference && (await this.isInProfileList(preference))) {
return preference;
} else {
return (await this.getProfiles())[0].real;
}
}

async getDefaultPreference() {
return (await this.getProfiles())[0].real;
}

setProfilePreference(profile: string) {
this.configService.setPreference(USER_PREFERENCE_KEYS.PRESET, profile);
}

async isValid(profile: string) {
return this.isInProfileList(profile);
}

async isInProfileList(profile: string) {
return (await this.getProfiles()).filter(({ real }) => real === profile);
return (
(await this.getProfiles()).filter(({ real }) => real === profile).length >
0
);
}

async closeMO2() {
Expand Down
41 changes: 29 additions & 12 deletions src/main/services/modpack.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ export class ModpackService {
}

checkCurrentModpackPathIsValid() {
return this.checkModpackPathIsValid(this.getModpackDirectory());
return (
this.isModpackSet() &&
this.checkModpackPathIsValid(this.getModpackDirectory())
);
}

async refreshModpack() {
Expand Down Expand Up @@ -63,20 +66,34 @@ export class ModpackService {
USER_PREFERENCE_KEYS.MOD_DIRECTORY,
filepath
);
this.configService.setDefaultPreferences({
[USER_PREFERENCE_KEYS.ENB_PROFILE]:
await this.enbService.getEnbPreference(),
[USER_PREFERENCE_KEYS.PRESET]:
await this.modOrganizerService.getProfilePreference(),
[USER_PREFERENCE_KEYS.RESOLUTION]:
this.resolutionService.getCurrentResolution(),
await this.validateConfig();
await this.backupAssets();
await this.enbService.copyCurrentEnbFiles(false);
}

async validateConfig() {
await this.configService.setDefaultPreferences({
[USER_PREFERENCE_KEYS.ENB_PROFILE]: {
value: await this.enbService.getDefaultPreference(),
validate: async () =>
this.enbService.isValid(await this.enbService.getEnbPreference()),
},
[USER_PREFERENCE_KEYS.PRESET]: {
value: await this.modOrganizerService.getDefaultPreference(),
validate: async () =>
this.modOrganizerService.isValid(
await this.modOrganizerService.getProfilePreference()
),
},
[USER_PREFERENCE_KEYS.RESOLUTION]: {
value: this.resolutionService.getCurrentResolution(),
},
});
}

async backupAssets() {
await this.enbService.backupOriginalENBs();
await this.modOrganizerService.backupOriginalProfiles();
await this.enbService.copyENBFiles(
await this.enbService.getEnbPreference(),
false
);
}

isModpackSet(): boolean {
Expand Down
6 changes: 3 additions & 3 deletions src/main/services/startup.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ export class StartupService {
public registerStartupCommands() {
this.startupCommands = [
{
name: "Ensure modpack is valid",
name: "Delete invalid modpack preference",
execute: () => {
if (!this.modpackService.checkCurrentModpackPathIsValid()) {
const modpackDirectory = this.modpackService.getModpackDirectory();
logger.error(
`Current selected modpack (${modpackDirectory}) is invalid. Removing so the user can select a valid one.`
);
// If the modpack is not valid, remove it so the user can select another
this.modpackService.deleteModpackDirectory();
}
},
requiresModpack: true,
},
{
name: "Select modpack",
Expand All @@ -48,11 +48,11 @@ export class StartupService {
public runStartup() {
return Promise.all(
this.startupCommands.map(async (command) => {
logger.debug(`Running startup command: ${command.name}`);
if (
(command.requiresModpack && this.modpackService.isModpackSet()) ||
!command.requiresModpack
) {
logger.debug(`Running startup command: ${command.name}`);
await command.execute();
}
})
Expand Down
16 changes: 12 additions & 4 deletions src/main/services/wabbajack.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { logger } from "@/main/logger";
import { SystemService } from "@/main/services/system.service";
import { service } from "@loopback/core";
import { BindingScope, injectable } from "@loopback/context";
import { ModpackService } from "@/main/services/modpack.service";

@injectable({
scope: BindingScope.SINGLETON,
Expand All @@ -14,7 +15,10 @@ export class WabbajackService {
this.installedModpacksFilename
}`;

constructor(@service(SystemService) private systemService: SystemService) {}
constructor(
@service(SystemService) private systemService: SystemService,
@service(ModpackService) private modpackService: ModpackService
) {}

async getInstalledModpacks() {
if (fs.existsSync(this.wabbajackInstalledModpacksPath)) {
Expand All @@ -29,17 +33,21 @@ export class WabbajackService {
}
}

async getInstalledWildlanderModpackPaths() {
async getInstalledCurrentModpackPaths() {
const { name: modpackName } = this.modpackService.getModpackMetadata();
const modpacks = await this.getInstalledModpacks();
const wildlanderModpacks =
modpacks !== null
? Object.keys(modpacks).filter(
(modpack) =>
modpack !== "$type" &&
modpacks[modpack].ModList.Name === "Wildlander"
modpacks[modpack].ModList.Name === modpackName
)
: [];
logger.debug(`Wildlander modpacks: ${JSON.stringify(wildlanderModpacks)}`);
logger.info(
`Discovered ${wildlanderModpacks.length} ${modpackName} modpack installations in ${this.installedModpacksFilename}`
);
logger.debug(wildlanderModpacks);
return wildlanderModpacks;
}
}
2 changes: 1 addition & 1 deletion src/main/services/window.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export class WindowService {

readFile(path.join(appRoot, pathName), (error, data) => {
if (error) {
console.error(
logger.error(
`Failed to read ${pathName} on ${scheme} protocol`,
error
);
Expand Down
1 change: 1 addition & 0 deletions src/renderer/components/AppDropdownFileSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
grow="true"
v-if="options"
@selected="optionSelected"
:small="true"
/>
</template>
Expand Down
Loading

0 comments on commit 73a47b7

Please sign in to comment.