Linux build with proton picker
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import { spawn } from 'child_process';
|
||||
import os from 'os';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import Logger from 'electron-log/main';
|
||||
@@ -12,6 +12,12 @@ import { patchConfig } from '~main/modules/patcher';
|
||||
import { applyLocalePatch } from '~main/modules/localePatch';
|
||||
import { minimizeToTray, restoreFromTray } from '~main/modules/tray';
|
||||
import { getMod } from '~common/mods';
|
||||
import {
|
||||
ensureWowDesktopEntry,
|
||||
gameLaunchEnv,
|
||||
spawnDetachedGame,
|
||||
spawnWithProton
|
||||
} from '~main/modules/proton';
|
||||
|
||||
import { createTRPCRouter, publicProcedure } from '../trpc';
|
||||
|
||||
@@ -33,7 +39,13 @@ type StartResult = { ok: boolean; error?: string };
|
||||
|
||||
export const launcherRouter = createTRPCRouter({
|
||||
start: publicProcedure.mutation(async (): Promise<StartResult> => {
|
||||
const { cleanWdb, minimizeToTrayOnPlay, clientDir } = Preferences.data;
|
||||
const {
|
||||
cleanWdb,
|
||||
minimizeToTrayOnPlay,
|
||||
clientDir,
|
||||
useProton,
|
||||
protonPath
|
||||
} = Preferences.data;
|
||||
if (!clientDir) return { ok: false, error: 'No game folder is set.' };
|
||||
|
||||
const exePath = path.join(clientDir, 'WoW.exe');
|
||||
@@ -64,22 +76,49 @@ export const launcherRouter = createTRPCRouter({
|
||||
|
||||
const octoLocale = Preferences.data.locale || 'enUS';
|
||||
const gameEnv = { ...process.env, OCTO_LOCALE: octoLocale };
|
||||
const launchExe = useLoader ? loaderPath : exePath;
|
||||
const launchArgs = useLoader ? ['WoW.exe'] : [];
|
||||
const wantProton = os.platform() === 'linux' && !!useProton;
|
||||
|
||||
if (wantProton && !protonPath)
|
||||
return {
|
||||
ok: false,
|
||||
error: 'Proton is enabled but no Proton version is selected.'
|
||||
};
|
||||
|
||||
Logger.log(
|
||||
useLoader
|
||||
wantProton
|
||||
? `Launching via Proton${useLoader ? ' + VanillaFixes' : ''} (OCTO_LOCALE=${octoLocale})...`
|
||||
: useLoader
|
||||
? `Launching via VanillaFixes (OCTO_LOCALE=${octoLocale})...`
|
||||
: `Launching ${exePath} (OCTO_LOCALE=${octoLocale})...`
|
||||
);
|
||||
const child = useLoader
|
||||
? spawn(loaderPath, ['WoW.exe'], {
|
||||
env: gameEnv,
|
||||
cwd: clientDir,
|
||||
detached: !minimizeToTrayOnPlay
|
||||
})
|
||||
: spawn(exePath, {
|
||||
env: gameEnv,
|
||||
cwd: clientDir,
|
||||
detached: !minimizeToTrayOnPlay
|
||||
});
|
||||
|
||||
const launchEnv = gameLaunchEnv(gameEnv);
|
||||
if (os.platform() === 'linux')
|
||||
await ensureWowDesktopEntry(clientDir).catch(e =>
|
||||
Logger.warn('Failed to write WoW desktop entry', e)
|
||||
);
|
||||
|
||||
let child;
|
||||
try {
|
||||
child = wantProton
|
||||
? await spawnWithProton({
|
||||
protonDir: protonPath!,
|
||||
exePath: launchExe,
|
||||
args: launchArgs,
|
||||
cwd: clientDir,
|
||||
env: launchEnv
|
||||
})
|
||||
: spawnDetachedGame(launchExe, launchArgs, {
|
||||
env: launchEnv,
|
||||
cwd: clientDir
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.error('Failed to launch the game', e);
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
return { ok: false, error: `Failed to launch the game: ${message}` };
|
||||
}
|
||||
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
@@ -93,6 +132,13 @@ export const launcherRouter = createTRPCRouter({
|
||||
}
|
||||
|
||||
child.on('error', e => Logger.error('Game process error', e));
|
||||
// VanillaFixes / Proton often exit right after spawning WoW — don't treat
|
||||
// that as the game ending. Tray restore uses isGameRunning polling.
|
||||
child.on('exit', code => {
|
||||
Logger.log(
|
||||
`Launch helper exited (code=${code}); tracking WoW.exe separately`
|
||||
);
|
||||
});
|
||||
|
||||
if (!minimizeToTrayOnPlay) {
|
||||
mainWindow?.close();
|
||||
@@ -100,10 +146,34 @@ export const launcherRouter = createTRPCRouter({
|
||||
}
|
||||
|
||||
minimizeToTray();
|
||||
child.on('exit', () => {
|
||||
Logger.log('WoW stopped');
|
||||
restoreFromTray();
|
||||
});
|
||||
void watchUntilGameExits(exePath);
|
||||
return { ok: true };
|
||||
})
|
||||
});
|
||||
|
||||
const sleep = (ms: number) => new Promise<void>(r => setTimeout(r, ms));
|
||||
|
||||
/** Wait for WoW.exe to appear (chainloader/proton lag), then restore tray on exit. */
|
||||
const watchUntilGameExits = async (exePath: string) => {
|
||||
const appearDeadline = Date.now() + 45_000;
|
||||
let seen = false;
|
||||
while (Date.now() < appearDeadline) {
|
||||
if (await isGameRunning(exePath)) {
|
||||
seen = true;
|
||||
break;
|
||||
}
|
||||
await sleep(500);
|
||||
}
|
||||
if (!seen) {
|
||||
Logger.warn('WoW.exe never appeared after launch; restoring tray');
|
||||
restoreFromTray();
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.log('WoW.exe detected; waiting for it to exit...');
|
||||
while (await isGameRunning(exePath)) {
|
||||
await sleep(2000);
|
||||
}
|
||||
Logger.log('WoW stopped');
|
||||
restoreFromTray();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user