diff --git a/GUI/index.html b/GUI/index.html
index 2cb204b..fa94706 100644
--- a/GUI/index.html
+++ b/GUI/index.html
@@ -619,9 +619,17 @@
-
+
+
+
+
+
diff --git a/GUI/js/ui.js b/GUI/js/ui.js
index b09399e..44182cb 100644
--- a/GUI/js/ui.js
+++ b/GUI/js/ui.js
@@ -10,6 +10,8 @@ let progressErrorContainer;
let progressErrorMessage;
let progressRetryInfo;
let progressRetryBtn;
+let progressJRRetryBtn;
+let progressPWRRetryBtn;
// Download retry state
let currentDownloadState = {
@@ -199,7 +201,8 @@ function updateProgress(data) {
if ((data.error || (data.message && data.message.includes('failed'))) &&
!(data.retryState && data.retryState.isAutomaticRetry)) {
const errorType = categorizeError(data.message);
- showDownloadError(data.message, data.canRetry, errorType);
+ console.log('[UI] Showing download error:', { message: data.message, canRetry: data.canRetry, errorType });
+ showDownloadError(data.message, data.canRetry, errorType, data);
} else if (data.percent === 100) {
hideDownloadError();
} else if (data.retryState && data.retryState.isAutomaticRetry) {
@@ -230,9 +233,17 @@ function updateRetryState(retryState) {
}
}
-function showDownloadError(errorMessage, canRetry = true, errorType = 'general') {
- if (!progressErrorContainer || !progressErrorMessage || !progressRetryBtn) return;
+function showDownloadError(errorMessage, canRetry = true, errorType = 'general', data = null) {
+ if (!progressErrorContainer || !progressErrorMessage) return;
+ console.log('[UI] showDownloadError called with:', { errorMessage, canRetry, errorType, data });
+ console.log('[UI] Data properties:', {
+ hasData: !!data,
+ hasRetryData: !!(data && data.retryData),
+ dataErrorType: data && data.errorType,
+ dataIsJREError: data && data.retryData && data.retryData.isJREError
+ });
+
currentDownloadState.lastError = errorMessage;
currentDownloadState.canRetry = canRetry;
currentDownloadState.errorType = errorType;
@@ -242,13 +253,37 @@ function showDownloadError(errorMessage, canRetry = true, errorType = 'general')
currentDownloadState.branch = data.retryData.branch;
currentDownloadState.fileName = data.retryData.fileName;
currentDownloadState.cacheDir = data.retryData.cacheDir;
+ // Override errorType if specified in data
+ if (data.errorType) {
+ currentDownloadState.errorType = data.errorType;
+ }
}
+ // Hide all retry buttons first
+ if (progressRetryBtn) progressRetryBtn.style.display = 'none';
+ if (progressJRRetryBtn) progressJRRetryBtn.style.display = 'none';
+ if (progressPWRRetryBtn) progressPWRRetryBtn.style.display = 'none';
+
// User-friendly error messages
const userMessage = getErrorMessage(errorMessage, errorType);
progressErrorMessage.textContent = userMessage;
progressErrorContainer.style.display = 'block';
- progressRetryBtn.style.display = canRetry ? 'block' : 'none';
+
+ // Show appropriate retry button based on error type
+ if (canRetry) {
+ if (errorType === 'jre') {
+ if (progressJRRetryBtn) {
+ console.log('[UI] Showing JRE retry button');
+ progressJRRetryBtn.style.display = 'block';
+ }
+ } else {
+ // All other errors use PWR retry button (game download, butler, etc.)
+ if (progressPWRRetryBtn) {
+ console.log('[UI] Showing PWR retry button');
+ progressPWRRetryBtn.style.display = 'block';
+ }
+ }
+ }
// Add visual indicators based on error type
progressErrorContainer.className = `progress-error-container error-${errorType}`;
@@ -261,6 +296,11 @@ function showDownloadError(errorMessage, canRetry = true, errorType = 'general')
function hideDownloadError() {
if (!progressErrorContainer) return;
+ // Hide all retry buttons
+ if (progressRetryBtn) progressRetryBtn.style.display = 'none';
+ if (progressJRRetryBtn) progressJRRetryBtn.style.display = 'none';
+ if (progressPWRRetryBtn) progressPWRRetryBtn.style.display = 'none';
+
progressErrorContainer.style.display = 'none';
currentDownloadState.canRetry = false;
currentDownloadState.lastError = null;
@@ -589,6 +629,8 @@ function setupUI() {
progressErrorMessage = document.getElementById('progressErrorMessage');
progressRetryInfo = document.getElementById('progressRetryInfo');
progressRetryBtn = document.getElementById('progressRetryBtn');
+ progressJRRetryBtn = document.getElementById('progressJRRetryBtn');
+ progressPWRRetryBtn = document.getElementById('progressPWRRetryBtn');
// Setup draggable progress bar
setupProgressDrag();
@@ -784,6 +826,8 @@ function categorizeError(message) {
return 'space';
} else if (msg.includes('conflict') || msg.includes('already exists')) {
return 'conflict';
+ } else if (msg.includes('jre') || msg.includes('java runtime')) {
+ return 'jre';
} else {
return 'general';
}
@@ -812,6 +856,8 @@ function getErrorMessage(technicalMessage, errorType) {
return 'Insufficient disk space. Free up space and retry.';
case 'conflict':
return 'Installation directory conflict. Please retry.';
+ case 'jre':
+ return 'Java runtime download failed. Please retry.';
default:
return 'Download failed. Please retry.';
}
@@ -839,70 +885,192 @@ function updateConnectionQuality(quality) {
// Enhanced retry button setup
function setupRetryButton() {
- if (!progressRetryBtn) return;
-
- progressRetryBtn.addEventListener('click', async () => {
- if (!currentDownloadState.canRetry || currentDownloadState.isDownloading) {
- return;
- }
-
- // Disable retry button during retry
- progressRetryBtn.disabled = true;
- progressRetryBtn.textContent = '🔄 Retrying...';
- progressRetryBtn.classList.add('retrying');
- currentDownloadState.isDownloading = true;
-
- try {
- // Hide error state during retry
- hideDownloadError();
-
- // Reset retry info styling for manual retries
- if (progressRetryInfo) {
- progressRetryInfo.style.background = '';
- progressRetryInfo.style.color = '';
- }
-
- // Update progress text with context-aware message
- if (progressText) {
- const contextMessage = getRetryContextMessage();
- progressText.textContent = contextMessage;
+ // Setup JRE retry button
+ if (progressJRRetryBtn) {
+ progressJRRetryBtn.addEventListener('click', async () => {
+ if (!currentDownloadState.canRetry || currentDownloadState.isDownloading) {
+ return;
}
+ progressJRRetryBtn.disabled = true;
+ progressJRRetryBtn.textContent = 'Retrying...';
+ progressJRRetryBtn.classList.add('retrying');
+ currentDownloadState.isDownloading = true;
- // Ensure retry data exists, create defaults if null
- if (!currentDownloadState.retryData) {
- currentDownloadState.retryData = {
- branch: 'release',
- fileName: '4.pwr'
- };
- console.log('[UI] Created default retry data:', currentDownloadState.retryData);
- }
-
- // Send retry request to backend
- if (window.electronAPI && window.electronAPI.retryDownload) {
- const result = await window.electronAPI.retryDownload(currentDownloadState.retryData);
+ try {
+ hideDownloadError();
- if (!result.success) {
- throw new Error(result.error || 'Retry failed');
+ if (progressRetryInfo) {
+ progressRetryInfo.style.background = '';
+ progressRetryInfo.style.color = '';
+ }
+
+ if (progressText) {
+ progressText.textContent = 'Re-downloading Java runtime...';
}
- } else {
- // Fallback for development/testing
- console.warn('electronAPI.retryDownload not available, simulating retry...');
- await new Promise(resolve => setTimeout(resolve, 2000));
- throw new Error('Retry API not available');
- }
- } catch (error) {
- console.error('Retry failed:', error);
- const errorType = categorizeError(error.message);
- showDownloadError(`Retry failed: ${error.message}`, true, errorType);
-
- // Reset retry button
- progressRetryBtn.disabled = false;
- progressRetryBtn.textContent = '🔄 Retry Download';
- progressRetryBtn.classList.remove('retrying');
- currentDownloadState.isDownloading = false;
- }
- });
+ if (!currentDownloadState.retryData || currentDownloadState.errorType !== 'jre') {
+ currentDownloadState.retryData = {
+ isJREError: true,
+ jreUrl: '',
+ fileName: 'jre.tar.gz',
+ cacheDir: '',
+ osName: 'linux',
+ arch: 'amd64'
+ };
+ console.log('[UI] Created default JRE retry data:', currentDownloadState.retryData);
+ }
+
+ if (window.electronAPI && window.electronAPI.retryDownload) {
+ const result = await window.electronAPI.retryDownload(currentDownloadState.retryData);
+ if (!result.success) {
+ throw new Error(result.error || 'JRE retry failed');
+ }
+ } else {
+ console.warn('electronAPI.retryDownload not available, simulating JRE retry...');
+ await new Promise(resolve => setTimeout(resolve, 2000));
+ throw new Error('JRE retry API not available');
+ }
+
+ } catch (error) {
+ console.error('JRE retry failed:', error);
+ showDownloadError(`JRE retry failed: ${error.message}`, true, 'jre');
+ } finally {
+ if (progressJRRetryBtn) {
+ progressJRRetryBtn.disabled = false;
+ progressJRRetryBtn.textContent = 'Retry Java Download';
+ progressJRRetryBtn.classList.remove('retrying');
+ }
+ currentDownloadState.isDownloading = false;
+ }
+ });
+ }
+
+ // Setup PWR retry button
+ if (progressPWRRetryBtn) {
+ progressPWRRetryBtn.addEventListener('click', async () => {
+ if (!currentDownloadState.canRetry || currentDownloadState.isDownloading) {
+ return;
+ }
+ progressPWRRetryBtn.disabled = true;
+ progressPWRRetryBtn.textContent = 'Retrying...';
+ progressPWRRetryBtn.classList.add('retrying');
+ currentDownloadState.isDownloading = true;
+
+ try {
+ hideDownloadError();
+
+ if (progressRetryInfo) {
+ progressRetryInfo.style.background = '';
+ progressRetryInfo.style.color = '';
+ }
+
+ if (progressText) {
+ const contextMessage = getRetryContextMessage();
+ progressText.textContent = contextMessage;
+ }
+
+ if (!currentDownloadState.retryData || currentDownloadState.errorType === 'jre') {
+ currentDownloadState.retryData = {
+ branch: 'release',
+ fileName: '4.pwr'
+ };
+ console.log('[UI] Created default PWR retry data:', currentDownloadState.retryData);
+ }
+
+ if (window.electronAPI && window.electronAPI.retryDownload) {
+ const result = await window.electronAPI.retryDownload(currentDownloadState.retryData);
+ if (!result.success) {
+ throw new Error(result.error || 'Game retry failed');
+ }
+ } else {
+ console.warn('electronAPI.retryDownload not available, simulating PWR retry...');
+ await new Promise(resolve => setTimeout(resolve, 2000));
+ throw new Error('Game retry API not available');
+ }
+
+ } catch (error) {
+ console.error('PWR retry failed:', error);
+ const errorType = categorizeError(error.message);
+ showDownloadError(`Game retry failed: ${error.message}`, true, errorType, error);
+ } finally {
+ if (progressPWRRetryBtn) {
+ progressPWRRetryBtn.disabled = false;
+ progressPWRRetryBtn.textContent = error && error.isJREError ? 'Retry Java Download' : 'Retry Game Download';
+ progressPWRRetryBtn.classList.remove('retrying');
+ }
+ currentDownloadState.isDownloading = false;
+ }
+ });
+ }
+
+ // Setup generic retry button (fallback)
+ if (progressRetryBtn) {
+ progressRetryBtn.addEventListener('click', async () => {
+ if (!currentDownloadState.canRetry || currentDownloadState.isDownloading) {
+ return;
+ }
+ progressRetryBtn.disabled = true;
+ progressRetryBtn.textContent = 'Retrying...';
+ progressRetryBtn.classList.add('retrying');
+ currentDownloadState.isDownloading = true;
+
+ try {
+ hideDownloadError();
+
+ if (progressRetryInfo) {
+ progressRetryInfo.style.background = '';
+ progressRetryInfo.style.color = '';
+ }
+
+ if (progressText) {
+ const contextMessage = getRetryContextMessage();
+ progressText.textContent = contextMessage;
+ }
+
+ if (!currentDownloadState.retryData) {
+ if (currentDownloadState.errorType === 'jre') {
+ currentDownloadState.retryData = {
+ isJREError: true,
+ jreUrl: '',
+ fileName: 'jre.tar.gz',
+ cacheDir: '',
+ osName: 'linux',
+ arch: 'amd64'
+ };
+ } else {
+ currentDownloadState.retryData = {
+ branch: 'release',
+ fileName: '4.pwr'
+ };
+ }
+ console.log('[UI] Created default retry data:', currentDownloadState.retryData);
+ }
+
+ if (window.electronAPI && window.electronAPI.retryDownload) {
+ const result = await window.electronAPI.retryDownload(currentDownloadState.retryData);
+ if (!result.success) {
+ throw new Error(result.error || 'Retry failed');
+ }
+ } else {
+ console.warn('electronAPI.retryDownload not available, simulating retry...');
+ await new Promise(resolve => setTimeout(resolve, 2000));
+ throw new Error('Retry API not available');
+ }
+
+ } catch (error) {
+ console.error('Retry failed:', error);
+ const errorType = categorizeError(error.message);
+ showDownloadError(`Retry failed: ${error.message}`, true, errorType);
+ } finally {
+ if (progressRetryBtn) {
+ progressRetryBtn.disabled = false;
+ progressRetryBtn.textContent = 'Retry Download';
+ progressRetryBtn.classList.remove('retrying');
+ }
+ currentDownloadState.isDownloading = false;
+ }
+ });
+ }
}
function getRetryContextMessage() {
@@ -925,6 +1093,8 @@ function getRetryContextMessage() {
return 'Retrying with corrected permissions...';
case 'conflict':
return 'Retrying after resolving conflicts...';
+ case 'jre':
+ return 'Re-downloading Java runtime...';
default:
return 'Initiating retry download...';
}
diff --git a/GUI/style.css b/GUI/style.css
index 8e492f1..8e28ef5 100644
--- a/GUI/style.css
+++ b/GUI/style.css
@@ -1815,6 +1815,12 @@ body {
gap: 0.75rem;
}
+.progress-retry-buttons {
+ display: flex;
+ gap: 0.5rem;
+ flex-wrap: wrap;
+}
+
.progress-retry-info {
color: #fbbf24;
font-family: 'JetBrains Mono', monospace;
diff --git a/backend/managers/gameManager.js b/backend/managers/gameManager.js
index 3087147..2fb8b62 100644
--- a/backend/managers/gameManager.js
+++ b/backend/managers/gameManager.js
@@ -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;
}
diff --git a/backend/managers/javaManager.js b/backend/managers/javaManager.js
index c7b48ac..84af570 100644
--- a/backend/managers/javaManager.js
+++ b/backend/managers/javaManager.js
@@ -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');
}
diff --git a/backend/utils/fileManager.js b/backend/utils/fileManager.js
index 576cc96..972ca61 100644
--- a/backend/utils/fileManager.js
+++ b/backend/utils/fileManager.js
@@ -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');
diff --git a/main.js b/main.js
index 7e67543..e7e2767 100644
--- a/main.js
+++ b/main.js
@@ -3,6 +3,7 @@ require('dotenv').config({ path: path.join(__dirname, '.env') });
const { app, BrowserWindow, ipcMain, dialog, shell } = require('electron');
const fs = require('fs');
const { launchGame, launchGameWithVersionCheck, installGame, saveUsername, loadUsername, saveChatUsername, loadChatUsername, saveChatColor, loadChatColor, saveJavaPath, loadJavaPath, saveInstallPath, loadInstallPath, saveDiscordRPC, loadDiscordRPC, saveLanguage, loadLanguage, saveCloseLauncherOnStart, loadCloseLauncherOnStart, isGameInstalled, uninstallGame, repairGame, getHytaleNews, handleFirstLaunchCheck, proposeGameUpdate, markAsLaunched } = require('./backend/launcher');
+const { retryPWRDownload } = require('./backend/managers/gameManager');
const logger = require('./backend/logger');
const profileManager = require('./backend/managers/profileManager');
@@ -430,37 +431,41 @@ ipcMain.handle('install-game', async (event, playerName, javaPath, installPath,
console.log('[Main] Returning success response for install-game:', successResponse);
return successResponse;
} catch (error) {
- console.error('Install error:', error);
+ // console.error('Install error:', error);
const errorMessage = error.message || error.toString();
// Enhanced error data extraction for both download and Butler errors
let errorData = {
message: errorMessage,
error: true,
- canRetry: true,
+ canRetry: true, // Default to true, will be overridden by specific error props
retryData: null
};
+ // Prioritize JRE errors first
+ if (error.isJREError) {
+ console.log('[Main] Processing JRE download error with retry context');
+ errorData.retryData = {
+ isJREError: true,
+ jreUrl: error.jreUrl,
+ fileName: error.fileName,
+ cacheDir: error.cacheDir,
+ osName: error.osName,
+ arch: error.arch
+ };
+ // For JRE errors, allow manual retry unless explicitly disabled
+ errorData.canRetry = error.canRetry !== false;
+ errorData.errorType = 'jre';
+ }
// Handle Butler-specific errors
- if (error.butlerError) {
+ else if (error.butlerError) {
console.log('[Main] Processing Butler error with retry context');
errorData.retryData = {
branch: error.branch || 'release',
fileName: error.fileName || '4.pwr',
cacheDir: error.cacheDir
};
- errorData.canRetry = error.canRetry !== undefined ? error.canRetry : true;
-
- // Add Butler-specific error details
- if (error.stderr) {
- console.error('[Main] Butler stderr:', error.stderr);
- }
- if (error.stdout) {
- console.log('[Main] Butler stdout:', error.stdout);
- }
- if (error.errorCode) {
- console.log('[Main] Butler error code:', error.errorCode);
- }
+ errorData.canRetry = error.canRetry !== false;
}
// Handle PWR download errors
else if (error.branch && error.fileName) {
@@ -470,7 +475,7 @@ ipcMain.handle('install-game', async (event, playerName, javaPath, installPath,
fileName: error.fileName,
cacheDir: error.cacheDir
};
- errorData.canRetry = error.canRetry !== undefined ? error.canRetry : true;
+ errorData.canRetry = error.canRetry !== false;
}
// Default fallback for other errors
else {
@@ -479,6 +484,8 @@ ipcMain.handle('install-game', async (event, playerName, javaPath, installPath,
branch: 'release',
fileName: '4.pwr'
};
+ // For generic errors, assume it's retryable unless specified
+ errorData.canRetry = error.canRetry !== false;
}
// Send enhanced error info for retry UI
@@ -636,7 +643,7 @@ ipcMain.handle('uninstall-game', async () => {
try {
await uninstallGame();
} catch (error) {
- console.error('Uninstall error:', error);
+ // console.error('Uninstall error:', error);
return { success: false, error: error.message };
}
});
@@ -669,16 +676,7 @@ ipcMain.handle('repair-game', async () => {
ipcMain.handle('retry-download', async (event, retryData) => {
try {
console.log('[IPC] retry-download called with data:', retryData);
-
- // Handle null retry data gracefully
- if (!retryData || !retryData.branch || !retryData.fileName) {
- console.log('[IPC] Invalid retry data, using defaults');
- retryData = {
- branch: 'release',
- fileName: '4.pwr'
- };
- }
-
+
const progressCallback = (message, percent, speed, downloaded, total, retryState) => {
if (mainWindow && !mainWindow.isDestroyed()) {
const data = {
@@ -693,14 +691,36 @@ ipcMain.handle('retry-download', async (event, retryData) => {
}
};
+ // Handle JRE download retries
+ if (retryData && retryData.isJREError) {
+ console.log(`[IPC] Retrying JRE download: jreUrl=${retryData.jreUrl}, fileName=${retryData.fileName}`);
+ console.log('[IPC] Full JRE retry data:', JSON.stringify(retryData, null, 2));
+
+ const { retryJREDownload } = require('./backend/managers/javaManager');
+ await retryJREDownload(retryData.jreUrl, jreCacheFile, progressCallback);
+ const jreCacheFile = path.join(retryData.cacheDir, retryData.fileName);
+
+ return { success: true };
+ }
+
+ // Handle PWR download retries (default)
+ if (!retryData || !retryData.branch || !retryData.fileName) {
+ console.log('[IPC] Invalid retry data, using PWR defaults');
+ retryData = {
+ branch: 'release',
+ fileName: '4.pwr'
+ };
+ }
+
// Extract PWR download info from retryData
const branch = retryData.branch;
const fileName = retryData.fileName;
const cacheDir = retryData.cacheDir;
console.log(`[IPC] Retrying PWR download: branch=${branch}, fileName=${fileName}`);
+ console.log('[IPC] Full PWR retry data:', JSON.stringify(retryData, null, 2));
- // Perform the retry with enhanced context
+ // Perform retry with enhanced context
await retryPWRDownload(branch, fileName, progressCallback, cacheDir);
return { success: true };
@@ -710,15 +730,28 @@ ipcMain.handle('retry-download', async (event, retryData) => {
// Send error update to frontend with context
if (mainWindow && !mainWindow.isDestroyed()) {
- const data = {
- message: errorMessage,
- error: true,
- canRetry: true,
- retryData: {
+ const isJreError = retryData?.isJREError;
+ const errorRetryData = isJreError ?
+ {
+ isJREError: true,
+ jreUrl: retryData?.jreUrl,
+ fileName: retryData?.fileName,
+ cacheDir: retryData?.cacheDir,
+ osName: retryData?.osName,
+ arch: retryData?.arch
+ } :
+ {
branch: retryData?.branch || 'release',
fileName: retryData?.fileName || '4.pwr',
cacheDir: retryData?.cacheDir
- }
+ };
+
+ const data = {
+ message: errorMessage,
+ error: true,
+ canRetry: error.canRetry !== false, // Respect canRetry from the thrown error
+ retryData: errorRetryData,
+ errorType: isJreError ? 'jre' : 'general' // Add errorType for the UI
};
mainWindow.webContents.send('progress-update', data);
}
@@ -846,7 +879,6 @@ ipcMain.handle('load-settings', async () => {
});
const { getModsPath, loadInstalledMods, downloadMod, uninstallMod, toggleMod, getCurrentUuid, getAllUuidMappings, setUuidForUser, generateNewUuid, deleteUuidForUser, resetCurrentUserUuid } = require('./backend/launcher');
-const { retryPWRDownload } = require('./backend/managers/gameManager');
const os = require('os');
ipcMain.handle('get-local-app-data', async () => {