Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
sanasol
2026-01-25 01:27:29 +01:00
19 changed files with 1216 additions and 262 deletions

View File

@@ -15,12 +15,6 @@ class AppUpdater {
}
setupAutoUpdater() {
// Enable dev mode for testing (reads dev-app-update.yml)
// Only enable in development, not in production builds
if (process.env.NODE_ENV === 'development' || !app.isPackaged) {
autoUpdater.forceDevUpdateConfig = true;
console.log('Dev update mode enabled - using dev-app-update.yml');
}
// Configure logger for electron-updater
// Create a compatible logger interface
@@ -176,7 +170,7 @@ class AppUpdater {
console.warn('macOS update error: App may not be code-signed. Auto-update requires code signing.');
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
this.mainWindow.webContents.send('update-error', {
message: 'Auto-update requires code signing. Please download manually from GitHub.',
message: 'Please download manually from GitHub.',
code: err.code,
isMacSigningError: true,
requiresManualDownload: true,

View File

@@ -520,9 +520,17 @@ async function installGame(playerName = 'Player', progressCallback, javaPathOver
try {
await downloadJRE(progressCallback, customCacheDir, customJreDir);
} catch (error) {
// Don't immediately fall back to system Java for JRE download errors - let user retry
if (error.isJREError) {
console.error('[Install] JRE download failed, allowing user retry:', error.message);
throw error; // Re-throw JRE errors to trigger retry UI
}
// For non-download JRE errors, fall back to system Java
const fallback = await detectSystemJava();
if (fallback) {
javaBin = fallback;
console.log('[Install] Using system Java as fallback');
} else {
throw error;
}

View File

@@ -9,7 +9,7 @@ const tar = require('tar');
const { expandHome, JRE_DIR } = require('../core/paths');
const { getOS, getArch } = require('../utils/platformUtils');
const { loadConfig } = require('../core/config');
const { downloadFile } = require('../utils/fileManager');
const { downloadFile, retryDownload } = require('../utils/fileManager');
const execFileAsync = promisify(execFile);
const JAVA_EXECUTABLE = 'java' + (process.platform === 'win32' ? '.exe' : '');
@@ -188,6 +188,20 @@ async function getJavaDetection() {
};
}
// Manual retry function for JRE downloads
async function retryJREDownload(url, cacheFile, progressCallback) {
console.log('Initiating manual JRE retry...');
// Ensure cache directory exists before retrying
const cacheDir = path.dirname(cacheFile);
if (!fs.existsSync(cacheDir)) {
console.log('Creating JRE cache directory:', cacheDir);
fs.mkdirSync(cacheDir, { recursive: true });
}
return await retryDownload(url, cacheFile, progressCallback);
}
async function downloadJRE(progressCallback, cacheDir, jreDir = JRE_DIR) {
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir, { recursive: true });
@@ -230,7 +244,40 @@ async function downloadJRE(progressCallback, cacheDir, jreDir = JRE_DIR) {
progressCallback('Fetching Java runtime...', null, null, null, null);
}
console.log('Fetching Java runtime...');
await downloadFile(platform.url, cacheFile, progressCallback);
let jreFile;
try {
jreFile = await downloadFile(platform.url, cacheFile, progressCallback);
// If downloadFile returns false or undefined, it means the download failed
// We should retry the download with a manual retry
if (!jreFile || typeof jreFile !== 'string') {
console.log('[JRE Download] JRE file download failed or incomplete, attempting retry...');
jreFile = await retryJREDownload(platform.url, cacheFile, progressCallback);
}
// Double-check we have a valid file
if (!jreFile || typeof jreFile !== 'string') {
throw new Error(`JRE download failed: received invalid path ${jreFile}. Please retry download.`);
}
} catch (downloadError) {
console.error('[JRE Download] JRE download failed:', downloadError.message);
// Enhance error with retry information for the UI
const enhancedError = new Error(`JRE download failed: ${downloadError.message}`);
enhancedError.originalError = downloadError;
enhancedError.canRetry = downloadError.isConnectionLost ? false : (downloadError.canRetry !== false);
enhancedError.jreUrl = platform.url;
enhancedError.jreDest = cacheFile;
enhancedError.osName = osName;
enhancedError.arch = arch;
enhancedError.fileName = fileName;
enhancedError.cacheDir = cacheDir;
enhancedError.isJREError = true; // Flag to identify JRE errors
enhancedError.isConnectionLost = downloadError.isConnectionLost || false;
throw enhancedError;
}
console.log('Download finished');
}

View File

@@ -423,7 +423,8 @@ async function downloadFile(url, dest, progressCallback, maxRetries = 5) {
const canRetry = (error.canRetry === false) ? false : isRetryable;
if (!canRetry || attempt === maxRetries - 1) {
retryState.canRetry = false;
// Don't set retryState.canRetry to false for max retries - user should still be able to retry manually
retryState.canRetry = error.canRetry === false ? false : true;
console.error(`Non-retryable error or max retries reached: ${error.code || error.message}`);
break;
}
@@ -439,6 +440,9 @@ async function downloadFile(url, dest, progressCallback, maxRetries = 5) {
enhancedError.retryState = retryState;
enhancedError.lastError = lastError;
enhancedError.detailedError = detailedError;
// Allow manual retry unless it's a connection lost error
enhancedError.canRetry = !lastError?.isConnectionLost && lastError?.canRetry !== false;
throw enhancedError;
}
@@ -543,6 +547,13 @@ async function retryDownload(url, dest, progressCallback, previousError = null)
additionalRetries = Math.max(2, 5 - previousError.retryState.attempts);
}
// Ensure cache directory exists before retrying
const destDir = path.dirname(dest);
if (!fs.existsSync(destDir)) {
console.log('Creating cache directory:', destDir);
fs.mkdirSync(destDir, { recursive: true });
}
try {
await downloadFile(url, dest, progressCallback, additionalRetries);
console.log('Manual retry successful');