mirror of
https://github.com/dream-pep/koring-launcher.git
synced 2026-09-12 05:45:18 +08:00
Introduce crash monitoring and an OOBE onboarding flow. Adds a crash logger (koring-crash.log), crash-monitor handler/window, preload-crash bridge, crash UI (crash.html, src/crash.tsx, pages/crash) and a debug page. Main process now sets up crash listeners, startup checks (first-launch, .minecraft), and a config reset/factory-reset that removes Koring.yml, koring-auth.json and background cache and can relaunch the app. Exposes config preloading to renderer, adds OOBE pages/layout, confirm dialog store/UI, trims Silk renderer settings, bumps version to 1.0.1 and adds motion dependency, and registers crash.html in Vite. Various related type and config updates included.
196 lines
5.3 KiB
TypeScript
196 lines
5.3 KiB
TypeScript
import electron from 'electron';
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
import { registerConfigHandlers } from './handlers/config';
|
|
import { registerAuthHandlers } from './handlers/auth';
|
|
import { registerInstallHandlers } from './handlers/install';
|
|
import { registerLaunchHandlers } from './handlers/launch';
|
|
import { registerModsHandlers } from './handlers/mods';
|
|
import { registerInstanceHandlers } from './handlers/instance';
|
|
import { registerBackgroundHandlers } from './handlers/background';
|
|
import { registerTaskHandlers } from './handlers/task';
|
|
import { registerSystemHandlers } from './handlers/system';
|
|
import { registerWindowHandlers } from './handlers/window';
|
|
import { registerCrashHandlers, setupCrashListeners, testCrashDialog } from './handlers/crash-monitor';
|
|
import { loadConfig, saveConfig, configExists } from './config';
|
|
|
|
const { app } = electron;
|
|
|
|
const isDev = !app.isPackaged;
|
|
|
|
// GPU acceleration flags
|
|
app.commandLine.appendSwitch('enable-gpu-rasterization');
|
|
app.commandLine.appendSwitch('enable-zero-copy');
|
|
|
|
const win: { mainWindow: electron.BrowserWindow | null; splashWindow: electron.BrowserWindow | null } = {
|
|
mainWindow: null,
|
|
splashWindow: null,
|
|
};
|
|
|
|
// Startup checks: .minecraft dir + config file + first launch detection
|
|
function runStartupChecks(): { isFirstLaunch: boolean; config: ReturnType<typeof loadConfig> } {
|
|
const dataPath = app.isPackaged
|
|
? path.dirname(app.getPath('exe'))
|
|
: path.join(__dirname, '..');
|
|
|
|
// 1. Ensure .minecraft directory exists
|
|
const minecraftDir = path.join(dataPath, '.minecraft');
|
|
if (!fs.existsSync(minecraftDir)) {
|
|
fs.mkdirSync(minecraftDir, { recursive: true });
|
|
}
|
|
|
|
// 2. Check if config exists, if not create with defaults
|
|
const hasConfig = configExists();
|
|
const isFirstLaunch = !hasConfig;
|
|
|
|
// 3. Load (or create) config
|
|
const config = loadConfig();
|
|
if (isFirstLaunch) {
|
|
saveConfig(config);
|
|
}
|
|
|
|
return { isFirstLaunch, config };
|
|
}
|
|
|
|
function createSplashWindow(): electron.BrowserWindow {
|
|
const iconPath = isDev
|
|
? path.join(__dirname, '../build/icon.ico')
|
|
: path.join(__dirname, '../build/icon.ico');
|
|
|
|
const splash = new electron.BrowserWindow({
|
|
width: 480,
|
|
height: 320,
|
|
transparent: true,
|
|
frame: false,
|
|
resizable: false,
|
|
skipTaskbar: true,
|
|
alwaysOnTop: true,
|
|
icon: iconPath,
|
|
webPreferences: {
|
|
nodeIntegration: false,
|
|
contextIsolation: true,
|
|
},
|
|
});
|
|
|
|
if (isDev) {
|
|
splash.loadURL('http://localhost:1420/splash.html');
|
|
} else {
|
|
splash.loadFile(path.join(__dirname, '../dist/splash.html'));
|
|
}
|
|
|
|
return splash;
|
|
}
|
|
|
|
function createMainWindow(): electron.BrowserWindow {
|
|
const iconPath = isDev
|
|
? path.join(__dirname, '../build/icon.ico')
|
|
: path.join(__dirname, '../build/icon.ico');
|
|
|
|
const main = new electron.BrowserWindow({
|
|
width: 1000,
|
|
height: 700,
|
|
minWidth: 800,
|
|
minHeight: 600,
|
|
frame: false,
|
|
transparent: false,
|
|
resizable: true,
|
|
show: false,
|
|
icon: iconPath,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'preload.js'),
|
|
nodeIntegration: false,
|
|
contextIsolation: true,
|
|
sandbox: false,
|
|
},
|
|
});
|
|
|
|
if (isDev) {
|
|
main.loadURL('http://localhost:1420');
|
|
} else {
|
|
main.loadFile(path.join(__dirname, '../dist/index.html'));
|
|
}
|
|
|
|
main.on('maximize', () => {
|
|
main.webContents.send('window:resized');
|
|
});
|
|
|
|
main.on('unmaximize', () => {
|
|
main.webContents.send('window:resized');
|
|
});
|
|
|
|
return main;
|
|
}
|
|
|
|
function registerAllHandlers() {
|
|
registerConfigHandlers();
|
|
registerAuthHandlers();
|
|
registerInstallHandlers(win);
|
|
registerLaunchHandlers(win);
|
|
registerModsHandlers();
|
|
registerInstanceHandlers(win);
|
|
registerBackgroundHandlers();
|
|
registerTaskHandlers(win);
|
|
registerSystemHandlers();
|
|
registerWindowHandlers(win);
|
|
registerCrashHandlers();
|
|
}
|
|
|
|
app.whenReady().then(() => {
|
|
registerAllHandlers();
|
|
|
|
// Run startup checks before creating windows
|
|
const { isFirstLaunch, config } = runStartupChecks();
|
|
|
|
// 1. Show splash immediately
|
|
win.splashWindow = createSplashWindow();
|
|
|
|
// 2. Create main window in background
|
|
win.mainWindow = createMainWindow();
|
|
|
|
// 3. Preload config into renderer before it renders
|
|
win.mainWindow.webContents.on('did-finish-load', () => {
|
|
win.mainWindow?.webContents.send('config:preload', { config, isFirstLaunch });
|
|
});
|
|
|
|
// 4. When main window finishes loading, wait a minimum time then transition
|
|
let mainReady = false;
|
|
let splashMinTimeDone = false;
|
|
|
|
const tryTransition = () => {
|
|
if (mainReady && splashMinTimeDone) {
|
|
if (win.mainWindow && !win.mainWindow.isDestroyed()) {
|
|
win.mainWindow.show();
|
|
win.mainWindow.focus();
|
|
}
|
|
if (win.splashWindow && !win.splashWindow.isDestroyed()) {
|
|
win.splashWindow.close();
|
|
win.splashWindow = null;
|
|
}
|
|
}
|
|
};
|
|
|
|
win.mainWindow.once('ready-to-show', () => {
|
|
mainReady = true;
|
|
tryTransition();
|
|
});
|
|
|
|
// Setup crash listeners on main window
|
|
setupCrashListeners(win.mainWindow);
|
|
|
|
// Minimum splash display time (1.5s)
|
|
setTimeout(() => {
|
|
splashMinTimeDone = true;
|
|
tryTransition();
|
|
}, 1500);
|
|
});
|
|
|
|
app.on('window-all-closed', () => {
|
|
app.quit();
|
|
});
|
|
|
|
app.on('activate', () => {
|
|
if (electron.BrowserWindow.getAllWindows().length === 0) {
|
|
win.mainWindow = createMainWindow();
|
|
}
|
|
});
|