'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 { applyPatches, wowExePath, wowInstallValid, doAuth } = require('./lib/patch'); 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 tokenEnv = config.github && config.github.token_env; const token = (tokenEnv && String(process.env[tokenEnv] || '').trim()) || String(process.env.GH_TOKEN || process.env.GITHUB_TOKEN || '').trim(); const updateFeedUrl = String(process.env.LAUNCHER_UPDATE_URL || config.update_feed_url || '').trim(); autoUpdateApi = setupAutoUpdater(app, () => mainWindow, { updateFeedUrl, githubOwner: config.github && config.github.owner, githubRepo: config.github && config.github.repo, token, }); 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(); return { configPath, gameDir: config.game_dir || '', authEnabled: !!(config.auth && config.auth.enabled), wowExe: (config.launch && config.launch.exe) || 'Wow.exe', wowOk: wowInstallValid(config), }; }); 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 = 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 }; });