Skip to content

Commit

Permalink
feat: enable ultrawide mods when that resolution is selected
Browse files Browse the repository at this point in the history
  • Loading branch information
MattLish committed Jun 16, 2022
1 parent 93bb232 commit 6eaf0dd
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 37 deletions.
12 changes: 12 additions & 0 deletions src/additional-instructions.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,17 @@
{
"action": "disable-ultra-widescreen",
"version": "1.0.0"
},
{
"action": "enable-mod",
"type": "resolution-ratio",
"target": "21:9",
"mod": "Wildlander 21-9 Resolution Support"
},
{
"action": "enable-mod",
"type": "resolution-ratio",
"target": "32:9",
"mod": "Wildlander 32-9 Resolution Support"
}
]
60 changes: 60 additions & 0 deletions src/main/services/instruction.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ export class InstructionService {
}
break;

case "enable-mod":
if (instruction.target === target) {
await this.toggleMod(instruction.mod, "enable");
} else {
await this.toggleMod(instruction.mod, "disable");
}
break;

case "disable-ultra-widescreen":
return true;
}
Expand Down Expand Up @@ -92,6 +100,58 @@ export class InstructionService {
}
}

/**
* modlist.txt: dictates the order, enabling, and disabling of mods (left panel in MO2)
* Enabled mods have a + symbol, like this: +Wildlander FULL
* Disabled mods have a - symbol, like this: -Wildlander FULL
*/
async toggleMod(mod: string, state: "enable" | "disable"): Promise<void> {
logger.info(`Toggling mod ${mod} to state ${state}`);

for (const modlistFile of await this.getModlistFiles()) {
logger.debug(`Toggling mod in ${modlistFile}`);
const mods = this.getModsFromFile(modlistFile);

const editedFile = [];
for await (let currentMod of mods) {
if (
currentMod.replace("+", "") === mod ||
currentMod.replace("-", "") === mod
) {
if (state === "disable" && currentMod.startsWith("+")) {
logger.debug(`Disabling mod ${mod}`);
currentMod = currentMod.replace("+", "-");
} else if (state === "enable" && currentMod.startsWith("-")) {
logger.debug(`Enabling mod ${mod}`);
currentMod = currentMod.replace("-", "+");
}
}

editedFile.push(currentMod);
}

await fs.promises.writeFile(
modlistFile,
editedFile.join(os.EOL),
"utf-8"
);
}
}

async getModlistFiles() {
return (await this.profileService.getProfiles()).map(
(profile) =>
`${this.profileService.profileDirectory()}/${profile.real}/modlist.txt`
);
}

getModsFromFile(modsFile: string) {
return readline.createInterface({
input: fs.createReadStream(modsFile),
crlfDelay: Infinity,
});
}

getPluginsFromFile(pluginsFile: PathLike) {
return readline.createInterface({
input: fs.createReadStream(pluginsFile),
Expand Down
5 changes: 4 additions & 1 deletion src/main/services/launcher.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ export class LauncherService {
await this.validateConfig();
await this.backupAssets();
await this.enbService.resetCurrentEnb(false);
await this.resolutionService.setResolutionInGraphicsSettings();
await this.resolutionService.setResolution(
this.resolutionService.getCurrentResolution()
);
await this.resolutionService.setShouldDisableUltraWidescreen();
}

async validateConfig() {
Expand Down
122 changes: 91 additions & 31 deletions src/main/services/resolution.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import { InstructionService } from "@/main/services/instruction.service";
})
export class ResolutionService {
private resolutionsCache!: Resolution[];
private ultraWidescreenDisabled: boolean | undefined;

constructor(
@service(ConfigService) private configService: ConfigService,
@service(InstructionService)
private modpackInstructionsService: InstructionService
private instructionsService: InstructionService
) {}

getResourcePath() {
Expand All @@ -43,18 +44,50 @@ export class ResolutionService {
return width / height > 1.78;
}

private shouldDisableUltraWidescreen() {
const instructions = this.modpackInstructionsService
getSupportedRatios() {
return [
{
key: "16:9",
value: 16 / 9,
},
{
key: "21:9",
value: 21 / 9,
},
{
key: "32:9",
value: 32 / 9,
},
];
}

getClosestSupportedRatio({ width, height }: Resolution) {
const supportedRatios = this.getSupportedRatios();
const ratio = width / height;

const closestValue = supportedRatios
.map((x) => x.value)
.reduce((prev, curr) =>
Math.abs(curr - ratio) < Math.abs(prev - ratio) ? curr : prev
);
const closestRatio = supportedRatios.find(
(x) => x.value === closestValue
)?.key;
logger.debug(`Found closest ratio for ${width}x${height}: ${closestRatio}`);
return closestRatio;
}

public async setShouldDisableUltraWidescreen() {
const instructions = this.instructionsService
.getInstructions()
.filter((x) => x.action === "disable-ultra-widescreen");
return this.modpackInstructionsService.execute(instructions);
this.ultraWidescreenDisabled =
(await this.instructionsService.execute(instructions)) ?? false;
logger.debug(`Ultra-widescreen disabled: ${this.ultraWidescreenDisabled}`);
}

async isUnsupportedResolution(resolution: Resolution) {
return (
(await this.shouldDisableUltraWidescreen()) &&
this.isUltraWidescreen(resolution)
);
return this.ultraWidescreenDisabled && this.isUltraWidescreen(resolution);
}

getCurrentResolution(): Resolution {
Expand Down Expand Up @@ -83,7 +116,23 @@ export class ResolutionService {
USER_PREFERENCE_KEYS.RESOLUTION,
resolution
);
return this.setResolutionInGraphicsSettings();
await this.setResolutionInGraphicsSettings();
return this.postSetResolution(resolution);
}

postSetResolution(resolution: Resolution) {
const resolutionInstructions = this.getResolutionInstructions();
const closestRatio = this.getClosestSupportedRatio(resolution);
return this.instructionsService.execute(
resolutionInstructions,
closestRatio
);
}

getResolutionInstructions() {
return this.instructionsService
.getInstructions()
.filter((instruction) => instruction.type === "resolution-ratio");
}

async getSupportedResolutions() {
Expand Down Expand Up @@ -127,7 +176,7 @@ export class ResolutionService {
}

async getResolutions(): Promise<Resolution[]> {
logger.info("Getting resolutions");
logger.debug("Getting resolutions");

if (this.resolutionsCache) {
logger.debug(
Expand All @@ -143,10 +192,12 @@ export class ResolutionService {
// Also, return an ultrawide resolution for testing
if (os.platform() !== "win32") {
return [
{ width: 7680, height: 4320 },
{ width: 7680, height: 4320 }, // 16:9
currentResolution,
{ width: 3440, height: 1440 }, // Ultra widescreen
{ width: 1920, height: 1080 },
{ width: 3840, height: 1080 }, // Ultra widescreen (32:9)
{ width: 3440, height: 1440 }, // Ultra widescreen (21:9)
{ width: 1920, height: 1080 }, // 16:9
{ width: 1280, height: 800 }, // 16:10
];
} else {
const resolutions = [...new Set(await this.getSupportedResolutions())]
Expand Down Expand Up @@ -198,12 +249,34 @@ export class ResolutionService {
)}/mods/${modpackName}/SKSE/Plugins/SSEDisplayTweaks.ini`;
}

//Borderless upscale will need to be toggled depending on if the user is using an ultra-widescreen monitor.
// Without upscale, users on ultra-widescreen but using a smaller resolution will get stretching
// User has normal monitor - enable upscale all the time
// User has ultra-widescreen and selects non-ultra-widescreen resolution - Disable upscaling
// User has ultra-widescreen and selects ultra-widescreen resolution - Enable upscaling
shouldEnableBorderlessUpscale(resolution: Resolution) {
const { width, height } = this.getCurrentResolution();

const monitorIsUltraWidescreen = this.isUltraWidescreen({ width, height });
const preferenceIsUltraWidescreen = this.isUltraWidescreen(resolution);
const borderlessUpscale =
monitorIsUltraWidescreen && preferenceIsUltraWidescreen
? true
: !(monitorIsUltraWidescreen && !preferenceIsUltraWidescreen);

logger.info(
`Setting borderless upscale for ${width}x${height}: ${borderlessUpscale}`
);
return borderlessUpscale;
}

async setResolutionInGraphicsSettings() {
const { width: widthPreference, height: heightPreference } =
userPreferences.get(USER_PREFERENCE_KEYS.RESOLUTION) as Resolution;
const { width, height } = userPreferences.get(
USER_PREFERENCE_KEYS.RESOLUTION
) as Resolution;

logger.info(
`Setting resolution in ${this.skyrimGraphicsSettingsPath()} to ${widthPreference} x ${heightPreference}`
`Setting resolution in ${this.skyrimGraphicsSettingsPath()} to ${width} x ${height}`
);
const SkyrimGraphicSettings = parse(
await fs.promises.readFile(this.skyrimGraphicsSettingsPath(), "utf-8"),
Expand All @@ -212,24 +285,11 @@ export class ResolutionService {

(
SkyrimGraphicSettings.Render as IIniObjectSection
).Resolution = `${widthPreference}x${heightPreference}`;

const { scaleFactor } = screen.getPrimaryDisplay();
const { width, height } = this.getCurrentResolution();

// If the user has an ultra-widescreen monitor,
// disable borderlessUpscale so the game doesn't get stretched.
const borderlessUpscale = !this.isUltraWidescreen({ width, height });

logger.debug(
`Setting borderless upscale for ${width * scaleFactor}x${
height * scaleFactor
}: ${borderlessUpscale}`
);
).Resolution = `${width}x${height}`;

// If the selected resolution is ultra-widescreen, don't upscale the image otherwise it gets stretched
(SkyrimGraphicSettings.Render as IIniObjectSection).BorderlessUpscale =
borderlessUpscale;
this.shouldEnableBorderlessUpscale({ width, height });

await fs.promises.writeFile(
this.skyrimGraphicsSettingsPath(),
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/components/Resolution.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
<div class="l-row">
<span class="material-icons c-resolution__info-icon u-text"> info </span>
<div>
Ultra-widescreen resolutions are not supported.
Ultra-widescreen resolutions are not supported in this modpack.
<BaseLink
href="https://github.com/Wildlander-mod/Support/blob/master/Docs/SSEFAQ.md#does-this-pack-support-ultrawide-resolutions"
href="https://github.com/Wildlander-mod/Support/wiki/FAQ#does-this-pack-support-ultrawide-resolutions"
:underline="true"
>
More info.
Expand Down
17 changes: 14 additions & 3 deletions src/types/additional-instructions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,25 @@ interface AdditionalInstructionBase {
version?: string;
}

interface PluginOrModInstruction extends AdditionalInstructionBase {
type: "enb" | "resolution-ratio";
target: string;
}

// Instruction will be run when enb is changed
// The `target` is the enb that the change will be applied on
interface DisablePluginInstruction extends AdditionalInstructionBase {
type: "enb";
interface DisablePluginInstruction extends PluginOrModInstruction {
action: "disable-plugin";
target: string;
plugin: string;
}

// Instruction will be run when enb is changed
// The `target` is the enb that the change will be applied on
interface EnableModInstruction extends PluginOrModInstruction {
action: "enable-mod";
mod: string;
}

// A single entry of `disable-ultra-widescreen` is enough for the instruction to return true
interface DisableUltraWidescreenInstruction extends AdditionalInstructionBase {
type?: string;
Expand All @@ -20,6 +30,7 @@ interface DisableUltraWidescreenInstruction extends AdditionalInstructionBase {

export type AdditionalInstruction =
| DisablePluginInstruction
| EnableModInstruction
| DisableUltraWidescreenInstruction;

export type AdditionalInstructions = AdditionalInstruction[];

0 comments on commit 6eaf0dd

Please sign in to comment.