fix hardcoded branch + pre-release/release issue

This commit is contained in:
AMIAY
2026-01-24 19:01:34 +01:00
parent 9c8a12f25c
commit 9f43a32779
6 changed files with 590 additions and 549 deletions

View File

@@ -167,6 +167,8 @@ function setupSettingsElements() {
closeLauncherCheck = document.getElementById('closeLauncherCheck'); closeLauncherCheck = document.getElementById('closeLauncherCheck');
gpuPreferenceRadios = document.querySelectorAll('input[name="gpuPreference"]'); gpuPreferenceRadios = document.querySelectorAll('input[name="gpuPreference"]');
gameBranchRadios = document.querySelectorAll('input[name="gameBranch"]'); gameBranchRadios = document.querySelectorAll('input[name="gameBranch"]');
console.log('[Settings] gameBranchRadios found:', gameBranchRadios.length);
// UUID Management elements // UUID Management elements
@@ -541,7 +543,8 @@ document.addEventListener('DOMContentLoaded', initSettings);
window.SettingsAPI = { window.SettingsAPI = {
getCurrentJavaPath, getCurrentJavaPath,
getCurrentPlayerName getCurrentPlayerName,
reloadBranch: loadVersionBranch
}; };
async function loadCurrentUuid() { async function loadCurrentUuid() {
@@ -1078,11 +1081,13 @@ async function loadVersionBranch() {
console.log('[Settings] Selected branch:', selectedBranch); console.log('[Settings] Selected branch:', selectedBranch);
// Update radio buttons // Update radio buttons
if (gameBranchRadios) { if (gameBranchRadios && gameBranchRadios.length > 0) {
gameBranchRadios.forEach(radio => { gameBranchRadios.forEach(radio => {
radio.checked = radio.value === selectedBranch; radio.checked = radio.value === selectedBranch;
console.log(`[Settings] Radio ${radio.value}: ${radio.checked ? 'checked' : 'unchecked'}`); console.log(`[Settings] Radio ${radio.value}: ${radio.checked ? 'checked' : 'unchecked'}`);
}); });
} else {
console.warn('[Settings] gameBranchRadios not found or empty');
} }
return selectedBranch; return selectedBranch;

View File

@@ -1,27 +1,27 @@
let progressOverlay; let progressOverlay;
let progressBar; let progressBar;
let progressBarFill; let progressBarFill;
let progressText; let progressText;
let progressPercent; let progressPercent;
let progressSpeed; let progressSpeed;
let progressSize; let progressSize;
let progressErrorContainer; let progressErrorContainer;
let progressErrorMessage; let progressErrorMessage;
let progressRetryInfo; let progressRetryInfo;
let progressRetryBtn; let progressRetryBtn;
// Download retry state // Download retry state
let currentDownloadState = { let currentDownloadState = {
isDownloading: false, isDownloading: false,
canRetry: false, canRetry: false,
retryData: null, retryData: null,
lastError: null, lastError: null,
errorType: null, errorType: null,
branch: null, branch: null,
fileName: null, fileName: null,
cacheDir: null cacheDir: null
}; };
function showPage(pageId) { function showPage(pageId) {
const pages = document.querySelectorAll('.page'); const pages = document.querySelectorAll('.page');
@@ -29,6 +29,15 @@ function showPage(pageId) {
if (page.id === pageId) { if (page.id === pageId) {
page.classList.add('active'); page.classList.add('active');
page.style.display = ''; page.style.display = '';
// Reload settings when settings page becomes visible
if (pageId === 'settings-page') {
console.log('[UI] Settings page activated, reloading branch...');
// Dynamically import and call loadVersionBranch from settings
if (window.SettingsAPI && window.SettingsAPI.reloadBranch) {
window.SettingsAPI.reloadBranch();
}
}
} else { } else {
page.classList.remove('active'); page.classList.remove('active');
page.style.display = 'none'; page.style.display = 'none';
@@ -159,108 +168,108 @@ function hideProgress() {
} }
} }
function updateProgress(data) { function updateProgress(data) {
// Handle retry state // Handle retry state
if (data.retryState) { if (data.retryState) {
currentDownloadState.retryData = data.retryState; currentDownloadState.retryData = data.retryState;
updateRetryState(data.retryState); updateRetryState(data.retryState);
} }
if (data.message && progressText) { if (data.message && progressText) {
progressText.textContent = data.message; progressText.textContent = data.message;
} }
if (data.percent !== null && data.percent !== undefined) { if (data.percent !== null && data.percent !== undefined) {
const percent = Math.min(100, Math.max(0, Math.round(data.percent))); const percent = Math.min(100, Math.max(0, Math.round(data.percent)));
if (progressPercent) progressPercent.textContent = `${percent}%`; if (progressPercent) progressPercent.textContent = `${percent}%`;
if (progressBarFill) progressBarFill.style.width = `${percent}%`; if (progressBarFill) progressBarFill.style.width = `${percent}%`;
if (progressBar) progressBar.style.width = `${percent}%`; if (progressBar) progressBar.style.width = `${percent}%`;
} }
if (data.speed && data.downloaded && data.total) { if (data.speed && data.downloaded && data.total) {
const speedMB = (data.speed / 1024 / 1024).toFixed(2); const speedMB = (data.speed / 1024 / 1024).toFixed(2);
const downloadedMB = (data.downloaded / 1024 / 1024).toFixed(2); const downloadedMB = (data.downloaded / 1024 / 1024).toFixed(2);
const totalMB = (data.total / 1024 / 1024).toFixed(2); const totalMB = (data.total / 1024 / 1024).toFixed(2);
if (progressSpeed) progressSpeed.textContent = `${speedMB} MB/s`; if (progressSpeed) progressSpeed.textContent = `${speedMB} MB/s`;
if (progressSize) progressSize.textContent = `${downloadedMB} / ${totalMB} MB`; if (progressSize) progressSize.textContent = `${downloadedMB} / ${totalMB} MB`;
} }
// Handle error states with enhanced categorization // Handle error states with enhanced categorization
// Don't show error during automatic retries - let the retry message display instead // Don't show error during automatic retries - let the retry message display instead
if ((data.error || (data.message && data.message.includes('failed'))) && if ((data.error || (data.message && data.message.includes('failed'))) &&
!(data.retryState && data.retryState.isAutomaticRetry)) { !(data.retryState && data.retryState.isAutomaticRetry)) {
const errorType = categorizeError(data.message); const errorType = categorizeError(data.message);
showDownloadError(data.message, data.canRetry, errorType); showDownloadError(data.message, data.canRetry, errorType);
} else if (data.percent === 100) { } else if (data.percent === 100) {
hideDownloadError(); hideDownloadError();
} else if (data.retryState && data.retryState.isAutomaticRetry) { } else if (data.retryState && data.retryState.isAutomaticRetry) {
// Hide any existing error during automatic retries // Hide any existing error during automatic retries
hideDownloadError(); hideDownloadError();
} }
} }
function updateRetryState(retryState) { function updateRetryState(retryState) {
if (!progressRetryInfo) return; if (!progressRetryInfo) return;
if (retryState.isAutomaticRetry && retryState.automaticStallRetries > 0) { if (retryState.isAutomaticRetry && retryState.automaticStallRetries > 0) {
// Show automatic stall retry count // Show automatic stall retry count
progressRetryInfo.textContent = `Auto-retry ${retryState.automaticStallRetries}/3`; progressRetryInfo.textContent = `Auto-retry ${retryState.automaticStallRetries}/3`;
progressRetryInfo.style.display = 'block'; progressRetryInfo.style.display = 'block';
progressRetryInfo.style.background = 'rgba(255, 193, 7, 0.2)'; // Light orange background for auto-retries progressRetryInfo.style.background = 'rgba(255, 193, 7, 0.2)'; // Light orange background for auto-retries
progressRetryInfo.style.color = '#ff9800'; // Orange text for auto-retries progressRetryInfo.style.color = '#ff9800'; // Orange text for auto-retries
} else if (retryState.attempts > 1) { } else if (retryState.attempts > 1) {
// Show manual retry count // Show manual retry count
progressRetryInfo.textContent = `Attempt ${retryState.attempts}/${retryState.maxRetries}`; progressRetryInfo.textContent = `Attempt ${retryState.attempts}/${retryState.maxRetries}`;
progressRetryInfo.style.display = 'block'; progressRetryInfo.style.display = 'block';
progressRetryInfo.style.background = ''; // Reset background progressRetryInfo.style.background = ''; // Reset background
progressRetryInfo.style.color = ''; // Reset color progressRetryInfo.style.color = ''; // Reset color
} else { } else {
progressRetryInfo.style.display = 'none'; progressRetryInfo.style.display = 'none';
progressRetryInfo.style.background = ''; // Reset background progressRetryInfo.style.background = ''; // Reset background
progressRetryInfo.style.color = ''; // Reset color progressRetryInfo.style.color = ''; // Reset color
} }
} }
function showDownloadError(errorMessage, canRetry = true, errorType = 'general') { function showDownloadError(errorMessage, canRetry = true, errorType = 'general') {
if (!progressErrorContainer || !progressErrorMessage || !progressRetryBtn) return; if (!progressErrorContainer || !progressErrorMessage || !progressRetryBtn) return;
currentDownloadState.lastError = errorMessage; currentDownloadState.lastError = errorMessage;
currentDownloadState.canRetry = canRetry; currentDownloadState.canRetry = canRetry;
currentDownloadState.errorType = errorType; currentDownloadState.errorType = errorType;
// Update retry context if available // Update retry context if available
if (data && data.retryData) { if (data && data.retryData) {
currentDownloadState.branch = data.retryData.branch; currentDownloadState.branch = data.retryData.branch;
currentDownloadState.fileName = data.retryData.fileName; currentDownloadState.fileName = data.retryData.fileName;
currentDownloadState.cacheDir = data.retryData.cacheDir; currentDownloadState.cacheDir = data.retryData.cacheDir;
} }
// User-friendly error messages // User-friendly error messages
const userMessage = getErrorMessage(errorMessage, errorType); const userMessage = getErrorMessage(errorMessage, errorType);
progressErrorMessage.textContent = userMessage; progressErrorMessage.textContent = userMessage;
progressErrorContainer.style.display = 'block'; progressErrorContainer.style.display = 'block';
progressRetryBtn.style.display = canRetry ? 'block' : 'none'; progressRetryBtn.style.display = canRetry ? 'block' : 'none';
// Add visual indicators based on error type // Add visual indicators based on error type
progressErrorContainer.className = `progress-error-container error-${errorType}`; progressErrorContainer.className = `progress-error-container error-${errorType}`;
if (progressOverlay) { if (progressOverlay) {
progressOverlay.classList.add('error-state'); progressOverlay.classList.add('error-state');
} }
} }
function hideDownloadError() { function hideDownloadError() {
if (!progressErrorContainer) return; if (!progressErrorContainer) return;
progressErrorContainer.style.display = 'none'; progressErrorContainer.style.display = 'none';
currentDownloadState.canRetry = false; currentDownloadState.canRetry = false;
currentDownloadState.lastError = null; currentDownloadState.lastError = null;
currentDownloadState.errorType = null; currentDownloadState.errorType = null;
if (progressOverlay) { if (progressOverlay) {
progressOverlay.classList.remove('error-state'); progressOverlay.classList.remove('error-state');
} }
} }
function setupAnimations() { function setupAnimations() {
document.body.style.opacity = '0'; document.body.style.opacity = '0';
@@ -568,24 +577,24 @@ function showNotification(message, type = 'info') {
}, 5000); }, 5000);
} }
function setupUI() { function setupUI() {
progressOverlay = document.getElementById('progressOverlay'); progressOverlay = document.getElementById('progressOverlay');
progressBar = document.getElementById('progressBar'); progressBar = document.getElementById('progressBar');
progressBarFill = document.getElementById('progressBarFill'); progressBarFill = document.getElementById('progressBarFill');
progressText = document.getElementById('progressText'); progressText = document.getElementById('progressText');
progressPercent = document.getElementById('progressPercent'); progressPercent = document.getElementById('progressPercent');
progressSpeed = document.getElementById('progressSpeed'); progressSpeed = document.getElementById('progressSpeed');
progressSize = document.getElementById('progressSize'); progressSize = document.getElementById('progressSize');
progressErrorContainer = document.getElementById('progressErrorContainer'); progressErrorContainer = document.getElementById('progressErrorContainer');
progressErrorMessage = document.getElementById('progressErrorMessage'); progressErrorMessage = document.getElementById('progressErrorMessage');
progressRetryInfo = document.getElementById('progressRetryInfo'); progressRetryInfo = document.getElementById('progressRetryInfo');
progressRetryBtn = document.getElementById('progressRetryBtn'); progressRetryBtn = document.getElementById('progressRetryBtn');
// Setup draggable progress bar // Setup draggable progress bar
setupProgressDrag(); setupProgressDrag();
// Setup retry button // Setup retry button
setupRetryButton(); setupRetryButton();
lockPlayButton(true); lockPlayButton(true);
@@ -753,175 +762,175 @@ function toggleMaximize() {
} }
} }
// Error categorization and user-friendly messages // Error categorization and user-friendly messages
function categorizeError(message) { function categorizeError(message) {
const msg = message.toLowerCase(); const msg = message.toLowerCase();
if (msg.includes('network') || msg.includes('connection') || msg.includes('offline')) { if (msg.includes('network') || msg.includes('connection') || msg.includes('offline')) {
return 'network'; return 'network';
} else if (msg.includes('stalled') || msg.includes('timeout')) { } else if (msg.includes('stalled') || msg.includes('timeout')) {
return 'stall'; return 'stall';
} else if (msg.includes('file') || msg.includes('disk')) { } else if (msg.includes('file') || msg.includes('disk')) {
return 'file'; return 'file';
} else if (msg.includes('permission') || msg.includes('access')) { } else if (msg.includes('permission') || msg.includes('access')) {
return 'permission'; return 'permission';
} else if (msg.includes('server') || msg.includes('5')) { } else if (msg.includes('server') || msg.includes('5')) {
return 'server'; return 'server';
} else if (msg.includes('corrupted') || msg.includes('pwr file') || msg.includes('unexpected eof')) { } else if (msg.includes('corrupted') || msg.includes('pwr file') || msg.includes('unexpected eof')) {
return 'corruption'; return 'corruption';
} else if (msg.includes('butler') || msg.includes('patch installation')) { } else if (msg.includes('butler') || msg.includes('patch installation')) {
return 'butler'; return 'butler';
} else if (msg.includes('space') || msg.includes('full') || msg.includes('device full')) { } else if (msg.includes('space') || msg.includes('full') || msg.includes('device full')) {
return 'space'; return 'space';
} else if (msg.includes('conflict') || msg.includes('already exists')) { } else if (msg.includes('conflict') || msg.includes('already exists')) {
return 'conflict'; return 'conflict';
} else { } else {
return 'general'; return 'general';
} }
} }
function getErrorMessage(technicalMessage, errorType) { function getErrorMessage(technicalMessage, errorType) {
// Technical errors go to console, user gets friendly messages // Technical errors go to console, user gets friendly messages
console.error(`Download error [${errorType}]:`, technicalMessage); console.error(`Download error [${errorType}]:`, technicalMessage);
switch (errorType) { switch (errorType) {
case 'network': case 'network':
return 'Network connection lost. Please check your internet connection and retry.'; return 'Network connection lost. Please check your internet connection and retry.';
case 'stall': case 'stall':
return 'Download stalled due to slow connection. Please retry.'; return 'Download stalled due to slow connection. Please retry.';
case 'file': case 'file':
return 'Unable to save file. Check disk space and permissions. Please retry.'; return 'Unable to save file. Check disk space and permissions. Please retry.';
case 'permission': case 'permission':
return 'Permission denied. Check if launcher has write access. Please retry.'; return 'Permission denied. Check if launcher has write access. Please retry.';
case 'server': case 'server':
return 'Server error. Please wait a moment and retry.'; return 'Server error. Please wait a moment and retry.';
case 'corruption': case 'corruption':
return 'Corrupted PWR file detected. File deleted and will retry.'; return 'Corrupted PWR file detected. File deleted and will retry.';
case 'butler': case 'butler':
return 'Patch installation failed. Please retry.'; return 'Patch installation failed. Please retry.';
case 'space': case 'space':
return 'Insufficient disk space. Free up space and retry.'; return 'Insufficient disk space. Free up space and retry.';
case 'conflict': case 'conflict':
return 'Installation directory conflict. Please retry.'; return 'Installation directory conflict. Please retry.';
default: default:
return 'Download failed. Please retry.'; return 'Download failed. Please retry.';
} }
} }
// Connection quality indicator (simplified) // Connection quality indicator (simplified)
function updateConnectionQuality(quality) { function updateConnectionQuality(quality) {
if (!progressSize) return; if (!progressSize) return;
const qualityColors = { const qualityColors = {
'Good': '#10b981', 'Good': '#10b981',
'Fair': '#fbbf24', 'Fair': '#fbbf24',
'Poor': '#f87171' 'Poor': '#f87171'
}; };
const color = qualityColors[quality] || '#6b7280'; const color = qualityColors[quality] || '#6b7280';
progressSize.style.color = color; progressSize.style.color = color;
// Add subtle quality indicator // Add subtle quality indicator
if (progressSize.dataset.quality !== quality) { if (progressSize.dataset.quality !== quality) {
progressSize.dataset.quality = quality; progressSize.dataset.quality = quality;
progressSize.style.transition = 'color 0.5s ease'; progressSize.style.transition = 'color 0.5s ease';
} }
} }
// Enhanced retry button setup // Enhanced retry button setup
function setupRetryButton() { function setupRetryButton() {
if (!progressRetryBtn) return; if (!progressRetryBtn) return;
progressRetryBtn.addEventListener('click', async () => { progressRetryBtn.addEventListener('click', async () => {
if (!currentDownloadState.canRetry || currentDownloadState.isDownloading) { if (!currentDownloadState.canRetry || currentDownloadState.isDownloading) {
return; return;
} }
// Disable retry button during retry // Disable retry button during retry
progressRetryBtn.disabled = true; progressRetryBtn.disabled = true;
progressRetryBtn.textContent = '🔄 Retrying...'; progressRetryBtn.textContent = '🔄 Retrying...';
progressRetryBtn.classList.add('retrying'); progressRetryBtn.classList.add('retrying');
currentDownloadState.isDownloading = true; currentDownloadState.isDownloading = true;
try { try {
// Hide error state during retry // Hide error state during retry
hideDownloadError(); hideDownloadError();
// Reset retry info styling for manual retries // Reset retry info styling for manual retries
if (progressRetryInfo) { if (progressRetryInfo) {
progressRetryInfo.style.background = ''; progressRetryInfo.style.background = '';
progressRetryInfo.style.color = ''; progressRetryInfo.style.color = '';
} }
// Update progress text with context-aware message // Update progress text with context-aware message
if (progressText) { if (progressText) {
const contextMessage = getRetryContextMessage(); const contextMessage = getRetryContextMessage();
progressText.textContent = contextMessage; progressText.textContent = contextMessage;
} }
// Ensure retry data exists, create defaults if null // Ensure retry data exists, create defaults if null
if (!currentDownloadState.retryData) { if (!currentDownloadState.retryData) {
currentDownloadState.retryData = { currentDownloadState.retryData = {
branch: 'release', branch: 'release',
fileName: '4.pwr' fileName: '4.pwr'
}; };
console.log('[UI] Created default retry data:', currentDownloadState.retryData); console.log('[UI] Created default retry data:', currentDownloadState.retryData);
} }
// Send retry request to backend // Send retry request to backend
if (window.electronAPI && window.electronAPI.retryDownload) { if (window.electronAPI && window.electronAPI.retryDownload) {
const result = await window.electronAPI.retryDownload(currentDownloadState.retryData); const result = await window.electronAPI.retryDownload(currentDownloadState.retryData);
if (!result.success) { if (!result.success) {
throw new Error(result.error || 'Retry failed'); throw new Error(result.error || 'Retry failed');
} }
} else { } else {
// Fallback for development/testing // Fallback for development/testing
console.warn('electronAPI.retryDownload not available, simulating retry...'); console.warn('electronAPI.retryDownload not available, simulating retry...');
await new Promise(resolve => setTimeout(resolve, 2000)); await new Promise(resolve => setTimeout(resolve, 2000));
throw new Error('Retry API not available'); throw new Error('Retry API not available');
} }
} catch (error) { } catch (error) {
console.error('Retry failed:', error); console.error('Retry failed:', error);
const errorType = categorizeError(error.message); const errorType = categorizeError(error.message);
showDownloadError(`Retry failed: ${error.message}`, true, errorType); showDownloadError(`Retry failed: ${error.message}`, true, errorType);
// Reset retry button // Reset retry button
progressRetryBtn.disabled = false; progressRetryBtn.disabled = false;
progressRetryBtn.textContent = '🔄 Retry Download'; progressRetryBtn.textContent = '🔄 Retry Download';
progressRetryBtn.classList.remove('retrying'); progressRetryBtn.classList.remove('retrying');
currentDownloadState.isDownloading = false; currentDownloadState.isDownloading = false;
} }
}); });
} }
function getRetryContextMessage() { function getRetryContextMessage() {
const errorType = currentDownloadState.errorType; const errorType = currentDownloadState.errorType;
switch (errorType) { switch (errorType) {
case 'network': case 'network':
return 'Reconnecting and retrying download...'; return 'Reconnecting and retrying download...';
case 'stall': case 'stall':
return 'Resuming stalled download...'; return 'Resuming stalled download...';
case 'server': case 'server':
return 'Waiting for server and retrying...'; return 'Waiting for server and retrying...';
case 'corruption': case 'corruption':
return 'Re-downloading corrupted PWR file...'; return 'Re-downloading corrupted PWR file...';
case 'butler': case 'butler':
return 'Re-attempting patch installation...'; return 'Re-attempting patch installation...';
case 'space': case 'space':
return 'Retrying after clearing disk space...'; return 'Retrying after clearing disk space...';
case 'permission': case 'permission':
return 'Retrying with corrected permissions...'; return 'Retrying with corrected permissions...';
case 'conflict': case 'conflict':
return 'Retrying after resolving conflicts...'; return 'Retrying after resolving conflicts...';
default: default:
return 'Initiating retry download...'; return 'Initiating retry download...';
} }
} }
// Make toggleMaximize globally available // Make toggleMaximize globally available
window.toggleMaximize = toggleMaximize; window.toggleMaximize = toggleMaximize;
document.addEventListener('DOMContentLoaded', setupUI); document.addEventListener('DOMContentLoaded', setupUI);

View File

@@ -1,6 +1,7 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const os = require('os'); const os = require('os');
const { loadVersionBranch } = require('./config');
function getAppDir() { function getAppDir() {
const home = os.homedir(); const home = os.homedir();
@@ -48,8 +49,20 @@ function expandHome(inputPath) {
const APP_DIR = DEFAULT_APP_DIR; const APP_DIR = DEFAULT_APP_DIR;
const CACHE_DIR = path.join(APP_DIR, 'cache'); const CACHE_DIR = path.join(APP_DIR, 'cache');
const TOOLS_DIR = path.join(APP_DIR, 'butler'); const TOOLS_DIR = path.join(APP_DIR, 'butler');
const GAME_DIR = path.join(APP_DIR, 'release', 'package', 'game', 'latest');
const JRE_DIR = path.join(APP_DIR, 'release', 'package', 'jre', 'latest'); // Dynamic GAME_DIR and JRE_DIR based on version_branch from config
function getGameDir() {
const branch = loadVersionBranch();
return path.join(APP_DIR, branch, 'package', 'game', 'latest');
}
function getJreDir() {
const branch = loadVersionBranch();
return path.join(APP_DIR, branch, 'package', 'jre', 'latest');
}
const GAME_DIR = getGameDir();
const JRE_DIR = getJreDir();
const PLAYER_ID_FILE = path.join(APP_DIR, 'player_id.json'); const PLAYER_ID_FILE = path.join(APP_DIR, 'player_id.json');
function getClientCandidates(gameLatest) { function getClientCandidates(gameLatest) {
@@ -156,7 +169,8 @@ async function getModsPath(customInstallPath = null) {
installPath = getAppDir(); installPath = getAppDir();
} }
const gameLatest = path.join(installPath, 'release', 'package', 'game', 'latest'); const branch = loadVersionBranch();
const gameLatest = path.join(installPath, branch, 'package', 'game', 'latest');
const userDataPath = findUserDataPath(gameLatest); const userDataPath = findUserDataPath(gameLatest);
@@ -195,7 +209,8 @@ function getProfilesDir(customInstallPath = null) {
} }
if (!installPath) installPath = getAppDir(); if (!installPath) installPath = getAppDir();
const gameLatest = path.join(installPath, 'release', 'package', 'game', 'latest'); const branch = loadVersionBranch();
const gameLatest = path.join(installPath, branch, 'package', 'game', 'latest');
const userDataPath = findUserDataPath(gameLatest); const userDataPath = findUserDataPath(gameLatest);
const profilesDir = path.join(userDataPath, 'Profiles'); const profilesDir = path.join(userDataPath, 'Profiles');
@@ -219,6 +234,8 @@ module.exports = {
TOOLS_DIR, TOOLS_DIR,
GAME_DIR, GAME_DIR,
JRE_DIR, JRE_DIR,
getGameDir,
getJreDir,
PLAYER_ID_FILE, PLAYER_ID_FILE,
getClientCandidates, getClientCandidates,
findClientPath, findClientPath,

View File

@@ -446,7 +446,11 @@ function isGameInstalled(branchOverride = null) {
} }
async function installGame(playerName = 'Player', progressCallback, javaPathOverride, installPathOverride, branchOverride = null) { async function installGame(playerName = 'Player', progressCallback, javaPathOverride, installPathOverride, branchOverride = null) {
const branch = branchOverride || loadVersionBranch(); console.log(`[InstallGame] branchOverride parameter received: ${branchOverride}`);
const loadedBranch = loadVersionBranch();
console.log(`[InstallGame] loadVersionBranch() returned: ${loadedBranch}`);
const branch = branchOverride || loadedBranch;
console.log(`[InstallGame] Final branch selected: ${branch}`);
const customAppDir = getResolvedAppDir(installPathOverride); const customAppDir = getResolvedAppDir(installPathOverride);
const customCacheDir = path.join(customAppDir, 'cache'); const customCacheDir = path.join(customAppDir, 'cache');
const customToolsDir = path.join(customAppDir, 'butler'); const customToolsDir = path.join(customAppDir, 'butler');

458
main.js
View File

@@ -255,11 +255,11 @@ app.whenReady().then(async () => {
mainWindow.webContents.send('lock-play-button', true); mainWindow.webContents.send('lock-play-button', true);
} }
const progressCallback = (message, percent, speed, downloaded, total, retryState) => { const progressCallback = (message, percent, speed, downloaded, total, retryState) => {
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('first-launch-progress', { message, percent, speed, downloaded, total, retryState }); mainWindow.webContents.send('first-launch-progress', { message, percent, speed, downloaded, total, retryState });
} }
}; };
const firstLaunchResult = await Promise.race([ const firstLaunchResult = await Promise.race([
handleFirstLaunchCheck(progressCallback), handleFirstLaunchCheck(progressCallback),
@@ -346,21 +346,21 @@ app.on('window-all-closed', () => {
}); });
ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, gpuPreference) => { ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, gpuPreference) => {
try { try {
const progressCallback = (message, percent, speed, downloaded, total, retryState) => { const progressCallback = (message, percent, speed, downloaded, total, retryState) => {
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
const data = { const data = {
message: message || null, message: message || null,
percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null, percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null,
speed: speed !== null && speed !== undefined ? speed : null, speed: speed !== null && speed !== undefined ? speed : null,
downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null, downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null,
total: total !== null && total !== undefined ? total : null, total: total !== null && total !== undefined ? total : null,
retryState: retryState || null retryState: retryState || null
}; };
mainWindow.webContents.send('progress-update', data); mainWindow.webContents.send('progress-update', data);
} }
}; };
const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath, gpuPreference); const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath, gpuPreference);
@@ -390,108 +390,113 @@ ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, g
} }
}); });
ipcMain.handle('install-game', async (event, playerName, javaPath, installPath, branch) => { ipcMain.handle('install-game', async (event, playerName, javaPath, installPath, branch) => {
try { try {
console.log(`[IPC] install-game called with branch: ${branch || 'default'}`); console.log(`[IPC] install-game called with parameters:`);
console.log(` - playerName: ${playerName}`);
// Signal installation start console.log(` - javaPath: ${javaPath}`);
if (mainWindow && !mainWindow.isDestroyed()) { console.log(` - installPath: ${installPath}`);
mainWindow.webContents.send('installation-start'); console.log(` - branch: ${branch}`);
} console.log(`[IPC] branch type: ${typeof branch}, value: ${JSON.stringify(branch)}`);
const progressCallback = (message, percent, speed, downloaded, total, retryState) => { // Signal installation start
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
const data = { mainWindow.webContents.send('installation-start');
message: message || null, }
percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null,
speed: speed !== null && speed !== undefined ? speed : null, const progressCallback = (message, percent, speed, downloaded, total, retryState) => {
downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null, if (mainWindow && !mainWindow.isDestroyed()) {
total: total !== null && total !== undefined ? total : null, const data = {
retryState: retryState || null message: message || null,
}; percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null,
mainWindow.webContents.send('progress-update', data); speed: speed !== null && speed !== undefined ? speed : null,
} downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null,
}; total: total !== null && total !== undefined ? total : null,
retryState: retryState || null
const result = await installGame(playerName, progressCallback, javaPath, installPath, branch); };
mainWindow.webContents.send('progress-update', data);
// Signal installation end }
if (mainWindow && !mainWindow.isDestroyed()) { };
mainWindow.webContents.send('installation-end');
} const result = await installGame(playerName, progressCallback, javaPath, installPath, branch);
// Ensure we always return a result for the IPC handler // Signal installation end
const successResponse = result || { success: true }; if (mainWindow && !mainWindow.isDestroyed()) {
console.log('[Main] Returning success response for install-game:', successResponse); mainWindow.webContents.send('installation-end');
return successResponse; }
} catch (error) {
console.error('Install error:', error); // Ensure we always return a result for the IPC handler
const errorMessage = error.message || error.toString(); const successResponse = result || { success: true };
console.log('[Main] Returning success response for install-game:', successResponse);
// Enhanced error data extraction for both download and Butler errors return successResponse;
let errorData = { } catch (error) {
message: errorMessage, console.error('Install error:', error);
error: true, const errorMessage = error.message || error.toString();
canRetry: true,
retryData: null // Enhanced error data extraction for both download and Butler errors
}; let errorData = {
message: errorMessage,
// Handle Butler-specific errors error: true,
if (error.butlerError) { canRetry: true,
console.log('[Main] Processing Butler error with retry context'); retryData: null
errorData.retryData = { };
branch: error.branch || 'release',
fileName: error.fileName || '4.pwr', // Handle Butler-specific errors
cacheDir: error.cacheDir if (error.butlerError) {
}; console.log('[Main] Processing Butler error with retry context');
errorData.canRetry = error.canRetry !== undefined ? error.canRetry : true; errorData.retryData = {
branch: error.branch || 'release',
// Add Butler-specific error details fileName: error.fileName || '4.pwr',
if (error.stderr) { cacheDir: error.cacheDir
console.error('[Main] Butler stderr:', error.stderr); };
} errorData.canRetry = error.canRetry !== undefined ? error.canRetry : true;
if (error.stdout) {
console.log('[Main] Butler stdout:', error.stdout); // Add Butler-specific error details
} if (error.stderr) {
if (error.errorCode) { console.error('[Main] Butler stderr:', error.stderr);
console.log('[Main] Butler error code:', error.errorCode); }
} if (error.stdout) {
} console.log('[Main] Butler stdout:', error.stdout);
// Handle PWR download errors }
else if (error.branch && error.fileName) { if (error.errorCode) {
console.log('[Main] Processing PWR download error with retry context'); console.log('[Main] Butler error code:', error.errorCode);
errorData.retryData = { }
branch: error.branch, }
fileName: error.fileName, // Handle PWR download errors
cacheDir: error.cacheDir else if (error.branch && error.fileName) {
}; console.log('[Main] Processing PWR download error with retry context');
errorData.canRetry = error.canRetry !== undefined ? error.canRetry : true; errorData.retryData = {
} branch: error.branch,
// Default fallback for other errors fileName: error.fileName,
else { cacheDir: error.cacheDir
console.log('[Main] Processing generic error, creating default retry data'); };
errorData.retryData = { errorData.canRetry = error.canRetry !== undefined ? error.canRetry : true;
branch: 'release', }
fileName: '4.pwr' // Default fallback for other errors
}; else {
} console.log('[Main] Processing generic error, creating default retry data');
errorData.retryData = {
// Send enhanced error info for retry UI branch: 'release',
if (mainWindow && !mainWindow.isDestroyed()) { fileName: '4.pwr'
console.log('[Main] Sending error data to renderer:', errorData); };
mainWindow.webContents.send('progress-update', errorData); }
}
// Send enhanced error info for retry UI
// Signal installation end on error too if (mainWindow && !mainWindow.isDestroyed()) {
if (mainWindow && !mainWindow.isDestroyed()) { console.log('[Main] Sending error data to renderer:', errorData);
mainWindow.webContents.send('installation-end'); mainWindow.webContents.send('progress-update', errorData);
} }
// Always return a proper response to prevent timeout // Signal installation end on error too
const errorResponse = { success: false, error: errorMessage }; if (mainWindow && !mainWindow.isDestroyed()) {
console.log('[Main] Returning error response for install-game:', errorResponse); mainWindow.webContents.send('installation-end');
return errorResponse; }
}
// Always return a proper response to prevent timeout
const errorResponse = { success: false, error: errorMessage };
console.log('[Main] Returning error response for install-game:', errorResponse);
return errorResponse;
}
}); });
ipcMain.handle('save-username', (event, username) => { ipcMain.handle('save-username', (event, username) => {
@@ -581,19 +586,19 @@ ipcMain.handle('select-install-path', async () => {
ipcMain.handle('accept-first-launch-update', async (event, existingGame) => { ipcMain.handle('accept-first-launch-update', async (event, existingGame) => {
try { try {
const progressCallback = (message, percent, speed, downloaded, total, retryState) => { const progressCallback = (message, percent, speed, downloaded, total, retryState) => {
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
const data = { const data = {
message: message || null, message: message || null,
percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null, percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null,
speed: speed !== null && speed !== undefined ? speed : null, speed: speed !== null && speed !== undefined ? speed : null,
downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null, downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null,
total: total !== null && total !== undefined ? total : null, total: total !== null && total !== undefined ? total : null,
retryState: retryState || null retryState: retryState || null
}; };
mainWindow.webContents.send('first-launch-progress', data); mainWindow.webContents.send('first-launch-progress', data);
} }
}; };
const result = await proposeGameUpdate(existingGame, progressCallback); const result = await proposeGameUpdate(existingGame, progressCallback);
@@ -636,94 +641,94 @@ ipcMain.handle('uninstall-game', async () => {
} }
}); });
ipcMain.handle('repair-game', async () => { ipcMain.handle('repair-game', async () => {
try { try {
const progressCallback = (message, percent, speed, downloaded, total, retryState) => { const progressCallback = (message, percent, speed, downloaded, total, retryState) => {
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
const data = { const data = {
message: message || null, message: message || null,
percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null, percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null,
speed: speed !== null && speed !== undefined ? speed : null, speed: speed !== null && speed !== undefined ? speed : null,
downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null, downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null,
total: total !== null && total !== undefined ? total : null, total: total !== null && total !== undefined ? total : null,
retryState: retryState || null retryState: retryState || null
}; };
mainWindow.webContents.send('progress-update', data); mainWindow.webContents.send('progress-update', data);
} }
}; };
const result = await repairGame(progressCallback); const result = await repairGame(progressCallback);
return result; return result;
} catch (error) { } catch (error) {
console.error('Repair error:', error); console.error('Repair error:', error);
const errorMessage = error.message || error.toString(); const errorMessage = error.message || error.toString();
return { success: false, error: errorMessage }; return { success: false, error: errorMessage };
} }
}); });
ipcMain.handle('retry-download', async (event, retryData) => { ipcMain.handle('retry-download', async (event, retryData) => {
try { try {
console.log('[IPC] retry-download called with data:', retryData); console.log('[IPC] retry-download called with data:', retryData);
// Handle null retry data gracefully // Handle null retry data gracefully
if (!retryData || !retryData.branch || !retryData.fileName) { if (!retryData || !retryData.branch || !retryData.fileName) {
console.log('[IPC] Invalid retry data, using defaults'); console.log('[IPC] Invalid retry data, using defaults');
retryData = { retryData = {
branch: 'release', branch: 'release',
fileName: '4.pwr' fileName: '4.pwr'
}; };
} }
const progressCallback = (message, percent, speed, downloaded, total, retryState) => { const progressCallback = (message, percent, speed, downloaded, total, retryState) => {
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
const data = { const data = {
message: message || null, message: message || null,
percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null, percent: percent !== null && percent !== undefined ? Math.min(100, Math.max(0, percent)) : null,
speed: speed !== null && speed !== undefined ? speed : null, speed: speed !== null && speed !== undefined ? speed : null,
downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null, downloaded: downloaded !== null && downloaded !== undefined ? downloaded : null,
total: total !== null && total !== undefined ? total : null, total: total !== null && total !== undefined ? total : null,
retryState: retryState || null retryState: retryState || null
}; };
mainWindow.webContents.send('progress-update', data); mainWindow.webContents.send('progress-update', data);
} }
}; };
// Extract PWR download info from retryData // Extract PWR download info from retryData
const branch = retryData.branch; const branch = retryData.branch;
const fileName = retryData.fileName; const fileName = retryData.fileName;
const cacheDir = retryData.cacheDir; const cacheDir = retryData.cacheDir;
console.log(`[IPC] Retrying PWR download: branch=${branch}, fileName=${fileName}`); console.log(`[IPC] Retrying PWR download: branch=${branch}, fileName=${fileName}`);
// Perform the retry with enhanced context // Perform the retry with enhanced context
await retryPWRDownload(branch, fileName, progressCallback, cacheDir); await retryPWRDownload(branch, fileName, progressCallback, cacheDir);
return { success: true }; return { success: true };
} catch (error) { } catch (error) {
console.error('Retry download error:', error); console.error('Retry download error:', error);
const errorMessage = error.message || error.toString(); const errorMessage = error.message || error.toString();
// Send error update to frontend with context // Send error update to frontend with context
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
const data = { const data = {
message: errorMessage, message: errorMessage,
error: true, error: true,
canRetry: true, canRetry: true,
retryData: { retryData: {
branch: retryData?.branch || 'release', branch: retryData?.branch || 'release',
fileName: retryData?.fileName || '4.pwr', fileName: retryData?.fileName || '4.pwr',
cacheDir: retryData?.cacheDir cacheDir: retryData?.cacheDir
} }
}; };
mainWindow.webContents.send('progress-update', data); mainWindow.webContents.send('progress-update', data);
} }
// Always return a proper response to prevent timeout // Always return a proper response to prevent timeout
const errorResponse = { success: false, error: errorMessage }; const errorResponse = { success: false, error: errorMessage };
console.log('[Main] Returning error response for retry-download:', errorResponse); console.log('[Main] Returning error response for retry-download:', errorResponse);
return errorResponse; return errorResponse;
} }
}); });
ipcMain.handle('get-hytale-news', async () => { ipcMain.handle('get-hytale-news', async () => {
try { try {
@@ -747,8 +752,9 @@ ipcMain.handle('open-external', async (event, url) => {
ipcMain.handle('open-game-location', async () => { ipcMain.handle('open-game-location', async () => {
try { try {
const { getResolvedAppDir } = require('./backend/launcher'); const { getResolvedAppDir, loadVersionBranch } = require('./backend/launcher');
const gameDir = path.join(getResolvedAppDir(), 'release', 'package', 'game'); const branch = loadVersionBranch();
const gameDir = path.join(getResolvedAppDir(), branch, 'package', 'game');
if (fs.existsSync(gameDir)) { if (fs.existsSync(gameDir)) {
await shell.openPath(gameDir); await shell.openPath(gameDir);
@@ -839,7 +845,7 @@ ipcMain.handle('load-settings', async () => {
} }
}); });
const { getModsPath, loadInstalledMods, downloadMod, uninstallMod, toggleMod, getCurrentUuid, getAllUuidMappings, setUuidForUser, generateNewUuid, deleteUuidForUser, resetCurrentUserUuid } = require('./backend/launcher'); const { getModsPath, loadInstalledMods, downloadMod, uninstallMod, toggleMod, getCurrentUuid, getAllUuidMappings, setUuidForUser, generateNewUuid, deleteUuidForUser, resetCurrentUserUuid } = require('./backend/launcher');
const { retryPWRDownload } = require('./backend/managers/gameManager'); const { retryPWRDownload } = require('./backend/managers/gameManager');
const os = require('os'); const os = require('os');

View File

@@ -2,7 +2,7 @@ const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', { contextBridge.exposeInMainWorld('electronAPI', {
launchGame: (playerName, javaPath, installPath, gpuPreference) => ipcRenderer.invoke('launch-game', playerName, javaPath, installPath, gpuPreference), launchGame: (playerName, javaPath, installPath, gpuPreference) => ipcRenderer.invoke('launch-game', playerName, javaPath, installPath, gpuPreference),
installGame: (playerName, javaPath, installPath) => ipcRenderer.invoke('install-game', playerName, javaPath, installPath), installGame: (playerName, javaPath, installPath, branch) => ipcRenderer.invoke('install-game', playerName, javaPath, installPath, branch),
closeWindow: () => ipcRenderer.invoke('window-close'), closeWindow: () => ipcRenderer.invoke('window-close'),
minimizeWindow: () => ipcRenderer.invoke('window-minimize'), minimizeWindow: () => ipcRenderer.invoke('window-minimize'),
maximizeWindow: () => ipcRenderer.invoke('window-maximize'), maximizeWindow: () => ipcRenderer.invoke('window-maximize'),