f409ffad12
- baked-gitea-channel: http:// for brassnet mirror. - win-game-dir: map Unix /home/... to Z:\ under win32 (Wine folder picker). - resolveGameDir + saveGameDir + patch paths use it; Wow.exe resolved case-insensitively. - Version 1.0.6; README checklist for Wine. Co-authored-by: Cursor <cursoragent@cursor.com>
159 lines
5.3 KiB
JavaScript
159 lines
5.3 KiB
JavaScript
'use strict';
|
|
|
|
const { app, BrowserWindow, ipcMain, dialog, Menu } = require('electron');
|
|
const path = require('path');
|
|
const { spawn } = require('child_process');
|
|
const { loadConfig, saveGameDir, resolveGameDir } = require('./lib/config-store');
|
|
const { normalizeWinGameDir } = require('./lib/win-game-dir');
|
|
const { applyPatches, wowExePath, wowInstallValid, doAuth } = require('./lib/patch');
|
|
const { readPatchState } = require('./lib/patch-manifest');
|
|
const { setupAutoUpdater } = require('./lib/auto-update');
|
|
|
|
let mainWindow;
|
|
let autoUpdateApi = {
|
|
checkNow: async () => ({ skipped: true, reason: 'not initialized' }),
|
|
};
|
|
|
|
function createWindow() {
|
|
mainWindow = new BrowserWindow({
|
|
width: 720,
|
|
height: 640,
|
|
show: false,
|
|
autoHideMenuBar: true,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'preload.cjs'),
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
sandbox: false,
|
|
},
|
|
});
|
|
Menu.setApplicationMenu(null);
|
|
mainWindow.loadFile(path.join(__dirname, 'index.html'));
|
|
mainWindow.once('ready-to-show', () => mainWindow.show());
|
|
}
|
|
|
|
function sendProgress(msg) {
|
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
mainWindow.webContents.send('launcher:progress', msg);
|
|
}
|
|
}
|
|
|
|
async function readMergedConfig() {
|
|
const { configPath, config } = await loadConfig(app);
|
|
const gameDir = resolveGameDir(config, configPath);
|
|
const merged = { ...config, game_dir: gameDir };
|
|
return { configPath, config: merged };
|
|
}
|
|
|
|
app.whenReady().then(async () => {
|
|
createWindow();
|
|
const { config } = await loadConfig(app);
|
|
const ghEnv = config.github && config.github.token_env;
|
|
const githubToken =
|
|
(ghEnv && String(process.env[ghEnv] || '').trim()) ||
|
|
String(process.env.GH_TOKEN || process.env.GITHUB_TOKEN || '').trim();
|
|
const giteaEnv = config.gitea && config.gitea.token_env;
|
|
const giteaToken =
|
|
(giteaEnv && String(process.env[giteaEnv] || '').trim()) ||
|
|
String(process.env.GITEA_TOKEN || '').trim();
|
|
const updateFeedUrl = String(process.env.LAUNCHER_UPDATE_URL || config.update_feed_url || '').trim();
|
|
autoUpdateApi = await setupAutoUpdater(app, () => mainWindow, {
|
|
updateFeedUrl,
|
|
config,
|
|
githubOwner: config.github && config.github.owner,
|
|
githubRepo: config.github && config.github.repo,
|
|
githubToken,
|
|
giteaToken,
|
|
allowGithubLauncherUpdates: config.launcher_updates_from_github === true,
|
|
});
|
|
app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
|
});
|
|
});
|
|
|
|
app.on('window-all-closed', () => {
|
|
if (process.platform !== 'darwin') app.quit();
|
|
});
|
|
|
|
ipcMain.handle('launcher:load', async () => {
|
|
const { configPath, config } = await readMergedConfig();
|
|
let clientBuild = '';
|
|
if (wowInstallValid(config)) {
|
|
const st = await readPatchState(config.game_dir);
|
|
if (st && st.client_build) clientBuild = String(st.client_build);
|
|
}
|
|
return {
|
|
configPath,
|
|
gameDir: config.game_dir || '',
|
|
authEnabled: !!(config.auth && config.auth.enabled),
|
|
wowExe: (config.launch && config.launch.exe) || 'Wow.exe',
|
|
wowOk: wowInstallValid(config),
|
|
clientBuild,
|
|
};
|
|
});
|
|
|
|
ipcMain.handle('launcher:saveGameDir', async (_e, dir) => {
|
|
const trimmed = String(dir || '').trim();
|
|
if (!trimmed) throw new Error('folder path is empty');
|
|
const { configPath } = await loadConfig(app);
|
|
const norm =
|
|
process.platform === 'win32' ? normalizeWinGameDir(path.normalize(trimmed)) : path.normalize(trimmed);
|
|
const probe = { ...(await readMergedConfig()).config, game_dir: norm };
|
|
if (!wowInstallValid(probe)) {
|
|
throw new Error(`That folder does not contain ${(probe.launch && probe.launch.exe) || 'Wow.exe'}`);
|
|
}
|
|
const c = await saveGameDir(configPath, norm);
|
|
const merged = { ...c, game_dir: resolveGameDir(c, configPath) };
|
|
return { ok: true, gameDir: merged.game_dir, wowOk: wowInstallValid(merged) };
|
|
});
|
|
|
|
ipcMain.handle('launcher:pickFolder', async (_e, startDir) => {
|
|
const win = BrowserWindow.getFocusedWindow() || mainWindow;
|
|
const r = await dialog.showOpenDialog(win, {
|
|
title: 'Select World of Warcraft 3.3.5a folder',
|
|
properties: ['openDirectory', 'createDirectory'],
|
|
defaultPath: startDir && String(startDir).trim() ? String(startDir).trim() : undefined,
|
|
});
|
|
if (r.canceled || !r.filePaths || !r.filePaths[0]) return { canceled: true, path: '' };
|
|
return { canceled: false, path: r.filePaths[0] };
|
|
});
|
|
|
|
ipcMain.handle('launcher:auth', async (_e, { user, pass }) => {
|
|
const { config } = await readMergedConfig();
|
|
await doAuth(config, user, pass);
|
|
return { ok: true };
|
|
});
|
|
|
|
ipcMain.handle('launcher:sync', async () => {
|
|
const { config } = await readMergedConfig();
|
|
if (!wowInstallValid(config)) {
|
|
throw new Error('Set a valid WoW folder (must contain Wow.exe) first.');
|
|
}
|
|
await applyPatches(config, sendProgress);
|
|
return { ok: true };
|
|
});
|
|
|
|
ipcMain.handle('launcher:checkUpdates', async () => {
|
|
try {
|
|
return await autoUpdateApi.checkNow();
|
|
} catch (e) {
|
|
const msg = e && (e.message || String(e));
|
|
return { ok: false, error: msg };
|
|
}
|
|
});
|
|
|
|
ipcMain.handle('launcher:play', async () => {
|
|
const { config } = await readMergedConfig();
|
|
const exe = wowExePath(config);
|
|
const args = (config.launch && config.launch.args) || [];
|
|
const child = spawn(exe, args, {
|
|
cwd: config.game_dir,
|
|
detached: true,
|
|
stdio: 'ignore',
|
|
windowsHide: true,
|
|
shell: false,
|
|
});
|
|
child.unref();
|
|
return { ok: true };
|
|
});
|