feat: Add option to toggle hardware acceleration for launcher #170

This commit is contained in:
Rahul-Sahani04
2026-01-25 21:08:47 +05:30
parent 2a5780c2d4
commit f0f19f690f
8 changed files with 422 additions and 308 deletions

3
.gitignore vendored
View File

@@ -19,4 +19,5 @@ backend/patcher/
# macOS Specific # macOS Specific
.DS_Store .DS_Store
*.zst.DS_Store *.zst.DS_Store
bun.lock

View File

@@ -127,17 +127,20 @@
<label class="form-label" data-i18n="install.gameBranch">Game Version</label> <label class="form-label" data-i18n="install.gameBranch">Game Version</label>
<div class="radio-group"> <div class="radio-group">
<label class="radio-label"> <label class="radio-label">
<input type="radio" name="installBranch" value="release" class="custom-radio" checked> <input type="radio" name="installBranch" value="release" class="custom-radio"
checked>
<span class="radio-text"> <span class="radio-text">
<i class="fas fa-check-circle mr-2"></i> <i class="fas fa-check-circle mr-2"></i>
<span data-i18n="install.releaseVersion">Release (Stable)</span> <span data-i18n="install.releaseVersion">Release (Stable)</span>
</span> </span>
</label> </label>
<label class="radio-label"> <label class="radio-label">
<input type="radio" name="installBranch" value="pre-release" class="custom-radio"> <input type="radio" name="installBranch" value="pre-release"
class="custom-radio">
<span class="radio-text"> <span class="radio-text">
<i class="fas fa-flask mr-2"></i> <i class="fas fa-flask mr-2"></i>
<span data-i18n="install.preReleaseVersion">Pre-Release (Experimental)</span> <span data-i18n="install.preReleaseVersion">Pre-Release
(Experimental)</span>
</span> </span>
</label> </label>
</div> </div>
@@ -361,226 +364,247 @@
<div class="settings-option"> <div class="settings-option">
<div class="settings-input-group"> <div class="settings-input-group">
<label class="settings-input-label" data-i18n="settings.gameBranch">Game Branch</label> <label class="settings-input-label" data-i18n="settings.gameBranch">Game
Branch</label>
<div class="segmented-control"> <div class="segmented-control">
<input type="radio" id="branch-release" name="gameBranch" value="release" checked> <input type="radio" id="branch-release" name="gameBranch"
<label for="branch-release" data-i18n="settings.branchRelease">Release</label> value="release" checked>
<input type="radio" id="branch-pre-release" name="gameBranch" value="pre-release"> <label for="branch-release"
<label for="branch-pre-release" data-i18n="settings.branchPreRelease">Pre-Release</label> data-i18n="settings.branchRelease">Release</label>
<input type="radio" id="branch-pre-release" name="gameBranch"
value="pre-release">
<label for="branch-pre-release"
data-i18n="settings.branchPreRelease">Pre-Release</label>
</div> </div>
<p class="settings-hint"> <p class="settings-hint">
<i class="fas fa-info-circle"></i> <i class="fas fa-info-circle"></i>
<span data-i18n="settings.branchHint">Switch between stable release and experimental pre-release versions</span> <span data-i18n="settings.branchHint">Switch between stable release and
experimental pre-release versions</span>
</p> </p>
<p class="settings-hint" style="color: #f39c12;"> <p class="settings-hint" style="color: #f39c12;">
<i class="fas fa-exclamation-triangle"></i> <i class="fas fa-exclamation-triangle"></i>
<span data-i18n="settings.branchWarning">Changing branch will download and install a different game version</span> <span data-i18n="settings.branchWarning">Changing branch will download
and install a different game version</span>
</p> </p>
</div> </div>
</div> </div>
<div class="settings-option"> <div class="settings-option">
<label class="settings-input-label" data-i18n="settings.gpuPreference">GPU <label class="settings-input-label" data-i18n="settings.gpuPreference">GPU
Preference</label> Preference</label>
<div class="segmented-control"> <div class="segmented-control">
<input type="radio" id="gpu-auto" name="gpuPreference" value="auto" <input type="radio" id="gpu-auto" name="gpuPreference" value="auto" checked>
checked> <label for="gpu-auto" data-i18n="settings.gpuAuto">Auto</label>
<label for="gpu-auto" data-i18n="settings.gpuAuto">Auto</label> <input type="radio" id="gpu-integrated" name="gpuPreference"
<input type="radio" id="gpu-integrated" name="gpuPreference" value="integrated">
value="integrated"> <label for="gpu-integrated"
<label for="gpu-integrated" data-i18n="settings.gpuIntegrated">Integrated</label>
data-i18n="settings.gpuIntegrated">Integrated</label> <input type="radio" id="gpu-dedicated" name="gpuPreference"
<input type="radio" id="gpu-dedicated" name="gpuPreference" value="dedicated">
value="dedicated"> <label for="gpu-dedicated"
<label for="gpu-dedicated" data-i18n="settings.gpuDedicated">Dedicated</label>
data-i18n="settings.gpuDedicated">Dedicated</label>
</div>
<p class="settings-hint">
<i class="fas fa-info-circle"></i>
<span data-i18n="settings.gpuHint">Select your preferred GPU (Linux:
affects DRI_PRIME)</span>
</p>
<div id="gpu-detection-info" class="gpu-detection-info"></div>
</div> </div>
<p class="settings-hint">
<i class="fas fa-info-circle"></i>
<span data-i18n="settings.gpuHint">Select your preferred GPU (Linux:
affects DRI_PRIME)</span>
</p>
<div id="gpu-detection-info" class="gpu-detection-info"></div>
</div> </div>
</div> </div>
</div>
<div class="settings-section"> <div class="settings-section">
<h3 class="settings-section-title"> <h3 class="settings-section-title">
<i class="fas fa-fingerprint"></i> <i class="fas fa-fingerprint"></i>
<span data-i18n="settings.account">Player UUID Management</span> <span data-i18n="settings.account">Player UUID Management</span>
</h3> </h3>
<div class="settings-option"> <div class="settings-option">
<div class="settings-input-group"> <div class="settings-input-group">
<label class="settings-input-label" data-i18n="settings.currentUUID">Current <label class="settings-input-label" data-i18n="settings.currentUUID">Current
UUID</label> UUID</label>
<div class="uuid-display-container"> <div class="uuid-display-container">
<input type="text" id="currentUuid" class="settings-input uuid-input" <input type="text" id="currentUuid" class="settings-input uuid-input"
readonly data-i18n-placeholder="settings.uuidPlaceholder" /> readonly data-i18n-placeholder="settings.uuidPlaceholder" />
<button id="copyUuidBtn" class="uuid-btn copy-btn" title="Copy UUID"> <button id="copyUuidBtn" class="uuid-btn copy-btn" title="Copy UUID">
<i class="fas fa-copy"></i> <i class="fas fa-copy"></i>
</button> </button>
<button id="regenerateUuidBtn" class="uuid-btn regenerate-btn" <button id="regenerateUuidBtn" class="uuid-btn regenerate-btn"
title="Generate New UUID"> title="Generate New UUID">
<i class="fas fa-sync-alt"></i> <i class="fas fa-sync-alt"></i>
</button>
</div>
<p class="settings-hint">
<i class="fas fa-info-circle"></i>
<span data-i18n="settings.uuidHint">Your unique player identifier for
this username</span>
</p>
</div>
</div>
<div class="settings-option">
<div class="settings-button-group">
<button id="manageUuidsBtn" class="settings-action-btn">
<i class="fas fa-list"></i>
<div class="btn-content">
<div class="btn-title" data-i18n="settings.manageUUIDs">Manage All
UUIDs</div>
<div class="btn-description" data-i18n="settings.manageUUIDsDesc">
View and manage all player UUIDs</div>
</div>
</button> </button>
</div> </div>
<p class="settings-hint">
<i class="fas fa-info-circle"></i>
<span data-i18n="settings.uuidHint">Your unique player identifier for
this username</span>
</p>
</div> </div>
</div> </div>
<div class="settings-section"> <div class="settings-option">
<h3 class="settings-section-title"> <div class="settings-button-group">
<i class="fab fa-discord"></i> <button id="manageUuidsBtn" class="settings-action-btn">
<span data-i18n="settings.discord">Discord Integration</span> <i class="fas fa-list"></i>
</h3> <div class="btn-content">
<div class="btn-title" data-i18n="settings.manageUUIDs">Manage All
<div class="settings-option"> UUIDs</div>
<label class="settings-checkbox"> <div class="btn-description" data-i18n="settings.manageUUIDsDesc">
<input type="checkbox" id="discordRPCCheck" checked /> View and manage all player UUIDs</div>
<span class="checkmark"></span>
<div class="checkbox-content">
<div class="checkbox-title" data-i18n="settings.enableRPC">Enable
Discord Rich Presence</div>
<div class="checkbox-description"
data-i18n="settings.discordDescription">Show your launcher activity
on Discord
</div>
</div> </div>
</label> </button>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">
<i class="fas fa-window-close"></i>
<span data-i18n="settings.closeLauncher">Launcher Behavior</span>
</h3>
<div class="settings-option">
<label class="settings-checkbox">
<input type="checkbox" id="closeLauncherCheck" />
<span class="checkmark"></span>
<div class="checkbox-content">
<div class="checkbox-title" data-i18n="settings.closeOnStart">Close Launcher on game start</div>
<div class="checkbox-description" data-i18n="settings.closeOnStartDescription">
Automatically close the launcher after Hytale has launched
</div>
</div>
</label>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">
<i class="fas fa-coffee"></i>
<span data-i18n="settings.java">Java Runtime</span>
</h3>
<div class="settings-option">
<label class="settings-checkbox">
<input type="checkbox" id="customJavaCheck" />
<span class="checkmark"></span>
<div class="checkbox-content">
<div class="checkbox-title" data-i18n="settings.useCustomJava">Use
Custom Java Path</div>
<div class="checkbox-description" data-i18n="settings.javaDescription">
Override the bundled Java runtime with
your own installation</div>
</div>
</label>
</div>
<div id="customJavaOptions" class="custom-java-options" style="display: none;">
<div class="settings-input-group">
<label class="settings-input-label" data-i18n="settings.javaPath">Java
Executable Path</label>
<div class="settings-input-with-button">
<input type="text" id="customJavaPath" class="settings-input"
data-i18n-placeholder="settings.javaPathPlaceholder" readonly />
<button id="browseJavaBtn" class="settings-browse-btn">
<i class="fas fa-folder-open"></i>
<span data-i18n="settings.javaBrowse">Browse</span>
</button>
</div>
<p class="settings-hint">
<i class="fas fa-info-circle"></i>
<span data-i18n="settings.javaHint">Select the Java installation folder
(supports Windows, Mac, Linux)</span>
</p>
</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">
<i class="fas fa-language"></i>
<span data-i18n="settings.language">Language</span>
</h3>
<div class="settings-option">
<div class="settings-input-group">
<label class="settings-input-label"
data-i18n="settings.selectLanguage">Select Language</label>
<select id="languageSelect" class="settings-input">
<!-- Options populated by i18n.js -->
</select>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
<div id="logs-page" class="page"> <div class="settings-section">
<div class="logs-container"> <h3 class="settings-section-title">
<div class="logs-header"> <i class="fab fa-discord"></i>
<h2 class="logs-title"> <span data-i18n="settings.discord">Discord Integration</span>
<i class="fas fa-terminal"></i> </h3>
<span data-i18n="settings.logs">SYSTEM LOGS</span>
</h2> <div class="settings-option">
<div class="logs-actions"> <label class="settings-checkbox">
<button class="logs-action-btn" onclick="copyLogs()"> <input type="checkbox" id="discordRPCCheck" checked />
<i class="fas fa-copy"></i> <span data-i18n="settings.logsCopy">Copy</span> <span class="checkmark"></span>
</button> <div class="checkbox-content">
<button class="logs-action-btn" onclick="refreshLogs()"> <div class="checkbox-title" data-i18n="settings.enableRPC">Enable
<i class="fas fa-sync-alt"></i> <span Discord Rich Presence</div>
data-i18n="settings.logsRefresh">Refresh</span> <div class="checkbox-description" data-i18n="settings.discordDescription">
</button> Show your launcher activity
<button class="logs-action-btn" onclick="openLogsFolder()"> on Discord
<i class="fas fa-folder-open"></i> <span data-i18n="settings.logsFolder">Open </div>
Folder</span> </div>
</button> </label>
</div> </div>
</div> </div>
<div id="logsTerminal" class="logs-terminal">
<div class="text-gray-500 text-center mt-10" data-i18n="settings.logsLoading">Loading <div class="settings-section">
logs...</div> <h3 class="settings-section-title">
<i class="fas fa-window-close"></i>
<span data-i18n="settings.closeLauncher">Launcher Behavior</span>
</h3>
<div class="settings-option">
<label class="settings-checkbox">
<input type="checkbox" id="closeLauncherCheck" />
<span class="checkmark"></span>
<div class="checkbox-content">
<div class="checkbox-title" data-i18n="settings.closeOnStart">Close Launcher
on game start</div>
<div class="checkbox-description"
data-i18n="settings.closeOnStartDescription">
Automatically close the launcher after Hytale has launched
</div>
</div>
</label>
</div>
<div class="settings-option">
<label class="settings-checkbox">
<input type="checkbox" id="launcherHwAccelCheck" />
<span class="checkmark"></span>
<div class="checkbox-content">
<div class="checkbox-title" data-i18n="settings.hwAccel">Launcher Hardware
Acceleration</div>
<div class="checkbox-description" data-i18n="settings.hwAccelDescription">
Enable hardware acceleration for the launcher UI (Requires restart)
</div>
</div>
</label>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">
<i class="fas fa-coffee"></i>
<span data-i18n="settings.java">Java Runtime</span>
</h3>
<div class="settings-option">
<label class="settings-checkbox">
<input type="checkbox" id="customJavaCheck" />
<span class="checkmark"></span>
<div class="checkbox-content">
<div class="checkbox-title" data-i18n="settings.useCustomJava">Use
Custom Java Path</div>
<div class="checkbox-description" data-i18n="settings.javaDescription">
Override the bundled Java runtime with
your own installation</div>
</div>
</label>
</div>
<div id="customJavaOptions" class="custom-java-options" style="display: none;">
<div class="settings-input-group">
<label class="settings-input-label" data-i18n="settings.javaPath">Java
Executable Path</label>
<div class="settings-input-with-button">
<input type="text" id="customJavaPath" class="settings-input"
data-i18n-placeholder="settings.javaPathPlaceholder" readonly />
<button id="browseJavaBtn" class="settings-browse-btn">
<i class="fas fa-folder-open"></i>
<span data-i18n="settings.javaBrowse">Browse</span>
</button>
</div>
<p class="settings-hint">
<i class="fas fa-info-circle"></i>
<span data-i18n="settings.javaHint">Select the Java installation folder
(supports Windows, Mac, Linux)</span>
</p>
</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">
<i class="fas fa-language"></i>
<span data-i18n="settings.language">Language</span>
</h3>
<div class="settings-option">
<div class="settings-input-group">
<label class="settings-input-label" data-i18n="settings.selectLanguage">Select
Language</label>
<select id="languageSelect" class="settings-input">
<!-- Options populated by i18n.js -->
</select>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div id="logs-page" class="page">
<div class="logs-container">
<div class="logs-header">
<h2 class="logs-title">
<i class="fas fa-terminal"></i>
<span data-i18n="settings.logs">SYSTEM LOGS</span>
</h2>
<div class="logs-actions">
<button class="logs-action-btn" onclick="copyLogs()">
<i class="fas fa-copy"></i> <span data-i18n="settings.logsCopy">Copy</span>
</button>
<button class="logs-action-btn" onclick="refreshLogs()">
<i class="fas fa-sync-alt"></i> <span
data-i18n="settings.logsRefresh">Refresh</span>
</button>
<button class="logs-action-btn" onclick="openLogsFolder()">
<i class="fas fa-folder-open"></i> <span data-i18n="settings.logsFolder">Open
Folder</span>
</button>
</div>
</div>
<div id="logsTerminal" class="logs-terminal">
<div class="text-gray-500 text-center mt-10" data-i18n="settings.logsLoading">Loading
logs...</div>
</div>
</div>
</div>
</div>
</main> </main>
</div> </div>
@@ -602,7 +626,7 @@
</div> </div>
</div> </div>
<div id="progressOverlay" class="progress-overlay" style="display: none;"> <div id="progressOverlay" class="progress-overlay" style="display: none;">
<div class="progress-content"> <div class="progress-content">
<div class="progress-info"> <div class="progress-info">
<span id="progressText" data-i18n="progress.initializing">Initializing...</span> <span id="progressText" data-i18n="progress.initializing">Initializing...</span>

View File

@@ -6,6 +6,7 @@ let browseJavaBtn;
let settingsPlayerName; let settingsPlayerName;
let discordRPCCheck; let discordRPCCheck;
let closeLauncherCheck; let closeLauncherCheck;
let launcherHwAccelCheck;
let gpuPreferenceRadios; let gpuPreferenceRadios;
let gameBranchRadios; let gameBranchRadios;
@@ -30,7 +31,7 @@ function showCustomConfirm(message, title, onConfirm, onCancel = null, confirmTe
title = title || (window.i18n ? window.i18n.t('confirm.defaultTitle') : 'Confirm Action'); title = title || (window.i18n ? window.i18n.t('confirm.defaultTitle') : 'Confirm Action');
confirmText = confirmText || (window.i18n ? window.i18n.t('common.confirm') : 'Confirm'); confirmText = confirmText || (window.i18n ? window.i18n.t('common.confirm') : 'Confirm');
cancelText = cancelText || (window.i18n ? window.i18n.t('common.cancel') : 'Cancel'); cancelText = cancelText || (window.i18n ? window.i18n.t('common.cancel') : 'Cancel');
const existingModal = document.querySelector('.custom-confirm-modal'); const existingModal = document.querySelector('.custom-confirm-modal');
if (existingModal) { if (existingModal) {
existingModal.remove(); existingModal.remove();
@@ -165,9 +166,10 @@ function setupSettingsElements() {
settingsPlayerName = document.getElementById('settingsPlayerName'); settingsPlayerName = document.getElementById('settingsPlayerName');
discordRPCCheck = document.getElementById('discordRPCCheck'); discordRPCCheck = document.getElementById('discordRPCCheck');
closeLauncherCheck = document.getElementById('closeLauncherCheck'); closeLauncherCheck = document.getElementById('closeLauncherCheck');
launcherHwAccelCheck = document.getElementById('launcherHwAccelCheck');
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); console.log('[Settings] gameBranchRadios found:', gameBranchRadios.length);
@@ -206,6 +208,10 @@ function setupSettingsElements() {
closeLauncherCheck.addEventListener('change', saveCloseLauncher); closeLauncherCheck.addEventListener('change', saveCloseLauncher);
} }
if (launcherHwAccelCheck) {
launcherHwAccelCheck.addEventListener('change', saveLauncherHwAccel);
}
// UUID event listeners // UUID event listeners
if (copyUuidBtn) { if (copyUuidBtn) {
@@ -266,7 +272,7 @@ function setupSettingsElements() {
function toggleCustomJava() { function toggleCustomJava() {
if (!customJavaOptions) return; if (!customJavaOptions) return;
if (customJavaCheck && customJavaCheck.checked) { if (customJavaCheck && customJavaCheck.checked) {
customJavaOptions.style.display = 'block'; customJavaOptions.style.display = 'block';
} else { } else {
@@ -329,12 +335,12 @@ async function saveDiscordRPC() {
if (window.electronAPI && window.electronAPI.saveDiscordRPC && discordRPCCheck) { if (window.electronAPI && window.electronAPI.saveDiscordRPC && discordRPCCheck) {
const enabled = discordRPCCheck.checked; const enabled = discordRPCCheck.checked;
console.log('Saving Discord RPC setting:', enabled); console.log('Saving Discord RPC setting:', enabled);
const result = await window.electronAPI.saveDiscordRPC(enabled); const result = await window.electronAPI.saveDiscordRPC(enabled);
if (result && result.success) { if (result && result.success) {
console.log('Discord RPC setting saved successfully:', enabled); console.log('Discord RPC setting saved successfully:', enabled);
// Feedback visuel pour l'utilisateur // Feedback visuel pour l'utilisateur
if (enabled) { if (enabled) {
const msg = window.i18n ? window.i18n.t('notifications.discordEnabled') : 'Discord Rich Presence enabled'; const msg = window.i18n ? window.i18n.t('notifications.discordEnabled') : 'Discord Rich Presence enabled';
@@ -391,13 +397,42 @@ async function loadCloseLauncher() {
} }
} }
async function saveLauncherHwAccel() {
try {
if (window.electronAPI && window.electronAPI.saveLauncherHardwareAcceleration && launcherHwAccelCheck) {
const enabled = launcherHwAccelCheck.checked;
await window.electronAPI.saveLauncherHardwareAcceleration(enabled);
const msg = window.i18n ? window.i18n.t('notifications.hwAccelSaved') : 'Setting saved. Please restart the launcher to apply changes.';
showNotification(msg, 'success');
}
} catch (error) {
console.error('Error saving hardware acceleration setting:', error);
const msg = window.i18n ? window.i18n.t('notifications.hwAccelSaveFailed') : 'Failed to save setting';
showNotification(msg, 'error');
}
}
async function loadLauncherHwAccel() {
try {
if (window.electronAPI && window.electronAPI.loadLauncherHardwareAcceleration) {
const enabled = await window.electronAPI.loadLauncherHardwareAcceleration();
if (launcherHwAccelCheck) {
launcherHwAccelCheck.checked = enabled;
}
}
} catch (error) {
console.error('Error loading hardware acceleration setting:', error);
}
}
async function savePlayerName() { async function savePlayerName() {
try { try {
if (!window.electronAPI || !settingsPlayerName) return; if (!window.electronAPI || !settingsPlayerName) return;
const playerName = settingsPlayerName.value.trim(); const playerName = settingsPlayerName.value.trim();
if (!playerName) { if (!playerName) {
const msg = window.i18n ? window.i18n.t('notifications.playerNameRequired') : 'Please enter a valid player name'; const msg = window.i18n ? window.i18n.t('notifications.playerNameRequired') : 'Please enter a valid player name';
showNotification(msg, 'error'); showNotification(msg, 'error');
@@ -407,7 +442,7 @@ async function savePlayerName() {
await window.electronAPI.saveUsername(playerName); await window.electronAPI.saveUsername(playerName);
const successMsg = window.i18n ? window.i18n.t('notifications.playerNameSaved') : 'Player name saved successfully'; const successMsg = window.i18n ? window.i18n.t('notifications.playerNameSaved') : 'Player name saved successfully';
showNotification(successMsg, 'success'); showNotification(successMsg, 'success');
} catch (error) { } catch (error) {
console.error('Error saving player name:', error); console.error('Error saving player name:', error);
const errorMsg = window.i18n ? window.i18n.t('notifications.playerNameSaveFailed') : 'Failed to save player name'; const errorMsg = window.i18n ? window.i18n.t('notifications.playerNameSaveFailed') : 'Failed to save player name';
@@ -418,7 +453,7 @@ async function savePlayerName() {
async function loadPlayerName() { async function loadPlayerName() {
try { try {
if (!window.electronAPI || !settingsPlayerName) return; if (!window.electronAPI || !settingsPlayerName) return;
const savedName = await window.electronAPI.loadUsername(); const savedName = await window.electronAPI.loadUsername();
if (savedName) { if (savedName) {
settingsPlayerName.value = savedName; settingsPlayerName.value = savedName;
@@ -507,6 +542,7 @@ async function loadAllSettings() {
await loadCurrentUuid(); await loadCurrentUuid();
await loadDiscordRPC(); await loadDiscordRPC();
await loadCloseLauncher(); await loadCloseLauncher();
await loadLauncherHwAccel();
await loadGpuPreference(); await loadGpuPreference();
await loadVersionBranch(); await loadVersionBranch();
} }
@@ -583,7 +619,7 @@ async function regenerateCurrentUuid() {
const title = window.i18n ? window.i18n.t('confirm.regenerateUuidTitle') : 'Generate New UUID'; const title = window.i18n ? window.i18n.t('confirm.regenerateUuidTitle') : 'Generate New UUID';
const confirmBtn = window.i18n ? window.i18n.t('confirm.regenerateUuidButton') : 'Generate'; const confirmBtn = window.i18n ? window.i18n.t('confirm.regenerateUuidButton') : 'Generate';
const cancelBtn = window.i18n ? window.i18n.t('common.cancel') : 'Cancel'; const cancelBtn = window.i18n ? window.i18n.t('common.cancel') : 'Cancel';
showCustomConfirm( showCustomConfirm(
message, message,
title, title,
@@ -614,7 +650,7 @@ async function performRegenerateUuid() {
if (modalCurrentUuid) modalCurrentUuid.value = result.uuid; if (modalCurrentUuid) modalCurrentUuid.value = result.uuid;
const msg = window.i18n ? window.i18n.t('notifications.uuidGenerated') : 'New UUID generated successfully!'; const msg = window.i18n ? window.i18n.t('notifications.uuidGenerated') : 'New UUID generated successfully!';
showNotification(msg, 'success'); showNotification(msg, 'success');
if (uuidModal && uuidModal.style.display !== 'none') { if (uuidModal && uuidModal.style.display !== 'none') {
await loadAllUuids(); await loadAllUuids();
} }
@@ -652,7 +688,7 @@ function closeUuidModal() {
async function loadAllUuids() { async function loadAllUuids() {
try { try {
if (!uuidList) return; if (!uuidList) return;
uuidList.innerHTML = ` uuidList.innerHTML = `
<div class="uuid-loading"> <div class="uuid-loading">
<i class="fas fa-spinner fa-spin"></i> <i class="fas fa-spinner fa-spin"></i>
@@ -662,7 +698,7 @@ async function loadAllUuids() {
if (window.electronAPI && window.electronAPI.getAllUuidMappings) { if (window.electronAPI && window.electronAPI.getAllUuidMappings) {
const mappings = await window.electronAPI.getAllUuidMappings(); const mappings = await window.electronAPI.getAllUuidMappings();
if (mappings.length === 0) { if (mappings.length === 0) {
uuidList.innerHTML = ` uuidList.innerHTML = `
<div class="uuid-loading"> <div class="uuid-loading">
@@ -674,11 +710,11 @@ async function loadAllUuids() {
} }
uuidList.innerHTML = ''; uuidList.innerHTML = '';
for (const mapping of mappings) { for (const mapping of mappings) {
const item = document.createElement('div'); const item = document.createElement('div');
item.className = `uuid-list-item${mapping.isCurrent ? ' current' : ''}`; item.className = `uuid-list-item${mapping.isCurrent ? ' current' : ''}`;
item.innerHTML = ` item.innerHTML = `
<div class="uuid-item-info"> <div class="uuid-item-info">
<div class="uuid-item-username">${escapeHtml(mapping.username)}</div> <div class="uuid-item-username">${escapeHtml(mapping.username)}</div>
@@ -694,7 +730,7 @@ async function loadAllUuids() {
</button>` : ''} </button>` : ''}
</div> </div>
`; `;
uuidList.appendChild(item); uuidList.appendChild(item);
} }
} }
@@ -737,7 +773,7 @@ async function setCustomUuid() {
} }
const uuid = customUuidInput.value.trim(); const uuid = customUuidInput.value.trim();
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!uuidRegex.test(uuid)) { if (!uuidRegex.test(uuid)) {
const msg = window.i18n ? window.i18n.t('notifications.uuidInvalidFormat') : 'Invalid UUID format'; const msg = window.i18n ? window.i18n.t('notifications.uuidInvalidFormat') : 'Invalid UUID format';
@@ -767,33 +803,33 @@ async function setCustomUuid() {
} }
} }
async function performSetCustomUuid(uuid) { async function performSetCustomUuid(uuid) {
try { try {
if (window.electronAPI && window.electronAPI.setUuidForUser) { if (window.electronAPI && window.electronAPI.setUuidForUser) {
const username = getCurrentPlayerName(); const username = getCurrentPlayerName();
const result = await window.electronAPI.setUuidForUser(username, uuid); const result = await window.electronAPI.setUuidForUser(username, uuid);
if (result.success) {
if (currentUuidDisplay) currentUuidDisplay.value = uuid;
if (modalCurrentUuid) modalCurrentUuid.value = uuid;
if (customUuidInput) customUuidInput.value = '';
const msg = window.i18n ? window.i18n.t('notifications.uuidSetSuccess') : 'Custom UUID set successfully!';
showNotification(msg, 'success');
await loadAllUuids();
} else {
throw new Error(result.error || 'Failed to set custom UUID');
}
}
} catch (error) {
console.error('Error setting custom UUID:', error);
const msg = window.i18n ? window.i18n.t('notifications.uuidSetFailed').replace('{error}', error.message) : `Failed to set custom UUID: ${error.message}`;
showNotification(msg, 'error');
}
}
window.copyUuid = async function(uuid) { if (result.success) {
if (currentUuidDisplay) currentUuidDisplay.value = uuid;
if (modalCurrentUuid) modalCurrentUuid.value = uuid;
if (customUuidInput) customUuidInput.value = '';
const msg = window.i18n ? window.i18n.t('notifications.uuidSetSuccess') : 'Custom UUID set successfully!';
showNotification(msg, 'success');
await loadAllUuids();
} else {
throw new Error(result.error || 'Failed to set custom UUID');
}
}
} catch (error) {
console.error('Error setting custom UUID:', error);
const msg = window.i18n ? window.i18n.t('notifications.uuidSetFailed').replace('{error}', error.message) : `Failed to set custom UUID: ${error.message}`;
showNotification(msg, 'error');
}
}
window.copyUuid = async function (uuid) {
try { try {
if (navigator.clipboard) { if (navigator.clipboard) {
await navigator.clipboard.writeText(uuid); await navigator.clipboard.writeText(uuid);
@@ -807,13 +843,13 @@ window.copyUuid = async function(uuid) {
} }
}; };
window.deleteUuid = async function(username) { window.deleteUuid = async function (username) {
try { try {
const message = window.i18n ? window.i18n.t('confirm.deleteUuidMessage').replace('{username}', username) : `Are you sure you want to delete the UUID for "${username}"? This action cannot be undone.`; const message = window.i18n ? window.i18n.t('confirm.deleteUuidMessage').replace('{username}', username) : `Are you sure you want to delete the UUID for "${username}"? This action cannot be undone.`;
const title = window.i18n ? window.i18n.t('confirm.deleteUuidTitle') : 'Delete UUID'; const title = window.i18n ? window.i18n.t('confirm.deleteUuidTitle') : 'Delete UUID';
const confirmBtn = window.i18n ? window.i18n.t('confirm.deleteUuidButton') : 'Delete'; const confirmBtn = window.i18n ? window.i18n.t('confirm.deleteUuidButton') : 'Delete';
const cancelBtn = window.i18n ? window.i18n.t('common.cancel') : 'Cancel'; const cancelBtn = window.i18n ? window.i18n.t('common.cancel') : 'Cancel';
showCustomConfirm( showCustomConfirm(
message, message,
title, title,
@@ -834,21 +870,21 @@ window.deleteUuid = async function(username) {
async function performDeleteUuid(username) { async function performDeleteUuid(username) {
try { try {
if (window.electronAPI && window.electronAPI.deleteUuidForUser) { if (window.electronAPI && window.electronAPI.deleteUuidForUser) {
const result = await window.electronAPI.deleteUuidForUser(username); const result = await window.electronAPI.deleteUuidForUser(username);
if (result.success) { if (result.success) {
const msg = window.i18n ? window.i18n.t('notifications.uuidDeleteSuccess') : 'UUID deleted successfully!'; const msg = window.i18n ? window.i18n.t('notifications.uuidDeleteSuccess') : 'UUID deleted successfully!';
showNotification(msg, 'success'); showNotification(msg, 'success');
await loadAllUuids(); await loadAllUuids();
} else { } else {
throw new Error(result.error || 'Failed to delete UUID'); throw new Error(result.error || 'Failed to delete UUID');
}
} }
} catch (error) {
console.error('Error deleting UUID:', error);
const msg = window.i18n ? window.i18n.t('notifications.uuidDeleteFailed').replace('{error}', error.message) : `Failed to delete UUID: ${error.message}`;
showNotification(msg, 'error');
} }
} catch (error) {
console.error('Error deleting UUID:', error);
const msg = window.i18n ? window.i18n.t('notifications.uuidDeleteFailed').replace('{error}', error.message) : `Failed to delete UUID: ${error.message}`;
showNotification(msg, 'error');
}
} }
function escapeHtml(text) { function escapeHtml(text) {
@@ -915,10 +951,10 @@ async function handleBranchChange(event) {
} }
// Confirm branch change // Confirm branch change
const branchName = window.i18n ? const branchName = window.i18n ?
window.i18n.t(`settings.branch${newBranch === 'pre-release' ? 'PreRelease' : 'Release'}`) : window.i18n.t(`settings.branch${newBranch === 'pre-release' ? 'PreRelease' : 'Release'}`) :
newBranch; newBranch;
const message = window.i18n ? const message = window.i18n ?
window.i18n.t('settings.branchWarning') : window.i18n.t('settings.branchWarning') :
'Changing branch will download and install a different game version'; 'Changing branch will download and install a different game version';
@@ -946,7 +982,7 @@ async function switchBranch(newBranch) {
const switchingMsg = window.i18n ? const switchingMsg = window.i18n ?
window.i18n.t('settings.branchSwitching').replace('{branch}', newBranch) : window.i18n.t('settings.branchSwitching').replace('{branch}', newBranch) :
`Switching to ${newBranch}...`; `Switching to ${newBranch}...`;
showNotification(switchingMsg, 'info'); showNotification(switchingMsg, 'info');
// Lock play button // Lock play button
@@ -960,14 +996,14 @@ async function switchBranch(newBranch) {
// Suggest reinstalling // Suggest reinstalling
setTimeout(() => { setTimeout(() => {
const branchLabel = newBranch === 'release' ? const branchLabel = newBranch === 'release' ?
(window.i18n ? window.i18n.t('install.releaseVersion') : 'Release') : (window.i18n ? window.i18n.t('install.releaseVersion') : 'Release') :
(window.i18n ? window.i18n.t('install.preReleaseVersion') : 'Pre-Release'); (window.i18n ? window.i18n.t('install.preReleaseVersion') : 'Pre-Release');
const confirmMsg = window.i18n ? const confirmMsg = window.i18n ?
window.i18n.t('settings.branchInstallConfirm').replace('{branch}', branchLabel) : window.i18n.t('settings.branchInstallConfirm').replace('{branch}', branchLabel) :
`The game will be installed for the ${branchLabel} branch. Continue?`; `The game will be installed for the ${branchLabel} branch. Continue?`;
showCustomConfirm( showCustomConfirm(
confirmMsg, confirmMsg,
window.i18n ? window.i18n.t('settings.installRequired') : 'Installation Required', window.i18n ? window.i18n.t('settings.installRequired') : 'Installation Required',
@@ -980,31 +1016,31 @@ async function switchBranch(newBranch) {
try { try {
const playerName = await window.electronAPI.loadUsername(); const playerName = await window.electronAPI.loadUsername();
const result = await window.electronAPI.installGame(playerName || 'Player', '', '', newBranch); const result = await window.electronAPI.installGame(playerName || 'Player', '', '', newBranch);
if (result.success) { if (result.success) {
// Save branch ONLY after successful installation // Save branch ONLY after successful installation
await window.electronAPI.saveVersionBranch(newBranch); await window.electronAPI.saveVersionBranch(newBranch);
const switchedMsg = window.i18n ? const switchedMsg = window.i18n ?
window.i18n.t('settings.branchSwitched').replace('{branch}', newBranch) : window.i18n.t('settings.branchSwitched').replace('{branch}', newBranch) :
`Switched to ${newBranch} successfully!`; `Switched to ${newBranch} successfully!`;
const successMsg = window.i18n ? const successMsg = window.i18n ?
window.i18n.t('progress.installationComplete') : window.i18n.t('progress.installationComplete') :
'Installation completed successfully!'; 'Installation completed successfully!';
showNotification(switchedMsg, 'success'); showNotification(switchedMsg, 'success');
showNotification(successMsg, 'success'); showNotification(successMsg, 'success');
// Refresh radio buttons to reflect the new branch // Refresh radio buttons to reflect the new branch
await loadVersionBranch(); await loadVersionBranch();
console.log('[Settings] Radio buttons updated after branch switch'); console.log('[Settings] Radio buttons updated after branch switch');
setTimeout(() => { setTimeout(() => {
if (window.LauncherUI) { if (window.LauncherUI) {
window.LauncherUI.hideProgress(); window.LauncherUI.hideProgress();
} }
// Unlock play button // Unlock play button
const playButton = document.getElementById('playButton'); const playButton = document.getElementById('playButton');
if (playButton) { if (playButton) {
@@ -1017,16 +1053,16 @@ async function switchBranch(newBranch) {
} }
} catch (error) { } catch (error) {
console.error('Installation error:', error); console.error('Installation error:', error);
const errorMsg = window.i18n ? const errorMsg = window.i18n ?
window.i18n.t('progress.installationFailed').replace('{error}', error.message) : window.i18n.t('progress.installationFailed').replace('{error}', error.message) :
`Installation failed: ${error.message}`; `Installation failed: ${error.message}`;
showNotification(errorMsg, 'error'); showNotification(errorMsg, 'error');
if (window.LauncherUI) { if (window.LauncherUI) {
window.LauncherUI.hideProgress(); window.LauncherUI.hideProgress();
} }
// Revert radio selection to old branch // Revert radio selection to old branch
loadVersionBranch().then(oldBranch => { loadVersionBranch().then(oldBranch => {
const radioToCheck = document.querySelector(`input[name="gameBranch"][value="${oldBranch}"]`); const radioToCheck = document.querySelector(`input[name="gameBranch"][value="${oldBranch}"]`);
@@ -1034,7 +1070,7 @@ async function switchBranch(newBranch) {
radioToCheck.checked = true; radioToCheck.checked = true;
} }
}); });
// Unlock play button // Unlock play button
const playButton = document.getElementById('playButton'); const playButton = document.getElementById('playButton');
if (playButton) { if (playButton) {
@@ -1075,11 +1111,11 @@ async function loadVersionBranch() {
if (window.electronAPI && window.electronAPI.loadVersionBranch) { if (window.electronAPI && window.electronAPI.loadVersionBranch) {
const branch = await window.electronAPI.loadVersionBranch(); const branch = await window.electronAPI.loadVersionBranch();
console.log('[Settings] Loaded version_branch from config:', branch); console.log('[Settings] Loaded version_branch from config:', branch);
// Use default if branch is null/undefined // Use default if branch is null/undefined
const selectedBranch = branch || 'release'; const selectedBranch = branch || 'release';
console.log('[Settings] Selected branch:', selectedBranch); console.log('[Settings] Selected branch:', selectedBranch);
// Update radio buttons // Update radio buttons
if (gameBranchRadios && gameBranchRadios.length > 0) { if (gameBranchRadios && gameBranchRadios.length > 0) {
gameBranchRadios.forEach(radio => { gameBranchRadios.forEach(radio => {

View File

@@ -131,6 +131,8 @@
"closeLauncher": "Launcher Behavior", "closeLauncher": "Launcher Behavior",
"closeOnStart": "Close Launcher on game start", "closeOnStart": "Close Launcher on game start",
"closeOnStartDescription": "Automatically close the launcher after Hytale has launched", "closeOnStartDescription": "Automatically close the launcher after Hytale has launched",
"hwAccel": "Hardware Acceleration",
"hwAccelDescription": "Enable hardware acceleration for the launcher",
"gameBranch": "Game Branch", "gameBranch": "Game Branch",
"branchRelease": "Release", "branchRelease": "Release",
"branchPreRelease": "Pre-Release", "branchPreRelease": "Pre-Release",
@@ -207,7 +209,9 @@
"modsDownloadFailed": "Failed to download mod: {error}", "modsDownloadFailed": "Failed to download mod: {error}",
"modsToggleFailed": "Failed to toggle mod: {error}", "modsToggleFailed": "Failed to toggle mod: {error}",
"modsDeleteFailed": "Failed to delete mod: {error}", "modsDeleteFailed": "Failed to delete mod: {error}",
"modsModNotFound": "Mod information not found" "modsModNotFound": "Mod information not found",
"hwAccelSaved": "Hardware acceleration setting saved",
"hwAccelSaveFailed": "Failed to save hardware acceleration setting"
}, },
"confirm": { "confirm": {
"defaultTitle": "Confirm action", "defaultTitle": "Confirm action",
@@ -243,4 +247,4 @@
"installingGameFiles": "Installing game files...", "installingGameFiles": "Installing game files...",
"installComplete": "Installation complete!" "installComplete": "Installation complete!"
} }
} }

View File

@@ -166,13 +166,22 @@ function loadCloseLauncherOnStart() {
return config.closeLauncherOnStart !== undefined ? config.closeLauncherOnStart : false; return config.closeLauncherOnStart !== undefined ? config.closeLauncherOnStart : false;
} }
function saveLauncherHardwareAcceleration(enabled) {
saveConfig({ launcherHardwareAcceleration: !!enabled });
}
function loadLauncherHardwareAcceleration() {
const config = loadConfig();
return config.launcherHardwareAcceleration !== undefined ? config.launcherHardwareAcceleration : true;
}
function saveModsToConfig(mods) { function saveModsToConfig(mods) {
try { try {
const config = loadConfig(); const config = loadConfig();
// Config migration handles structure, but mod saves must go to the ACTIVE profile. // Config migration handles structure, but mod saves must go to the ACTIVE profile.
// Global installedMods is kept mainly for reference/migration. // Global installedMods is kept mainly for reference/migration.
// The profile is the source of truth for enabled mods. // The profile is the source of truth for enabled mods.
if (config.activeProfileId && config.profiles && config.profiles[config.activeProfileId]) { if (config.activeProfileId && config.profiles && config.profiles[config.activeProfileId]) {
@@ -369,6 +378,11 @@ module.exports = {
// Close Launcher export // Close Launcher export
saveCloseLauncherOnStart, saveCloseLauncherOnStart,
loadCloseLauncherOnStart, loadCloseLauncherOnStart,
// Hardware Acceleration functions
saveLauncherHardwareAcceleration,
loadLauncherHardwareAcceleration,
// Version Management exports // Version Management exports
saveVersionClient, saveVersionClient,
loadVersionClient, loadVersionClient,

View File

@@ -19,6 +19,12 @@ const {
loadLanguage, loadLanguage,
saveCloseLauncherOnStart, saveCloseLauncherOnStart,
loadCloseLauncherOnStart, loadCloseLauncherOnStart,
// Hardware Acceleration
saveLauncherHardwareAcceleration,
loadLauncherHardwareAcceleration,
saveModsToConfig, saveModsToConfig,
loadModsFromConfig, loadModsFromConfig,
getUuidForUser, getUuidForUser,
@@ -125,20 +131,24 @@ module.exports = {
// Discord RPC functions // Discord RPC functions
saveDiscordRPC, saveDiscordRPC,
loadDiscordRPC, loadDiscordRPC,
// Language functions // Language functions
saveLanguage, saveLanguage,
loadLanguage, loadLanguage,
// Close Launcher functions // Close Launcher functions
saveCloseLauncherOnStart, saveCloseLauncherOnStart,
loadCloseLauncherOnStart, loadCloseLauncherOnStart,
// Hardware Acceleration functions
saveLauncherHardwareAcceleration,
loadLauncherHardwareAcceleration,
// GPU Preference functions // GPU Preference functions
saveGpuPreference, saveGpuPreference,
loadGpuPreference, loadGpuPreference,
detectGpu, detectGpu,
// Version functions // Version functions
getLatestClientVersion, getLatestClientVersion,
saveVersionClient, saveVersionClient,

66
main.js
View File

@@ -3,9 +3,20 @@ require('dotenv').config({ path: path.join(__dirname, '.env') });
const { app, BrowserWindow, ipcMain, dialog, shell } = require('electron'); const { app, BrowserWindow, ipcMain, dialog, shell } = require('electron');
const { autoUpdater } = require('electron-updater'); const { autoUpdater } = require('electron-updater');
const fs = require('fs'); 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 { launchGame, launchGameWithVersionCheck, installGame, saveUsername, loadUsername, saveChatUsername, loadChatUsername, saveChatColor, loadChatColor, saveJavaPath, loadJavaPath, saveInstallPath, loadInstallPath, saveDiscordRPC, loadDiscordRPC, saveLanguage, loadLanguage, saveCloseLauncherOnStart, loadCloseLauncherOnStart, saveLauncherHardwareAcceleration, loadLauncherHardwareAcceleration, isGameInstalled, uninstallGame, repairGame, getHytaleNews, handleFirstLaunchCheck, proposeGameUpdate, markAsLaunched } = require('./backend/launcher');
const { retryPWRDownload } = require('./backend/managers/gameManager'); const { retryPWRDownload } = require('./backend/managers/gameManager');
// Handle Hardware Acceleration
try {
const hwEnabled = loadLauncherHardwareAcceleration();
if (!hwEnabled) {
console.log('Hardware acceleration disabled by user setting');
app.disableHardwareAcceleration();
}
} catch (error) {
console.error('Failed to load hardware acceleration setting:', error);
}
const logger = require('./backend/logger'); const logger = require('./backend/logger');
const profileManager = require('./backend/managers/profileManager'); const profileManager = require('./backend/managers/profileManager');
@@ -97,7 +108,7 @@ function toggleDiscordRPC(enabled) {
console.log('Discord RPC disconnected successfully'); console.log('Discord RPC disconnected successfully');
} catch (error) { } catch (error) {
console.error('Error disconnecting Discord RPC:', error.message); console.error('Error disconnecting Discord RPC:', error.message);
discordRPC = null; discordRPC = null;
} }
} }
} }
@@ -165,11 +176,11 @@ function createWindow() {
// Configure and initialize electron-updater // Configure and initialize electron-updater
autoUpdater.autoDownload = false; autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = true; autoUpdater.autoInstallOnAppQuit = true;
autoUpdater.on('checking-for-update', () => { autoUpdater.on('checking-for-update', () => {
console.log('Checking for launcher updates...'); console.log('Checking for launcher updates...');
}); });
autoUpdater.on('update-available', (info) => { autoUpdater.on('update-available', (info) => {
console.log('Update available:', info.version); console.log('Update available:', info.version);
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
@@ -181,15 +192,15 @@ function createWindow() {
}); });
} }
}); });
autoUpdater.on('update-not-available', (info) => { autoUpdater.on('update-not-available', (info) => {
console.log('Launcher is up to date:', info.version); console.log('Launcher is up to date:', info.version);
}); });
autoUpdater.on('error', (err) => { autoUpdater.on('error', (err) => {
console.error('Error in auto-updater:', err); console.error('Error in auto-updater:', err);
}); });
autoUpdater.on('download-progress', (progressObj) => { autoUpdater.on('download-progress', (progressObj) => {
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('update-download-progress', { mainWindow.webContents.send('update-download-progress', {
@@ -200,7 +211,7 @@ function createWindow() {
}); });
} }
}); });
autoUpdater.on('update-downloaded', (info) => { autoUpdater.on('update-downloaded', (info) => {
console.log('Update downloaded:', info.version); console.log('Update downloaded:', info.version);
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
@@ -209,7 +220,7 @@ function createWindow() {
}); });
} }
}); });
// Check for updates after 3 seconds // Check for updates after 3 seconds
setTimeout(() => { setTimeout(() => {
autoUpdater.checkForUpdates().catch(err => { autoUpdater.checkForUpdates().catch(err => {
@@ -240,9 +251,9 @@ function createWindow() {
// Close application shortcuts // Close application shortcuts
const isMac = process.platform === 'darwin'; const isMac = process.platform === 'darwin';
const quitShortcut = (isMac && input.meta && input.key.toLowerCase() === 'q') || const quitShortcut = (isMac && input.meta && input.key.toLowerCase() === 'q') ||
(!isMac && input.control && input.key.toLowerCase() === 'q') || (!isMac && input.control && input.key.toLowerCase() === 'q') ||
(!isMac && input.alt && input.key === 'F4'); (!isMac && input.alt && input.key === 'F4');
if (quitShortcut) { if (quitShortcut) {
app.quit(); app.quit();
@@ -453,7 +464,7 @@ ipcMain.handle('install-game', async (event, playerName, javaPath, installPath,
console.log(` - installPath: ${installPath}`); console.log(` - installPath: ${installPath}`);
console.log(` - branch: ${branch}`); console.log(` - branch: ${branch}`);
console.log(`[IPC] branch type: ${typeof branch}, value: ${JSON.stringify(branch)}`); console.log(`[IPC] branch type: ${typeof branch}, value: ${JSON.stringify(branch)}`);
// Signal installation start // Signal installation start
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('installation-start'); mainWindow.webContents.send('installation-start');
@@ -632,6 +643,15 @@ ipcMain.handle('load-close-launcher', () => {
return loadCloseLauncherOnStart(); return loadCloseLauncherOnStart();
}); });
ipcMain.handle('save-launcher-hw-accel', (event, enabled) => {
saveLauncherHardwareAcceleration(enabled);
return { success: true };
});
ipcMain.handle('load-launcher-hw-accel', () => {
return loadLauncherHardwareAcceleration();
});
ipcMain.handle('select-install-path', async () => { ipcMain.handle('select-install-path', async () => {
const result = await dialog.showOpenDialog(mainWindow, { const result = await dialog.showOpenDialog(mainWindow, {
@@ -749,14 +769,14 @@ ipcMain.handle('retry-download', async (event, retryData) => {
if (retryData && retryData.isJREError) { if (retryData && retryData.isJREError) {
console.log(`[IPC] Retrying JRE download: jreUrl=${retryData.jreUrl}, fileName=${retryData.fileName}`); console.log(`[IPC] Retrying JRE download: jreUrl=${retryData.jreUrl}, fileName=${retryData.fileName}`);
console.log('[IPC] Full JRE retry data:', JSON.stringify(retryData, null, 2)); console.log('[IPC] Full JRE retry data:', JSON.stringify(retryData, null, 2));
const { retryJREDownload } = require('./backend/managers/javaManager'); const { retryJREDownload } = require('./backend/managers/javaManager');
const jreCacheFile = path.join(retryData.cacheDir, retryData.fileName); const jreCacheFile = path.join(retryData.cacheDir, retryData.fileName);
await retryJREDownload(retryData.jreUrl, jreCacheFile, progressCallback); await retryJREDownload(retryData.jreUrl, jreCacheFile, progressCallback);
return { success: true }; return { success: true };
} }
// Handle PWR download retries (default) // Handle PWR download retries (default)
if (!retryData || !retryData.branch || !retryData.fileName) { if (!retryData || !retryData.branch || !retryData.fileName) {
console.log('[IPC] Invalid retry data, using PWR defaults'); console.log('[IPC] Invalid retry data, using PWR defaults');
@@ -765,23 +785,23 @@ ipcMain.handle('retry-download', async (event, retryData) => {
fileName: '4.pwr' fileName: '4.pwr'
}; };
} }
// 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}`);
console.log('[IPC] Full PWR retry data:', JSON.stringify(retryData, null, 2)); console.log('[IPC] Full PWR retry data:', JSON.stringify(retryData, null, 2));
// Perform retry with enhanced context // Perform 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 isJreError = retryData?.isJREError; const isJreError = retryData?.isJREError;
@@ -799,7 +819,7 @@ ipcMain.handle('retry-download', async (event, retryData) => {
fileName: retryData?.fileName || '4.pwr', fileName: retryData?.fileName || '4.pwr',
cacheDir: retryData?.cacheDir cacheDir: retryData?.cacheDir
}; };
const data = { const data = {
message: errorMessage, message: errorMessage,
error: true, error: true,
@@ -809,7 +829,7 @@ ipcMain.handle('retry-download', async (event, retryData) => {
}; };
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);

View File

@@ -23,6 +23,11 @@ contextBridge.exposeInMainWorld('electronAPI', {
loadLanguage: () => ipcRenderer.invoke('load-language'), loadLanguage: () => ipcRenderer.invoke('load-language'),
saveCloseLauncher: (enabled) => ipcRenderer.invoke('save-close-launcher', enabled), saveCloseLauncher: (enabled) => ipcRenderer.invoke('save-close-launcher', enabled),
loadCloseLauncher: () => ipcRenderer.invoke('load-close-launcher'), loadCloseLauncher: () => ipcRenderer.invoke('load-close-launcher'),
// Harwadre Acceleration
saveLauncherHardwareAcceleration: (enabled) => ipcRenderer.invoke('save-launcher-hw-accel', enabled),
loadLauncherHardwareAcceleration: () => ipcRenderer.invoke('load-launcher-hw-accel'),
selectInstallPath: () => ipcRenderer.invoke('select-install-path'), selectInstallPath: () => ipcRenderer.invoke('select-install-path'),
browseJavaPath: () => ipcRenderer.invoke('browse-java-path'), browseJavaPath: () => ipcRenderer.invoke('browse-java-path'),
isGameInstalled: () => ipcRenderer.invoke('is-game-installed'), isGameInstalled: () => ipcRenderer.invoke('is-game-installed'),