mirror of
https://git.sanhost.net/sanasol/hytale-f2p
synced 2026-02-26 06:51:47 -03:00
pre-release & release game version [to check]
This commit is contained in:
@@ -123,6 +123,26 @@
|
|||||||
value="Player" />
|
value="Player" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" data-i18n="install.gameBranch">Game Version</label>
|
||||||
|
<div class="radio-group">
|
||||||
|
<label class="radio-label">
|
||||||
|
<input type="radio" name="installBranch" value="release" class="custom-radio" checked>
|
||||||
|
<span class="radio-text">
|
||||||
|
<i class="fas fa-check-circle mr-2"></i>
|
||||||
|
<span data-i18n="install.releaseVersion">Release (Stable)</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="radio-label">
|
||||||
|
<input type="radio" name="installBranch" value="pre-release" class="custom-radio">
|
||||||
|
<span class="radio-text">
|
||||||
|
<i class="fas fa-flask mr-2"></i>
|
||||||
|
<span data-i18n="install.preReleaseVersion">Pre-Release (Experimental)</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="checkbox-group">
|
<label class="checkbox-group">
|
||||||
<input type="checkbox" id="installCustomCheck" class="custom-checkbox">
|
<input type="checkbox" id="installCustomCheck" class="custom-checkbox">
|
||||||
@@ -337,9 +357,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<div class="segmented-control">
|
||||||
|
<input type="radio" id="branch-release" name="gameBranch" value="release" checked>
|
||||||
|
<label for="branch-release" 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>
|
||||||
|
<p class="settings-hint">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
<span data-i18n="settings.branchHint">Switch between stable release and experimental pre-release versions</span>
|
||||||
|
</p>
|
||||||
|
<p class="settings-hint" style="color: #f39c12;">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
<span data-i18n="settings.branchWarning">Changing branch will download and install a different game version</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
|||||||
@@ -60,6 +60,18 @@ export async function installGame() {
|
|||||||
const playerName = (installPlayerName ? installPlayerName.value.trim() : '') || 'Player';
|
const playerName = (installPlayerName ? installPlayerName.value.trim() : '') || 'Player';
|
||||||
const installPath = installPathInput ? installPathInput.value.trim() : '';
|
const installPath = installPathInput ? installPathInput.value.trim() : '';
|
||||||
|
|
||||||
|
// Récupérer la branche sélectionnée
|
||||||
|
const selectedBranchRadio = document.querySelector('input[name="installBranch"]:checked');
|
||||||
|
const selectedBranch = selectedBranchRadio ? selectedBranchRadio.value : 'release';
|
||||||
|
|
||||||
|
console.log(`[Install] Installing game with branch: ${selectedBranch}`);
|
||||||
|
|
||||||
|
// Sauvegarder la branche sélectionnée dans le config
|
||||||
|
if (window.electronAPI && window.electronAPI.saveVersionBranch) {
|
||||||
|
await window.electronAPI.saveVersionBranch(selectedBranch);
|
||||||
|
console.log(`[Install] Branch saved to config: ${selectedBranch}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (window.LauncherUI) window.LauncherUI.showProgress();
|
if (window.LauncherUI) window.LauncherUI.showProgress();
|
||||||
isDownloading = true;
|
isDownloading = true;
|
||||||
if (installBtn) {
|
if (installBtn) {
|
||||||
@@ -69,7 +81,7 @@ export async function installGame() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (window.electronAPI && window.electronAPI.installGame) {
|
if (window.electronAPI && window.electronAPI.installGame) {
|
||||||
const result = await window.electronAPI.installGame(playerName, '', installPath);
|
const result = await window.electronAPI.installGame(playerName, '', installPath, selectedBranch);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const successMsg = window.i18n ? window.i18n.t('progress.installationComplete') : 'Installation completed successfully!';
|
const successMsg = window.i18n ? window.i18n.t('progress.installationComplete') : 'Installation completed successfully!';
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ let customJavaCheck;
|
|||||||
let customJavaOptions;
|
let customJavaOptions;
|
||||||
let customJavaPath;
|
let customJavaPath;
|
||||||
let browseJavaBtn;
|
let browseJavaBtn;
|
||||||
let settingsPlayerName;
|
let settingsPlayerName;
|
||||||
let discordRPCCheck;
|
let discordRPCCheck;
|
||||||
let closeLauncherCheck;
|
let closeLauncherCheck;
|
||||||
let gpuPreferenceRadios;
|
let gpuPreferenceRadios;
|
||||||
|
let gameBranchRadios;
|
||||||
|
|
||||||
|
|
||||||
// UUID Management elements
|
// UUID Management elements
|
||||||
let currentUuidDisplay;
|
let currentUuidDisplay;
|
||||||
@@ -161,11 +162,12 @@ function setupSettingsElements() {
|
|||||||
customJavaOptions = document.getElementById('customJavaOptions');
|
customJavaOptions = document.getElementById('customJavaOptions');
|
||||||
customJavaPath = document.getElementById('customJavaPath');
|
customJavaPath = document.getElementById('customJavaPath');
|
||||||
browseJavaBtn = document.getElementById('browseJavaBtn');
|
browseJavaBtn = document.getElementById('browseJavaBtn');
|
||||||
settingsPlayerName = document.getElementById('settingsPlayerName');
|
settingsPlayerName = document.getElementById('settingsPlayerName');
|
||||||
discordRPCCheck = document.getElementById('discordRPCCheck');
|
discordRPCCheck = document.getElementById('discordRPCCheck');
|
||||||
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"]');
|
||||||
|
|
||||||
|
|
||||||
// UUID Management elements
|
// UUID Management elements
|
||||||
currentUuidDisplay = document.getElementById('currentUuid');
|
currentUuidDisplay = document.getElementById('currentUuid');
|
||||||
@@ -194,14 +196,14 @@ function setupSettingsElements() {
|
|||||||
settingsPlayerName.addEventListener('change', savePlayerName);
|
settingsPlayerName.addEventListener('change', savePlayerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discordRPCCheck) {
|
if (discordRPCCheck) {
|
||||||
discordRPCCheck.addEventListener('change', saveDiscordRPC);
|
discordRPCCheck.addEventListener('change', saveDiscordRPC);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closeLauncherCheck) {
|
if (closeLauncherCheck) {
|
||||||
closeLauncherCheck.addEventListener('change', saveCloseLauncher);
|
closeLauncherCheck.addEventListener('change', saveCloseLauncher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// UUID event listeners
|
// UUID event listeners
|
||||||
if (copyUuidBtn) {
|
if (copyUuidBtn) {
|
||||||
@@ -252,6 +254,12 @@ function setupSettingsElements() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gameBranchRadios) {
|
||||||
|
gameBranchRadios.forEach(radio => {
|
||||||
|
radio.addEventListener('change', handleBranchChange);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleCustomJava() {
|
function toggleCustomJava() {
|
||||||
@@ -344,43 +352,43 @@ async function saveDiscordRPC() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadDiscordRPC() {
|
async function loadDiscordRPC() {
|
||||||
try {
|
try {
|
||||||
if (window.electronAPI && window.electronAPI.loadDiscordRPC) {
|
if (window.electronAPI && window.electronAPI.loadDiscordRPC) {
|
||||||
const enabled = await window.electronAPI.loadDiscordRPC();
|
const enabled = await window.electronAPI.loadDiscordRPC();
|
||||||
if (discordRPCCheck) {
|
if (discordRPCCheck) {
|
||||||
discordRPCCheck.checked = enabled;
|
discordRPCCheck.checked = enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading Discord RPC setting:', error);
|
console.error('Error loading Discord RPC setting:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveCloseLauncher() {
|
async function saveCloseLauncher() {
|
||||||
try {
|
try {
|
||||||
if (window.electronAPI && window.electronAPI.saveCloseLauncher && closeLauncherCheck) {
|
if (window.electronAPI && window.electronAPI.saveCloseLauncher && closeLauncherCheck) {
|
||||||
const enabled = closeLauncherCheck.checked;
|
const enabled = closeLauncherCheck.checked;
|
||||||
await window.electronAPI.saveCloseLauncher(enabled);
|
await window.electronAPI.saveCloseLauncher(enabled);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error saving close launcher setting:', error);
|
console.error('Error saving close launcher setting:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadCloseLauncher() {
|
async function loadCloseLauncher() {
|
||||||
try {
|
try {
|
||||||
if (window.electronAPI && window.electronAPI.loadCloseLauncher) {
|
if (window.electronAPI && window.electronAPI.loadCloseLauncher) {
|
||||||
const enabled = await window.electronAPI.loadCloseLauncher();
|
const enabled = await window.electronAPI.loadCloseLauncher();
|
||||||
if (closeLauncherCheck) {
|
if (closeLauncherCheck) {
|
||||||
closeLauncherCheck.checked = enabled;
|
closeLauncherCheck.checked = enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading close launcher setting:', error);
|
console.error('Error loading close launcher setting:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function savePlayerName() {
|
async function savePlayerName() {
|
||||||
try {
|
try {
|
||||||
@@ -491,15 +499,16 @@ async function loadGpuPreference() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadAllSettings() {
|
async function loadAllSettings() {
|
||||||
await loadCustomJavaPath();
|
await loadCustomJavaPath();
|
||||||
await loadPlayerName();
|
await loadPlayerName();
|
||||||
await loadCurrentUuid();
|
await loadCurrentUuid();
|
||||||
await loadDiscordRPC();
|
await loadDiscordRPC();
|
||||||
await loadCloseLauncher();
|
await loadCloseLauncher();
|
||||||
await loadGpuPreference();
|
await loadGpuPreference();
|
||||||
}
|
await loadVersionBranch();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function openGameLocation() {
|
async function openGameLocation() {
|
||||||
try {
|
try {
|
||||||
@@ -891,4 +900,177 @@ function showNotification(message, type = 'info') {
|
|||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}// Append this to settings.js for branch management
|
||||||
|
|
||||||
|
// === Game Branch Management ===
|
||||||
|
async function handleBranchChange(event) {
|
||||||
|
const newBranch = event.target.value;
|
||||||
|
const currentBranch = await loadVersionBranch();
|
||||||
|
|
||||||
|
if (newBranch === currentBranch) {
|
||||||
|
return; // No change
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm branch change
|
||||||
|
const branchName = window.i18n ?
|
||||||
|
window.i18n.t(`settings.branch${newBranch === 'pre-release' ? 'PreRelease' : 'Release'}`) :
|
||||||
|
newBranch;
|
||||||
|
|
||||||
|
const message = window.i18n ?
|
||||||
|
window.i18n.t('settings.branchWarning') :
|
||||||
|
'Changing branch will download and install a different game version';
|
||||||
|
|
||||||
|
showCustomConfirm(
|
||||||
|
message,
|
||||||
|
window.i18n ? window.i18n.t('settings.gameBranch') : 'Game Branch',
|
||||||
|
async () => {
|
||||||
|
await switchBranch(newBranch);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// Cancel: revert radio selection
|
||||||
|
loadVersionBranch().then(branch => {
|
||||||
|
const radioToCheck = document.querySelector(`input[name="gameBranch"][value="${branch}"]`);
|
||||||
|
if (radioToCheck) {
|
||||||
|
radioToCheck.checked = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function switchBranch(newBranch) {
|
||||||
|
try {
|
||||||
|
const switchingMsg = window.i18n ?
|
||||||
|
window.i18n.t('settings.branchSwitching').replace('{branch}', newBranch) :
|
||||||
|
`Switching to ${newBranch}...`;
|
||||||
|
|
||||||
|
showNotification(switchingMsg, 'info');
|
||||||
|
|
||||||
|
// Lock play button
|
||||||
|
const playButton = document.getElementById('playButton');
|
||||||
|
if (playButton) {
|
||||||
|
playButton.disabled = true;
|
||||||
|
playButton.classList.add('disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save new branch
|
||||||
|
await window.electronAPI.saveVersionBranch(newBranch);
|
||||||
|
|
||||||
|
const switchedMsg = window.i18n ?
|
||||||
|
window.i18n.t('settings.branchSwitched').replace('{branch}', newBranch) :
|
||||||
|
`Switched to ${newBranch} successfully!`;
|
||||||
|
|
||||||
|
showNotification(switchedMsg, 'success');
|
||||||
|
|
||||||
|
// Suggest reinstalling
|
||||||
|
setTimeout(() => {
|
||||||
|
const branchLabel = newBranch === 'release' ?
|
||||||
|
(window.i18n ? window.i18n.t('install.releaseVersion') : 'Release') :
|
||||||
|
(window.i18n ? window.i18n.t('install.preReleaseVersion') : 'Pre-Release');
|
||||||
|
|
||||||
|
const confirmMsg = window.i18n ?
|
||||||
|
window.i18n.t('settings.branchInstallConfirm').replace('{branch}', branchLabel) :
|
||||||
|
`The game will be installed for the ${branchLabel} branch. Continue?`;
|
||||||
|
|
||||||
|
showCustomConfirm(
|
||||||
|
confirmMsg,
|
||||||
|
window.i18n ? window.i18n.t('settings.installRequired') : 'Installation Required',
|
||||||
|
async () => {
|
||||||
|
// Show progress and trigger game installation
|
||||||
|
if (window.LauncherUI) {
|
||||||
|
window.LauncherUI.showProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const playerName = await window.electronAPI.loadUsername();
|
||||||
|
const result = await window.electronAPI.installGame(playerName || 'Player', '', '', newBranch);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
const successMsg = window.i18n ?
|
||||||
|
window.i18n.t('progress.installationComplete') :
|
||||||
|
'Installation completed successfully!';
|
||||||
|
|
||||||
|
showNotification(successMsg, 'success');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (window.LauncherUI) {
|
||||||
|
window.LauncherUI.hideProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock play button
|
||||||
|
const playButton = document.getElementById('playButton');
|
||||||
|
if (playButton) {
|
||||||
|
playButton.disabled = false;
|
||||||
|
playButton.classList.remove('disabled');
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
throw new Error(result.error || 'Installation failed');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Installation error:', error);
|
||||||
|
const errorMsg = window.i18n ?
|
||||||
|
window.i18n.t('progress.installationFailed').replace('{error}', error.message) :
|
||||||
|
`Installation failed: ${error.message}`;
|
||||||
|
|
||||||
|
showNotification(errorMsg, 'error');
|
||||||
|
|
||||||
|
if (window.LauncherUI) {
|
||||||
|
window.LauncherUI.hideProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock play button
|
||||||
|
const playButton = document.getElementById('playButton');
|
||||||
|
if (playButton) {
|
||||||
|
playButton.disabled = false;
|
||||||
|
playButton.classList.remove('disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// Cancel - unlock play button
|
||||||
|
const playButton = document.getElementById('playButton');
|
||||||
|
if (playButton) {
|
||||||
|
playButton.disabled = false;
|
||||||
|
playButton.classList.remove('disabled');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
window.i18n ? window.i18n.t('common.install') : 'Install',
|
||||||
|
window.i18n ? window.i18n.t('common.cancel') : 'Cancel'
|
||||||
|
);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error switching branch:', error);
|
||||||
|
showNotification(`Failed to switch branch: ${error.message}`, 'error');
|
||||||
|
|
||||||
|
// Revert radio selection
|
||||||
|
loadVersionBranch().then(branch => {
|
||||||
|
const radioToCheck = document.querySelector(`input[name="gameBranch"][value="${branch}"]`);
|
||||||
|
if (radioToCheck) {
|
||||||
|
radioToCheck.checked = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadVersionBranch() {
|
||||||
|
try {
|
||||||
|
if (window.electronAPI && window.electronAPI.loadVersionBranch) {
|
||||||
|
const branch = await window.electronAPI.loadVersionBranch();
|
||||||
|
|
||||||
|
// Update radio buttons
|
||||||
|
if (gameBranchRadios) {
|
||||||
|
gameBranchRadios.forEach(radio => {
|
||||||
|
radio.checked = radio.value === branch;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return branch;
|
||||||
|
}
|
||||||
|
return 'release'; // Default
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading version branch:', error);
|
||||||
|
return 'release';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
45
GUI/js/ui.js
45
GUI/js/ui.js
@@ -501,6 +501,7 @@ function setupUI() {
|
|||||||
setupAnimations();
|
setupAnimations();
|
||||||
setupFirstLaunchHandlers();
|
setupFirstLaunchHandlers();
|
||||||
loadLauncherVersion();
|
loadLauncherVersion();
|
||||||
|
checkGameInstallation();
|
||||||
|
|
||||||
document.body.focus();
|
document.body.focus();
|
||||||
}
|
}
|
||||||
@@ -520,6 +521,50 @@ async function loadLauncherVersion() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check game installation status on startup
|
||||||
|
async function checkGameInstallation() {
|
||||||
|
try {
|
||||||
|
console.log('Checking game installation status...');
|
||||||
|
|
||||||
|
// Check if game is installed
|
||||||
|
const isInstalled = await window.electronAPI.isGameInstalled();
|
||||||
|
|
||||||
|
// Load version_client from config
|
||||||
|
let versionClient = null;
|
||||||
|
if (window.electronAPI.loadVersionClient) {
|
||||||
|
versionClient = await window.electronAPI.loadVersionClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Game installed: ${isInstalled}, version_client: ${versionClient}`);
|
||||||
|
|
||||||
|
// If version_client is null and game is not installed, trigger installation
|
||||||
|
if (versionClient === null && !isInstalled) {
|
||||||
|
console.log('Game not installed and version_client is null, showing install page...');
|
||||||
|
|
||||||
|
// Show installation page
|
||||||
|
const installPage = document.getElementById('install-page');
|
||||||
|
const launcher = document.getElementById('launcher-container');
|
||||||
|
const sidebar = document.querySelector('.sidebar');
|
||||||
|
|
||||||
|
if (installPage) {
|
||||||
|
installPage.style.display = 'block';
|
||||||
|
if (launcher) launcher.style.display = 'none';
|
||||||
|
if (sidebar) sidebar.style.pointerEvents = 'none';
|
||||||
|
|
||||||
|
// Unlock play button since we're in install mode
|
||||||
|
lockPlayButton(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Game is installed or version is set, unlock play button
|
||||||
|
lockPlayButton(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking game installation:', error);
|
||||||
|
// Unlock on error to prevent permanent lock
|
||||||
|
lockPlayButton(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.LauncherUI = {
|
window.LauncherUI = {
|
||||||
showPage,
|
showPage,
|
||||||
setActiveNav,
|
setActiveNav,
|
||||||
|
|||||||
@@ -15,6 +15,9 @@
|
|||||||
"title": "FREE TO PLAY LAUNCHER",
|
"title": "FREE TO PLAY LAUNCHER",
|
||||||
"playerName": "Player Name",
|
"playerName": "Player Name",
|
||||||
"playerNamePlaceholder": "Enter your name",
|
"playerNamePlaceholder": "Enter your name",
|
||||||
|
"gameBranch": "Game Version",
|
||||||
|
"releaseVersion": "Release (Stable)",
|
||||||
|
"preReleaseVersion": "Pre-Release (Experimental)",
|
||||||
"customInstallation": "Custom Installation",
|
"customInstallation": "Custom Installation",
|
||||||
"installationFolder": "Installation Folder",
|
"installationFolder": "Installation Folder",
|
||||||
"pathPlaceholder": "Default location",
|
"pathPlaceholder": "Default location",
|
||||||
@@ -125,7 +128,16 @@
|
|||||||
"logsLoading": "Loading logs...",
|
"logsLoading": "Loading logs...",
|
||||||
"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",
|
||||||
|
"gameBranch": "Game Branch",
|
||||||
|
"branchRelease": "Release",
|
||||||
|
"branchPreRelease": "Pre-Release",
|
||||||
|
"branchHint": "Switch between stable release and experimental pre-release versions",
|
||||||
|
"branchWarning": "Changing branch will download and install a different game version",
|
||||||
|
"branchSwitching": "Switching to {branch}...",
|
||||||
|
"branchSwitched": "Switched to {branch} successfully!",
|
||||||
|
"installRequired": "Installation Required",
|
||||||
|
"branchInstallConfirm": "The game will be installed for the {branch} branch. Continue?"
|
||||||
},
|
},
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"modalTitle": "UUID Management",
|
"modalTitle": "UUID Management",
|
||||||
@@ -157,7 +169,8 @@
|
|||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"apply": "Apply"
|
"apply": "Apply",
|
||||||
|
"install": "Install"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"gameDataNotFound": "Error: Game data not found",
|
"gameDataNotFound": "Error: Game data not found",
|
||||||
|
|||||||
@@ -14,8 +14,9 @@
|
|||||||
"install": {
|
"install": {
|
||||||
"title": "LAUNCHER GRATUITO",
|
"title": "LAUNCHER GRATUITO",
|
||||||
"playerName": "Nombre del Jugador",
|
"playerName": "Nombre del Jugador",
|
||||||
"playerNamePlaceholder": "Ingresa tu nombre",
|
"playerNamePlaceholder": "Ingresa tu nombre", "gameBranch": "Versión del Juego",
|
||||||
"customInstallation": "Instalación Personalizada",
|
"releaseVersion": "Lanzamiento (Estable)",
|
||||||
|
"preReleaseVersion": "Pre-Lanzamiento (Experimental)", "customInstallation": "Instalación Personalizada",
|
||||||
"installationFolder": "Carpeta de Instalación",
|
"installationFolder": "Carpeta de Instalación",
|
||||||
"pathPlaceholder": "Ubicación predeterminada",
|
"pathPlaceholder": "Ubicación predeterminada",
|
||||||
"browse": "Examinar",
|
"browse": "Examinar",
|
||||||
@@ -125,7 +126,16 @@
|
|||||||
"logsLoading": "Cargando registros...",
|
"logsLoading": "Cargando registros...",
|
||||||
"closeLauncher": "Comportamiento del Launcher",
|
"closeLauncher": "Comportamiento del Launcher",
|
||||||
"closeOnStart": "Cerrar Launcher al iniciar el juego",
|
"closeOnStart": "Cerrar Launcher al iniciar el juego",
|
||||||
"closeOnStartDescription": "Cierra automáticamente el launcher después de que Hytale se haya iniciado"
|
"closeOnStartDescription": "Cierra automáticamente el launcher después de que Hytale se haya iniciado",
|
||||||
|
"gameBranch": "Rama del Juego",
|
||||||
|
"branchRelease": "Lanzamiento",
|
||||||
|
"branchPreRelease": "Pre-Lanzamiento",
|
||||||
|
"branchHint": "Cambia entre la versión estable y la versión experimental de pre-lanzamiento",
|
||||||
|
"branchWarning": "Cambiar de rama descargará e instalará una versión diferente del juego",
|
||||||
|
"branchSwitching": "Cambiando a {branch}...",
|
||||||
|
"branchSwitched": "¡Cambiado a {branch} con éxito!",
|
||||||
|
"installRequired": "Instalación Requerida",
|
||||||
|
"branchInstallConfirm": "El juego se instalará para la rama {branch}. ¿Continuar?"
|
||||||
},
|
},
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"modalTitle": "Gestión de UUID",
|
"modalTitle": "Gestión de UUID",
|
||||||
@@ -157,7 +167,8 @@
|
|||||||
"delete": "Eliminar",
|
"delete": "Eliminar",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
"loading": "Cargando...",
|
"loading": "Cargando...",
|
||||||
"apply": "Aplicar"
|
"apply": "Aplicar",
|
||||||
|
"install": "Instalar"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"gameDataNotFound": "Error: No se encontraron datos del juego",
|
"gameDataNotFound": "Error: No se encontraron datos del juego",
|
||||||
|
|||||||
@@ -14,8 +14,9 @@
|
|||||||
"install": {
|
"install": {
|
||||||
"title": "LANÇADOR JOGO GRATUITO",
|
"title": "LANÇADOR JOGO GRATUITO",
|
||||||
"playerName": "Nome do Jogador",
|
"playerName": "Nome do Jogador",
|
||||||
"playerNamePlaceholder": "Digite seu nome",
|
"playerNamePlaceholder": "Digite seu nome", "gameBranch": "Versão do Jogo",
|
||||||
"customInstallation": "Instalação Personalizada",
|
"releaseVersion": "Lançamento (Estável)",
|
||||||
|
"preReleaseVersion": "Pré-Lançamento (Experimental)", "customInstallation": "Instalação Personalizada",
|
||||||
"installationFolder": "Pasta de Instalação",
|
"installationFolder": "Pasta de Instalação",
|
||||||
"pathPlaceholder": "Local padrão",
|
"pathPlaceholder": "Local padrão",
|
||||||
"browse": "Procurar",
|
"browse": "Procurar",
|
||||||
@@ -125,7 +126,16 @@
|
|||||||
"logsLoading": "Carregando registros...",
|
"logsLoading": "Carregando registros...",
|
||||||
"closeLauncher": "Comportamento do Lançador",
|
"closeLauncher": "Comportamento do Lançador",
|
||||||
"closeOnStart": "Fechar Lançador ao iniciar o jogo",
|
"closeOnStart": "Fechar Lançador ao iniciar o jogo",
|
||||||
"closeOnStartDescription": "Fechar automaticamente o lançador após o Hytale ter sido iniciado"
|
"closeOnStartDescription": "Fechar automaticamente o lançador após o Hytale ter sido iniciado",
|
||||||
|
"gameBranch": "Versão do Jogo",
|
||||||
|
"branchRelease": "Lançamento",
|
||||||
|
"branchPreRelease": "Pré-Lançamento",
|
||||||
|
"branchHint": "Alterne entre a versão estável e a versão experimental de pré-lançamento",
|
||||||
|
"branchWarning": "Mudar de versão irá baixar e instalar uma versão diferente do jogo",
|
||||||
|
"branchSwitching": "Mudando para {branch}...",
|
||||||
|
"branchSwitched": "Mudado para {branch} com sucesso!",
|
||||||
|
"installRequired": "Instalação Necessária",
|
||||||
|
"branchInstallConfirm": "O jogo será instalado para o ramo {branch}. Continuar?"
|
||||||
},
|
},
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"modalTitle": "Gerenciamento de UUID",
|
"modalTitle": "Gerenciamento de UUID",
|
||||||
@@ -158,7 +168,8 @@
|
|||||||
"delete": "Excluir",
|
"delete": "Excluir",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
"loading": "Carregando...",
|
"loading": "Carregando...",
|
||||||
"apply": "Aplicar"
|
"apply": "Aplicar",
|
||||||
|
"install": "Instalar"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"gameDataNotFound": "Erro: Dados do jogo não encontrados",
|
"gameDataNotFound": "Erro: Dados do jogo não encontrados",
|
||||||
|
|||||||
@@ -662,6 +662,57 @@ body {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Radio buttons for install page */
|
||||||
|
.radio-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-label:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
border-color: rgba(147, 51, 234, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-label .custom-radio {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-label .custom-radio:checked ~ .radio-text {
|
||||||
|
color: #9333ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-label:has(.custom-radio:checked) {
|
||||||
|
background: rgba(147, 51, 234, 0.15);
|
||||||
|
border-color: #9333ea;
|
||||||
|
box-shadow: 0 0 20px rgba(147, 51, 234, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #d1d5db;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-text i {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.launcher-container {
|
.launcher-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -304,6 +304,30 @@ function loadGpuPreference() {
|
|||||||
return config.gpuPreference || 'auto';
|
return config.gpuPreference || 'auto';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveVersionClient(versionClient) {
|
||||||
|
saveConfig({ version_client: versionClient });
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadVersionClient() {
|
||||||
|
const config = loadConfig();
|
||||||
|
return config.version_client !== undefined ? config.version_client : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveVersionBranch(versionBranch) {
|
||||||
|
const branch = versionBranch || 'release';
|
||||||
|
if (branch !== 'release' && branch !== 'pre-release') {
|
||||||
|
console.warn(`Invalid branch "${branch}", defaulting to "release"`);
|
||||||
|
saveConfig({ version_branch: 'release' });
|
||||||
|
} else {
|
||||||
|
saveConfig({ version_branch: branch });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadVersionBranch() {
|
||||||
|
const config = loadConfig();
|
||||||
|
return config.version_branch || 'release';
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
loadConfig,
|
loadConfig,
|
||||||
saveConfig,
|
saveConfig,
|
||||||
@@ -343,5 +367,10 @@ module.exports = {
|
|||||||
loadGpuPreference,
|
loadGpuPreference,
|
||||||
// Close Launcher export
|
// Close Launcher export
|
||||||
saveCloseLauncherOnStart,
|
saveCloseLauncherOnStart,
|
||||||
loadCloseLauncherOnStart
|
loadCloseLauncherOnStart,
|
||||||
|
// Version Management exports
|
||||||
|
saveVersionClient,
|
||||||
|
loadVersionClient,
|
||||||
|
saveVersionBranch,
|
||||||
|
loadVersionBranch
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,12 @@ const {
|
|||||||
resetCurrentUserUuid,
|
resetCurrentUserUuid,
|
||||||
// GPU Preference
|
// GPU Preference
|
||||||
saveGpuPreference,
|
saveGpuPreference,
|
||||||
loadGpuPreference
|
loadGpuPreference,
|
||||||
|
// Version Management
|
||||||
|
saveVersionClient,
|
||||||
|
loadVersionClient,
|
||||||
|
saveVersionBranch,
|
||||||
|
loadVersionBranch
|
||||||
} = require('./core/config');
|
} = require('./core/config');
|
||||||
|
|
||||||
const { getResolvedAppDir, getModsPath } = require('./core/paths');
|
const { getResolvedAppDir, getModsPath } = require('./core/paths');
|
||||||
@@ -138,6 +143,10 @@ module.exports = {
|
|||||||
// Version functions
|
// Version functions
|
||||||
getInstalledClientVersion,
|
getInstalledClientVersion,
|
||||||
getLatestClientVersion,
|
getLatestClientVersion,
|
||||||
|
saveVersionClient,
|
||||||
|
loadVersionClient,
|
||||||
|
saveVersionBranch,
|
||||||
|
loadVersionBranch,
|
||||||
|
|
||||||
// News functions
|
// News functions
|
||||||
getHytaleNews,
|
getHytaleNews,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const { spawn } = require('child_process');
|
|||||||
const { v4: uuidv4 } = require('uuid');
|
const { v4: uuidv4 } = require('uuid');
|
||||||
const { getResolvedAppDir, findClientPath } = require('../core/paths');
|
const { getResolvedAppDir, findClientPath } = require('../core/paths');
|
||||||
const { setupWaylandEnvironment, setupGpuEnvironment } = require('../utils/platformUtils');
|
const { setupWaylandEnvironment, setupGpuEnvironment } = require('../utils/platformUtils');
|
||||||
const { saveUsername, saveInstallPath, loadJavaPath, getUuidForUser, getAuthServerUrl, getAuthDomain } = require('../core/config');
|
const { saveUsername, saveInstallPath, loadJavaPath, getUuidForUser, getAuthServerUrl, getAuthDomain, loadVersionBranch, loadVersionClient, saveVersionClient } = require('../core/config');
|
||||||
const { resolveJavaPath, getJavaExec, getBundledJavaPath, detectSystemJava, JAVA_EXECUTABLE } = require('./javaManager');
|
const { resolveJavaPath, getJavaExec, getBundledJavaPath, detectSystemJava, JAVA_EXECUTABLE } = require('./javaManager');
|
||||||
const { getInstalledClientVersion, getLatestClientVersion } = require('../services/versionManager');
|
const { getInstalledClientVersion, getLatestClientVersion } = require('../services/versionManager');
|
||||||
const { updateGameFiles } = require('./gameManager');
|
const { updateGameFiles } = require('./gameManager');
|
||||||
@@ -101,10 +101,11 @@ function generateLocalTokens(uuid, name) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function launchGame(playerName = 'Player', progressCallback, javaPathOverride, installPathOverride, gpuPreference = 'auto') {
|
async function launchGame(playerName = 'Player', progressCallback, javaPathOverride, installPathOverride, gpuPreference = 'auto', branchOverride = null) {
|
||||||
|
const branch = branchOverride || loadVersionBranch();
|
||||||
const customAppDir = getResolvedAppDir(installPathOverride);
|
const customAppDir = getResolvedAppDir(installPathOverride);
|
||||||
const customGameDir = path.join(customAppDir, 'release', 'package', 'game', 'latest');
|
const customGameDir = path.join(customAppDir, branch, 'package', 'game', 'latest');
|
||||||
const customJreDir = path.join(customAppDir, 'release', 'package', 'jre', 'latest');
|
const customJreDir = path.join(customAppDir, branch, 'package', 'jre', 'latest');
|
||||||
const userDataDir = path.join(customGameDir, 'Client', 'UserData');
|
const userDataDir = path.join(customGameDir, 'Client', 'UserData');
|
||||||
|
|
||||||
const gameLatest = customGameDir;
|
const gameLatest = customGameDir;
|
||||||
@@ -151,13 +152,14 @@ async function launchGame(playerName = 'Player', progressCallback, javaPathOverr
|
|||||||
const { identityToken, sessionToken } = await fetchAuthTokens(uuid, playerName);
|
const { identityToken, sessionToken } = await fetchAuthTokens(uuid, playerName);
|
||||||
|
|
||||||
// Patch client and server binaries to use custom auth server (BEFORE signing on macOS)
|
// Patch client and server binaries to use custom auth server (BEFORE signing on macOS)
|
||||||
|
// FORCE patch on every launch to ensure consistency
|
||||||
const authDomain = getAuthDomain();
|
const authDomain = getAuthDomain();
|
||||||
if (clientPatcher) {
|
if (clientPatcher) {
|
||||||
try {
|
try {
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback('Patching game for custom server...', null, null, null, null);
|
progressCallback('Patching game for custom server...', null, null, null, null);
|
||||||
}
|
}
|
||||||
console.log(`Patching game binaries for ${authDomain}...`);
|
console.log(`Force patching game binaries for ${authDomain}...`);
|
||||||
|
|
||||||
const patchResult = await clientPatcher.ensureClientPatched(gameLatest, (msg, percent) => {
|
const patchResult = await clientPatcher.ensureClientPatched(gameLatest, (msg, percent) => {
|
||||||
console.log(`[Patcher] ${msg}`);
|
console.log(`[Patcher] ${msg}`);
|
||||||
@@ -167,16 +169,12 @@ async function launchGame(playerName = 'Player', progressCallback, javaPathOverr
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (patchResult.success) {
|
if (patchResult.success) {
|
||||||
if (patchResult.alreadyPatched) {
|
console.log(`Game patched successfully (${patchResult.patchCount} total occurrences)`);
|
||||||
console.log(`Game already patched for ${authDomain}`);
|
if (patchResult.client) {
|
||||||
} else {
|
console.log(` Client: ${patchResult.client.patchCount || 0} occurrences`);
|
||||||
console.log(`Game patched successfully (${patchResult.patchCount} total occurrences)`);
|
}
|
||||||
if (patchResult.client) {
|
if (patchResult.server) {
|
||||||
console.log(` Client: ${patchResult.client.patchCount || 0} occurrences`);
|
console.log(` Server: ${patchResult.server.patchCount || 0} occurrences`);
|
||||||
}
|
|
||||||
if (patchResult.server) {
|
|
||||||
console.log(` Server: ${patchResult.server.patchCount || 0} occurrences`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn('Game patching failed:', patchResult.error);
|
console.warn('Game patching failed:', patchResult.error);
|
||||||
@@ -355,23 +353,23 @@ exec "$REAL_JAVA" "\${ARGS[@]}"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function launchGameWithVersionCheck(playerName = 'Player', progressCallback, javaPathOverride, installPathOverride, gpuPreference = 'auto') {
|
async function launchGameWithVersionCheck(playerName = 'Player', progressCallback, javaPathOverride, installPathOverride, gpuPreference = 'auto', branchOverride = null) {
|
||||||
try {
|
try {
|
||||||
|
const branch = branchOverride || loadVersionBranch();
|
||||||
|
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback('Checking for updates...', 0, null, null, null);
|
progressCallback('Checking for updates...', 0, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const [installedVersion, latestVersion] = await Promise.all([
|
const installedVersion = loadVersionClient();
|
||||||
getInstalledClientVersion(),
|
const latestVersion = await getLatestClientVersion(branch);
|
||||||
getLatestClientVersion()
|
|
||||||
]);
|
|
||||||
|
|
||||||
console.log(`Installed version: ${installedVersion}, Latest version: ${latestVersion}`);
|
console.log(`Installed version: ${installedVersion}, Latest version: ${latestVersion} (branch: ${branch})`);
|
||||||
|
|
||||||
let needsUpdate = false;
|
let needsUpdate = false;
|
||||||
if (installedVersion && latestVersion && installedVersion !== latestVersion) {
|
if (!installedVersion || installedVersion !== latestVersion) {
|
||||||
needsUpdate = true;
|
needsUpdate = true;
|
||||||
console.log('Version mismatch detected, update required');
|
console.log('Version mismatch or not installed, update required');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsUpdate) {
|
if (needsUpdate) {
|
||||||
@@ -380,13 +378,13 @@ async function launchGameWithVersionCheck(playerName = 'Player', progressCallbac
|
|||||||
}
|
}
|
||||||
|
|
||||||
const customAppDir = getResolvedAppDir(installPathOverride);
|
const customAppDir = getResolvedAppDir(installPathOverride);
|
||||||
const customGameDir = path.join(customAppDir, 'release', 'package', 'game', 'latest');
|
const customGameDir = path.join(customAppDir, branch, 'package', 'game', 'latest');
|
||||||
const customToolsDir = path.join(customAppDir, 'butler');
|
const customToolsDir = path.join(customAppDir, 'butler');
|
||||||
const customCacheDir = path.join(customAppDir, 'cache');
|
const customCacheDir = path.join(customAppDir, 'cache');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateGameFiles(latestVersion, progressCallback, customGameDir, customToolsDir, customCacheDir);
|
await updateGameFiles(latestVersion, progressCallback, customGameDir, customToolsDir, customCacheDir, branch);
|
||||||
console.log('Game updated successfully, waiting before launch...');
|
console.log('Game updated successfully, patching will be forced on launch...');
|
||||||
|
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback('Preparing game launch...', 90, null, null, null);
|
progressCallback('Preparing game launch...', 90, null, null, null);
|
||||||
@@ -406,7 +404,7 @@ async function launchGameWithVersionCheck(playerName = 'Player', progressCallbac
|
|||||||
progressCallback('Launching game...', 80, null, null, null);
|
progressCallback('Launching game...', 80, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await launchGame(playerName, progressCallback, javaPathOverride, installPathOverride, gpuPreference);
|
return await launchGame(playerName, progressCallback, javaPathOverride, installPathOverride, gpuPreference, branch);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in version check and launch:', error);
|
console.error('Error in version check and launch:', error);
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ const { downloadFile } = require('../utils/fileManager');
|
|||||||
const { getLatestClientVersion, getInstalledClientVersion } = require('../services/versionManager');
|
const { getLatestClientVersion, getInstalledClientVersion } = require('../services/versionManager');
|
||||||
const { installButler } = require('./butlerManager');
|
const { installButler } = require('./butlerManager');
|
||||||
const { downloadAndReplaceHomePageUI, downloadAndReplaceLogo } = require('./uiFileManager');
|
const { downloadAndReplaceHomePageUI, downloadAndReplaceLogo } = require('./uiFileManager');
|
||||||
const { saveUsername, saveInstallPath, loadJavaPath, CONFIG_FILE, loadConfig } = require('../core/config');
|
const { saveUsername, saveInstallPath, loadJavaPath, CONFIG_FILE, loadConfig, loadVersionBranch, saveVersionClient, loadVersionClient } = require('../core/config');
|
||||||
const { resolveJavaPath, detectSystemJava, downloadJRE, getJavaExec, getBundledJavaPath } = require('./javaManager');
|
const { resolveJavaPath, detectSystemJava, downloadJRE, getJavaExec, getBundledJavaPath } = require('./javaManager');
|
||||||
|
const userDataBackup = require('../utils/userDataBackup');
|
||||||
|
|
||||||
async function downloadPWR(version = 'release', fileName = '4.pwr', progressCallback, cacheDir = CACHE_DIR) {
|
async function downloadPWR(branch = 'release', fileName = '4.pwr', progressCallback, cacheDir = CACHE_DIR) {
|
||||||
const osName = getOS();
|
const osName = getOS();
|
||||||
const arch = getArch();
|
const arch = getArch();
|
||||||
|
|
||||||
@@ -18,9 +19,9 @@ async function downloadPWR(version = 'release', fileName = '4.pwr', progressCall
|
|||||||
throw new Error('Hytale x86_64 Intel Mac Support has not been released yet. Please check back later.');
|
throw new Error('Hytale x86_64 Intel Mac Support has not been released yet. Please check back later.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `https://game-patches.hytale.com/patches/${osName}/${arch}/${version}/0/${fileName}`;
|
const url = `https://game-patches.hytale.com/patches/${osName}/${arch}/${branch}/0/${fileName}`;
|
||||||
|
|
||||||
const dest = path.join(cacheDir, fileName);
|
const dest = path.join(cacheDir, `${branch}_${fileName}`);
|
||||||
|
|
||||||
if (fs.existsSync(dest)) {
|
if (fs.existsSync(dest)) {
|
||||||
console.log('PWR file found in cache:', dest);
|
console.log('PWR file found in cache:', dest);
|
||||||
@@ -104,13 +105,22 @@ async function applyPWR(pwrFile, progressCallback, gameDir = GAME_DIR, toolsDir
|
|||||||
console.log('Installation complete');
|
console.log('Installation complete');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateGameFiles(newVersion, progressCallback, gameDir = GAME_DIR, toolsDir = TOOLS_DIR, cacheDir = CACHE_DIR) {
|
async function updateGameFiles(newVersion, progressCallback, gameDir = GAME_DIR, toolsDir = TOOLS_DIR, cacheDir = CACHE_DIR, branchOverride = null) {
|
||||||
let tempUpdateDir;
|
let tempUpdateDir;
|
||||||
|
let backupPath = null;
|
||||||
|
const branch = branchOverride || loadVersionBranch();
|
||||||
|
const installPath = path.dirname(path.dirname(path.dirname(path.dirname(gameDir))));
|
||||||
|
|
||||||
|
// Vérifier si on a version_client et version_branch dans config.json
|
||||||
|
const config = loadConfig();
|
||||||
|
const hasVersionConfig = !!(config.version_client && config.version_branch);
|
||||||
|
console.log(`[UpdateGameFiles] hasVersionConfig: ${hasVersionConfig}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback('Updating game files...', 0, null, null, null);
|
progressCallback('Updating game files...', 0, null, null, null);
|
||||||
}
|
}
|
||||||
console.log(`Updating game files to version: ${newVersion}`);
|
console.log(`Updating game files to version: ${newVersion} (branch: ${branch})`);
|
||||||
|
|
||||||
tempUpdateDir = path.join(gameDir, '..', 'temp_update');
|
tempUpdateDir = path.join(gameDir, '..', 'temp_update');
|
||||||
|
|
||||||
@@ -123,7 +133,7 @@ async function updateGameFiles(newVersion, progressCallback, gameDir = GAME_DIR,
|
|||||||
progressCallback('Downloading new game version...', 10, null, null, null);
|
progressCallback('Downloading new game version...', 10, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pwrFile = await downloadPWR('release', newVersion, progressCallback, cacheDir);
|
const pwrFile = await downloadPWR(branch, newVersion, progressCallback, cacheDir);
|
||||||
|
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback('Extracting new files...', 50, null, null, null);
|
progressCallback('Extracting new files...', 50, null, null, null);
|
||||||
@@ -132,34 +142,18 @@ async function updateGameFiles(newVersion, progressCallback, gameDir = GAME_DIR,
|
|||||||
await applyPWR(pwrFile, progressCallback, tempUpdateDir, toolsDir);
|
await applyPWR(pwrFile, progressCallback, tempUpdateDir, toolsDir);
|
||||||
|
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback('Replacing game files...', 80, null, null, null);
|
progressCallback('Backing up user data...', 70, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
let userDataBackup = null;
|
// Backup UserData using new system
|
||||||
const userDataPath = findUserDataRecursive(gameDir);
|
try {
|
||||||
|
backupPath = await userDataBackup.backupUserData(installPath, branch, hasVersionConfig);
|
||||||
|
} catch (backupError) {
|
||||||
|
console.warn('UserData backup failed:', backupError.message);
|
||||||
|
}
|
||||||
|
|
||||||
if (userDataPath && fs.existsSync(userDataPath)) {
|
if (progressCallback) {
|
||||||
userDataBackup = path.join(gameDir, '..', 'UserData_backup_' + Date.now());
|
progressCallback('Replacing game files...', 80, null, null, null);
|
||||||
console.log(`Backing up UserData from ${userDataPath} to: ${userDataBackup}`);
|
|
||||||
|
|
||||||
function copyRecursive(src, dest) {
|
|
||||||
const stat = fs.statSync(src);
|
|
||||||
if (stat.isDirectory()) {
|
|
||||||
if (!fs.existsSync(dest)) {
|
|
||||||
fs.mkdirSync(dest, { recursive: true });
|
|
||||||
}
|
|
||||||
const files = fs.readdirSync(src);
|
|
||||||
for (const file of files) {
|
|
||||||
copyRecursive(path.join(src, file), path.join(dest, file));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fs.copyFileSync(src, dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
copyRecursive(userDataPath, userDataBackup);
|
|
||||||
} else {
|
|
||||||
console.log('No UserData folder found in game directory');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs.existsSync(gameDir)) {
|
if (fs.existsSync(gameDir)) {
|
||||||
@@ -175,44 +169,26 @@ async function updateGameFiles(newVersion, progressCallback, gameDir = GAME_DIR,
|
|||||||
const logoResult = await downloadAndReplaceLogo(gameDir, progressCallback);
|
const logoResult = await downloadAndReplaceLogo(gameDir, progressCallback);
|
||||||
console.log('Logo@2x.png update result after update:', logoResult);
|
console.log('Logo@2x.png update result after update:', logoResult);
|
||||||
|
|
||||||
if (userDataBackup && fs.existsSync(userDataBackup)) {
|
if (progressCallback) {
|
||||||
const newUserDataPath = findUserDataPath(gameDir);
|
progressCallback('Restoring user data...', 90, null, null, null);
|
||||||
const userDataParent = path.dirname(newUserDataPath);
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(userDataParent)) {
|
// Restore UserData using new system
|
||||||
fs.mkdirSync(userDataParent, { recursive: true });
|
if (backupPath) {
|
||||||
|
try {
|
||||||
|
await userDataBackup.restoreUserData(backupPath, installPath, branch);
|
||||||
|
await userDataBackup.cleanupBackup(backupPath);
|
||||||
|
} catch (restoreError) {
|
||||||
|
console.warn('UserData restore failed:', restoreError.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Restoring UserData to: ${newUserDataPath}`);
|
|
||||||
|
|
||||||
function copyRecursive(src, dest) {
|
|
||||||
const stat = fs.statSync(src);
|
|
||||||
if (stat.isDirectory()) {
|
|
||||||
if (!fs.existsSync(dest)) {
|
|
||||||
fs.mkdirSync(dest, { recursive: true });
|
|
||||||
}
|
|
||||||
const files = fs.readdirSync(src);
|
|
||||||
for (const file of files) {
|
|
||||||
copyRecursive(path.join(src, file), path.join(dest, file));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fs.copyFileSync(src, dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
copyRecursive(userDataBackup, newUserDataPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Game files updated successfully to version: ${newVersion}`);
|
console.log(`Game files updated successfully to version: ${newVersion}`);
|
||||||
|
|
||||||
if (userDataBackup && fs.existsSync(userDataBackup)) {
|
// Save the updated version and branch to config
|
||||||
try {
|
saveVersionClient(newVersion);
|
||||||
fs.rmSync(userDataBackup, { recursive: true, force: true });
|
const { saveVersionBranch } = require('../core/config');
|
||||||
console.log('UserData backup cleaned up');
|
saveVersionBranch(branch);
|
||||||
} catch (cleanupError) {
|
|
||||||
console.warn('Could not clean up UserData backup:', cleanupError.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Waiting for file system sync...');
|
console.log('Waiting for file system sync...');
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
@@ -225,9 +201,9 @@ async function updateGameFiles(newVersion, progressCallback, gameDir = GAME_DIR,
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating game files:', error);
|
console.error('Error updating game files:', error);
|
||||||
|
|
||||||
if (userDataBackup && fs.existsSync(userDataBackup)) {
|
if (backupPath) {
|
||||||
try {
|
try {
|
||||||
fs.rmSync(userDataBackup, { recursive: true, force: true });
|
await userDataBackup.cleanupBackup(backupPath);
|
||||||
console.log('UserData backup cleaned up after error');
|
console.log('UserData backup cleaned up after error');
|
||||||
} catch (cleanupError) {
|
} catch (cleanupError) {
|
||||||
console.warn('Could not clean up UserData backup:', cleanupError.message);
|
console.warn('Could not clean up UserData backup:', cleanupError.message);
|
||||||
@@ -242,21 +218,45 @@ async function updateGameFiles(newVersion, progressCallback, gameDir = GAME_DIR,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isGameInstalled() {
|
function isGameInstalled(branchOverride = null) {
|
||||||
|
const branch = branchOverride || loadVersionBranch();
|
||||||
const appDir = getResolvedAppDir();
|
const appDir = getResolvedAppDir();
|
||||||
const gameDir = path.join(appDir, 'release', 'package', 'game', 'latest');
|
const gameDir = path.join(appDir, branch, 'package', 'game', 'latest');
|
||||||
const clientPath = findClientPath(gameDir);
|
const clientPath = findClientPath(gameDir);
|
||||||
return clientPath !== null;
|
return clientPath !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function installGame(playerName = 'Player', progressCallback, javaPathOverride, installPathOverride) {
|
async function installGame(playerName = 'Player', progressCallback, javaPathOverride, installPathOverride, branchOverride = null) {
|
||||||
|
const branch = branchOverride || loadVersionBranch();
|
||||||
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');
|
||||||
const customGameDir = path.join(customAppDir, 'release', 'package', 'game', 'latest');
|
const customGameDir = path.join(customAppDir, branch, 'package', 'game', 'latest');
|
||||||
const customJreDir = path.join(customAppDir, 'release', 'package', 'jre', 'latest');
|
const customJreDir = path.join(customAppDir, branch, 'package', 'jre', 'latest');
|
||||||
const userDataDir = path.join(customGameDir, 'Client', 'UserData');
|
const userDataDir = path.join(customGameDir, 'Client', 'UserData');
|
||||||
|
|
||||||
|
// Vérifier si on a version_client et version_branch dans config.json
|
||||||
|
const config = loadConfig();
|
||||||
|
const hasVersionConfig = !!(config.version_client && config.version_branch);
|
||||||
|
console.log(`[InstallGame] Configuration détectée - version_client: ${config.version_client}, version_branch: ${config.version_branch}`);
|
||||||
|
console.log(`[InstallGame] hasVersionConfig: ${hasVersionConfig}`);
|
||||||
|
|
||||||
|
// Backup UserData AVANT l'installation si nécessaire
|
||||||
|
let backupPath = null;
|
||||||
|
if (progressCallback) {
|
||||||
|
progressCallback('Checking for existing UserData...', 5, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`[InstallGame] Tentative de backup UserData (hasVersionConfig: ${hasVersionConfig})...`);
|
||||||
|
backupPath = await userDataBackup.backupUserData(customAppDir, branch, hasVersionConfig);
|
||||||
|
if (backupPath) {
|
||||||
|
console.log(`[InstallGame] ✓ UserData sauvegardé dans: ${backupPath}`);
|
||||||
|
}
|
||||||
|
} catch (backupError) {
|
||||||
|
console.warn('[InstallGame] ✗ Backup UserData échoué:', backupError.message);
|
||||||
|
}
|
||||||
|
|
||||||
[customAppDir, customCacheDir, customToolsDir].forEach(dir => {
|
[customAppDir, customCacheDir, customToolsDir].forEach(dir => {
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
@@ -313,18 +313,47 @@ async function installGame(playerName = 'Player', progressCallback, javaPathOver
|
|||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback('Fetching game files...', null, null, null, null);
|
progressCallback('Fetching game files...', null, null, null, null);
|
||||||
}
|
}
|
||||||
console.log('Installing game files...');
|
console.log(`Installing game files for branch: ${branch}...`);
|
||||||
|
|
||||||
const latestVersion = await getLatestClientVersion();
|
const latestVersion = await getLatestClientVersion(branch);
|
||||||
const pwrFile = await downloadPWR('release', latestVersion, progressCallback, customCacheDir);
|
const pwrFile = await downloadPWR(branch, latestVersion, progressCallback, customCacheDir);
|
||||||
await applyPWR(pwrFile, progressCallback, customGameDir, customToolsDir);
|
await applyPWR(pwrFile, progressCallback, customGameDir, customToolsDir);
|
||||||
|
|
||||||
|
// Save the installed version and branch to config
|
||||||
|
saveVersionClient(latestVersion);
|
||||||
|
const { saveVersionBranch } = require('../core/config');
|
||||||
|
saveVersionBranch(branch);
|
||||||
|
|
||||||
const homeUIResult = await downloadAndReplaceHomePageUI(customGameDir, progressCallback);
|
const homeUIResult = await downloadAndReplaceHomePageUI(customGameDir, progressCallback);
|
||||||
console.log('HomePage.ui update result after installation:', homeUIResult);
|
console.log('HomePage.ui update result after installation:', homeUIResult);
|
||||||
|
|
||||||
const logoResult = await downloadAndReplaceLogo(customGameDir, progressCallback);
|
const logoResult = await downloadAndReplaceLogo(customGameDir, progressCallback);
|
||||||
console.log('Logo@2x.png update result after installation:', logoResult);
|
console.log('Logo@2x.png update result after installation:', logoResult);
|
||||||
|
|
||||||
|
// Ensure UserData directory exists
|
||||||
|
if (!fs.existsSync(userDataDir)) {
|
||||||
|
console.log(`[InstallGame] Création du dossier UserData dans: ${userDataDir}`);
|
||||||
|
fs.mkdirSync(userDataDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore UserData from backup if exists
|
||||||
|
if (backupPath) {
|
||||||
|
if (progressCallback) {
|
||||||
|
progressCallback('Restoring UserData...', 95, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`[InstallGame] Restauration du UserData depuis: ${backupPath}`);
|
||||||
|
await userDataBackup.restoreUserData(backupPath, customAppDir, branch);
|
||||||
|
await userDataBackup.cleanupBackup(backupPath);
|
||||||
|
console.log('[InstallGame] ✓ UserData restauré avec succès');
|
||||||
|
} catch (restoreError) {
|
||||||
|
console.warn('[InstallGame] ✗ Erreur lors de la restauration UserData:', restoreError.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('[InstallGame] Aucun backup à restaurer, dossier UserData vide créé');
|
||||||
|
}
|
||||||
|
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback('Installation complete', 100, null, null, null);
|
progressCallback('Installation complete', 100, null, null, null);
|
||||||
}
|
}
|
||||||
@@ -357,8 +386,9 @@ async function uninstallGame() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkExistingGameInstallation() {
|
function checkExistingGameInstallation(branchOverride = null) {
|
||||||
try {
|
try {
|
||||||
|
const branch = branchOverride || loadVersionBranch();
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
|
|
||||||
if (!config.installPath || !config.installPath.trim()) {
|
if (!config.installPath || !config.installPath.trim()) {
|
||||||
@@ -366,7 +396,7 @@ function checkExistingGameInstallation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const installPath = config.installPath.trim();
|
const installPath = config.installPath.trim();
|
||||||
const gameDir = path.join(installPath, 'HytaleF2P', 'release', 'package', 'game', 'latest');
|
const gameDir = path.join(installPath, 'HytaleF2P', branch, 'package', 'game', 'latest');
|
||||||
|
|
||||||
if (!fs.existsSync(gameDir)) {
|
if (!fs.existsSync(gameDir)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -384,7 +414,8 @@ function checkExistingGameInstallation() {
|
|||||||
clientPath: clientPath,
|
clientPath: clientPath,
|
||||||
userDataPath: userDataPath,
|
userDataPath: userDataPath,
|
||||||
installPath: installPath,
|
installPath: installPath,
|
||||||
hasUserData: userDataPath && fs.existsSync(userDataPath)
|
hasUserData: userDataPath && fs.existsSync(userDataPath),
|
||||||
|
branch: branch
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error checking existing game installation:', error);
|
console.error('Error checking existing game installation:', error);
|
||||||
@@ -392,40 +423,32 @@ function checkExistingGameInstallation() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function repairGame(progressCallback) {
|
async function repairGame(progressCallback, branchOverride = null) {
|
||||||
|
const branch = branchOverride || loadVersionBranch();
|
||||||
const appDir = getResolvedAppDir();
|
const appDir = getResolvedAppDir();
|
||||||
const gameDir = path.join(appDir, 'release', 'package', 'game', 'latest');
|
const gameDir = path.join(appDir, branch, 'package', 'game', 'latest');
|
||||||
|
const installPath = appDir;
|
||||||
|
let backupPath = null;
|
||||||
|
|
||||||
|
// Vérifier si on a version_client et version_branch dans config.json
|
||||||
|
const config = loadConfig();
|
||||||
|
const hasVersionConfig = !!(config.version_client && config.version_branch);
|
||||||
|
console.log(`[RepairGame] hasVersionConfig: ${hasVersionConfig}`);
|
||||||
|
|
||||||
// Check if game exists
|
// Check if game exists
|
||||||
if (!fs.existsSync(gameDir)) {
|
if (!fs.existsSync(gameDir)) {
|
||||||
throw new Error('Game directory not found. Cannot repair.');
|
throw new Error('Game directory not found. Cannot repair.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locate UserData
|
|
||||||
const userDataPath = findUserDataRecursive(gameDir);
|
|
||||||
let userDataBackup = null;
|
|
||||||
|
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback('Backing up user data...', 10, null, null, null);
|
progressCallback('Backing up user data...', 10, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backup UserData
|
// Backup UserData using new system
|
||||||
if (userDataPath && fs.existsSync(userDataPath)) {
|
try {
|
||||||
userDataBackup = path.join(appDir, 'UserData_backup_repair_' + Date.now());
|
backupPath = await userDataBackup.backupUserData(installPath, branch, hasVersionConfig);
|
||||||
console.log(`Backing up UserData during repair from ${userDataPath} to ${userDataBackup}`);
|
} catch (backupError) {
|
||||||
|
console.warn('UserData backup failed during repair:', backupError.message);
|
||||||
// Copy function
|
|
||||||
function copyRecursive(src, dest) {
|
|
||||||
const stat = fs.statSync(src);
|
|
||||||
if (stat.isDirectory()) {
|
|
||||||
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
||||||
fs.readdirSync(src).forEach(child => copyRecursive(path.join(src, child), path.join(dest, child)));
|
|
||||||
} else {
|
|
||||||
fs.copyFileSync(src, dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
copyRecursive(userDataPath, userDataBackup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
@@ -446,39 +469,21 @@ async function repairGame(progressCallback) {
|
|||||||
|
|
||||||
// Passing null/undefined for overrides to use defaults/saved configs
|
// Passing null/undefined for overrides to use defaults/saved configs
|
||||||
// installGame calls progressCallback internally
|
// installGame calls progressCallback internally
|
||||||
await installGame('Player', progressCallback);
|
await installGame('Player', progressCallback, null, null, branch);
|
||||||
|
|
||||||
// Restore UserData
|
// Restore UserData using new system
|
||||||
if (userDataBackup && fs.existsSync(userDataBackup)) {
|
if (backupPath) {
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback('Restoring user data...', 90, null, null, null);
|
progressCallback('Restoring user data...', 90, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// installGame creates: path.join(customGameDir, 'Client', 'UserData')
|
try {
|
||||||
const newGameDir = path.join(appDir, 'release', 'package', 'game', 'latest');
|
await userDataBackup.restoreUserData(backupPath, installPath, branch);
|
||||||
const newUserDataPath = path.join(newGameDir, 'Client', 'UserData');
|
await userDataBackup.cleanupBackup(backupPath);
|
||||||
|
console.log('UserData restored successfully after repair');
|
||||||
if (!fs.existsSync(newUserDataPath)) {
|
} catch (restoreError) {
|
||||||
fs.mkdirSync(newUserDataPath, { recursive: true });
|
console.warn('UserData restore failed after repair:', restoreError.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Restoring UserData to ${newUserDataPath}`);
|
|
||||||
|
|
||||||
function copyRecursive(src, dest) {
|
|
||||||
const stat = fs.statSync(src);
|
|
||||||
if (stat.isDirectory()) {
|
|
||||||
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
||||||
fs.readdirSync(src).forEach(child => copyRecursive(path.join(src, child), path.join(dest, child)));
|
|
||||||
} else {
|
|
||||||
fs.copyFileSync(src, dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
copyRecursive(userDataBackup, newUserDataPath);
|
|
||||||
|
|
||||||
// Cleanup Backup
|
|
||||||
console.log('Cleaning up repair backup...');
|
|
||||||
fs.rmSync(userDataBackup, { recursive: true, force: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { markAsLaunched, loadConfig } = require('../core/config');
|
const { markAsLaunched, loadConfig, saveVersionBranch, saveVersionClient, loadVersionBranch, loadVersionClient } = require('../core/config');
|
||||||
const { checkExistingGameInstallation, updateGameFiles } = require('../managers/gameManager');
|
const { checkExistingGameInstallation, updateGameFiles } = require('../managers/gameManager');
|
||||||
const { getInstalledClientVersion, getLatestClientVersion } = require('./versionManager');
|
const { getInstalledClientVersion, getLatestClientVersion } = require('./versionManager');
|
||||||
|
|
||||||
@@ -56,6 +56,20 @@ async function handleFirstLaunchCheck(progressCallback) {
|
|||||||
try {
|
try {
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
|
|
||||||
|
// Initialize version_branch and version_client if not set
|
||||||
|
const currentBranch = loadVersionBranch();
|
||||||
|
const currentVersion = loadVersionClient();
|
||||||
|
|
||||||
|
if (!currentBranch) {
|
||||||
|
console.log('Initializing version_branch to "release"');
|
||||||
|
saveVersionBranch('release');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVersion === undefined || currentVersion === null) {
|
||||||
|
console.log('Initializing version_client to null (will trigger installation)');
|
||||||
|
saveVersionClient(null);
|
||||||
|
}
|
||||||
|
|
||||||
if (config.hasLaunchedBefore === true) {
|
if (config.hasLaunchedBefore === true) {
|
||||||
return { isFirstLaunch: false, needsUpdate: false };
|
return { isFirstLaunch: false, needsUpdate: false };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
|
||||||
async function getLatestClientVersion() {
|
async function getLatestClientVersion(branch = 'release') {
|
||||||
try {
|
try {
|
||||||
console.log('Fetching latest client version from API...');
|
console.log(`Fetching latest client version from API (branch: ${branch})...`);
|
||||||
const response = await axios.get('https://files.hytalef2p.com/api/version_client', {
|
const response = await axios.get('https://files.hytalef2p.com/api/version_client', {
|
||||||
|
params: { branch },
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': 'Hytale-F2P-Launcher'
|
'User-Agent': 'Hytale-F2P-Launcher'
|
||||||
@@ -12,7 +13,7 @@ async function getLatestClientVersion() {
|
|||||||
|
|
||||||
if (response.data && response.data.client_version) {
|
if (response.data && response.data.client_version) {
|
||||||
const version = response.data.client_version;
|
const version = response.data.client_version;
|
||||||
console.log(`Latest client version: ${version}`);
|
console.log(`Latest client version for ${branch}: ${version}`);
|
||||||
return version;
|
return version;
|
||||||
} else {
|
} else {
|
||||||
console.log('Warning: Invalid API response, falling back to default version');
|
console.log('Warning: Invalid API response, falling back to default version');
|
||||||
|
|||||||
@@ -11,6 +11,18 @@ class UpdateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async checkForUpdates() {
|
async checkForUpdates() {
|
||||||
|
// Disabled: Using electron-updater for automatic updates instead
|
||||||
|
console.log('Update check skipped - using electron-updater');
|
||||||
|
console.log(`Current version: ${CURRENT_VERSION}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateAvailable: false,
|
||||||
|
currentVersion: CURRENT_VERSION,
|
||||||
|
newVersion: CURRENT_VERSION,
|
||||||
|
message: 'Using electron-updater for automatic updates'
|
||||||
|
};
|
||||||
|
|
||||||
|
/* kept for reference
|
||||||
try {
|
try {
|
||||||
console.log('Checking for updates...');
|
console.log('Checking for updates...');
|
||||||
console.log(`Local version: ${CURRENT_VERSION}`);
|
console.log(`Local version: ${CURRENT_VERSION}`);
|
||||||
@@ -54,6 +66,7 @@ class UpdateManager {
|
|||||||
currentVersion: CURRENT_VERSION
|
currentVersion: CURRENT_VERSION
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
getDownloadUrl() {
|
getDownloadUrl() {
|
||||||
|
|||||||
129
backend/utils/userDataBackup.js
Normal file
129
backend/utils/userDataBackup.js
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backup and restore UserData folder during game updates
|
||||||
|
*/
|
||||||
|
class UserDataBackup {
|
||||||
|
/**
|
||||||
|
* Backup UserData folder to a temporary location
|
||||||
|
* @param {string} installPath - Base installation path (e.g., C:\Users\...\HytaleF2P)
|
||||||
|
* @param {string} branch - Branch name (release or pre-release)
|
||||||
|
* @param {boolean} hasVersionConfig - True if config.json has version_client and version_branch
|
||||||
|
* @returns {Promise<string|null>} - Path to backup or null if no UserData found
|
||||||
|
*/
|
||||||
|
async backupUserData(installPath, branch, hasVersionConfig = true) {
|
||||||
|
let userDataPath;
|
||||||
|
|
||||||
|
// Si on n'a pas de version_client/version_branch dans config.json,
|
||||||
|
// c'est une ancienne installation, on cherche dans installPath/HytaleF2P/release
|
||||||
|
if (!hasVersionConfig) {
|
||||||
|
const oldPath = path.join(installPath, 'HytaleF2P', 'release', 'package', 'game', 'latest', 'Client', 'UserData');
|
||||||
|
console.log(`[UserDataBackup] Pas de version_client/version_branch détecté, recherche ancienne installation dans: ${oldPath}`);
|
||||||
|
|
||||||
|
if (fs.existsSync(oldPath)) {
|
||||||
|
userDataPath = oldPath;
|
||||||
|
console.log(`[UserDataBackup] ✓ Ancienne installation trouvée ! UserData existe dans l'ancien emplacement`);
|
||||||
|
} else {
|
||||||
|
console.log(`[UserDataBackup] ✗ Aucune ancienne installation trouvée dans ${oldPath}`);
|
||||||
|
userDataPath = path.join(installPath, branch, 'package', 'game', 'latest', 'Client', 'UserData');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Si on a version_client/version_branch, on cherche dans installPath/HytaleF2P/<branch>
|
||||||
|
userDataPath = path.join(installPath, branch, 'package', 'game', 'latest', 'Client', 'UserData');
|
||||||
|
console.log(`[UserDataBackup] Version configurée, recherche dans: ${userDataPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(userDataPath)) {
|
||||||
|
console.log(`[UserDataBackup] ✗ Aucun UserData trouvé à ${userDataPath}, backup ignoré`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[UserDataBackup] ✓ UserData trouvé à ${userDataPath}`);
|
||||||
|
const backupPath = path.join(installPath, `UserData_backup_${branch}_${Date.now()}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`[UserDataBackup] Copie de ${userDataPath} vers ${backupPath}...`);
|
||||||
|
await fs.copy(userDataPath, backupPath, {
|
||||||
|
overwrite: true,
|
||||||
|
errorOnExist: false
|
||||||
|
});
|
||||||
|
console.log('[UserDataBackup] ✓ Backup complété avec succès');
|
||||||
|
return backupPath;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[UserDataBackup] ✗ Erreur lors du backup:', error);
|
||||||
|
throw new Error(`Failed to backup UserData: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore UserData folder from backup
|
||||||
|
* @param {string} backupPath - Path to the backup folder
|
||||||
|
* @param {string} installPath - Base installation path
|
||||||
|
* @param {string} branch - Branch name (release or pre-release)
|
||||||
|
* @returns {Promise<boolean>} - True if restored, false otherwise
|
||||||
|
*/
|
||||||
|
async restoreUserData(backupPath, installPath, branch) {
|
||||||
|
if (!backupPath || !fs.existsSync(backupPath)) {
|
||||||
|
console.log('No backup to restore or backup path does not exist');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userDataPath = path.join(installPath, branch, 'package', 'game', 'latest', 'Client', 'UserData');
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`Restoring UserData from ${backupPath} to ${userDataPath}`);
|
||||||
|
|
||||||
|
// Ensure parent directory exists
|
||||||
|
const parentDir = path.dirname(userDataPath);
|
||||||
|
if (!fs.existsSync(parentDir)) {
|
||||||
|
await fs.ensureDir(parentDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.copy(backupPath, userDataPath, {
|
||||||
|
overwrite: true,
|
||||||
|
errorOnExist: false
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('UserData restore completed successfully');
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error restoring UserData:', error);
|
||||||
|
throw new Error(`Failed to restore UserData: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up backup folder
|
||||||
|
* @param {string} backupPath - Path to the backup folder to delete
|
||||||
|
* @returns {Promise<boolean>} - True if deleted, false otherwise
|
||||||
|
*/
|
||||||
|
async cleanupBackup(backupPath) {
|
||||||
|
if (!backupPath || !fs.existsSync(backupPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`Cleaning up backup at ${backupPath}`);
|
||||||
|
await fs.remove(backupPath);
|
||||||
|
console.log('Backup cleanup completed');
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error cleaning up backup:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if UserData exists for a specific branch
|
||||||
|
* @param {string} installPath - Base installation path
|
||||||
|
* @param {string} branch - Branch name (release or pre-release)
|
||||||
|
* @returns {boolean} - True if UserData exists
|
||||||
|
*/
|
||||||
|
hasUserData(installPath, branch) {
|
||||||
|
const userDataPath = path.join(installPath, branch, 'package', 'game', 'latest', 'Client', 'UserData');
|
||||||
|
return fs.existsSync(userDataPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new UserDataBackup();
|
||||||
146
main.js
146
main.js
@@ -2,8 +2,8 @@ const path = require('path');
|
|||||||
require('dotenv').config({ path: path.join(__dirname, '.env') });
|
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 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, isGameInstalled, uninstallGame, repairGame, getHytaleNews, handleFirstLaunchCheck, proposeGameUpdate, markAsLaunched } = require('./backend/launcher');
|
||||||
|
|
||||||
const UpdateManager = require('./backend/updateManager');
|
const UpdateManager = require('./backend/updateManager');
|
||||||
const logger = require('./backend/logger');
|
const logger = require('./backend/logger');
|
||||||
const profileManager = require('./backend/managers/profileManager');
|
const profileManager = require('./backend/managers/profileManager');
|
||||||
@@ -187,21 +187,21 @@ function createWindow() {
|
|||||||
if (input.key === 'F12') {
|
if (input.key === 'F12') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
if (input.key === 'F5') {
|
if (input.key === 'F5') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mainWindow.webContents.on('context-menu', (e) => {
|
mainWindow.webContents.on('context-menu', (e) => {
|
||||||
@@ -345,14 +345,14 @@ app.on('before-quit', () => {
|
|||||||
cleanupDiscordRPC();
|
cleanupDiscordRPC();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
console.log('=== LAUNCHER CLOSING ===');
|
console.log('=== LAUNCHER CLOSING ===');
|
||||||
|
|
||||||
cleanupDiscordRPC();
|
cleanupDiscordRPC();
|
||||||
|
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, gpuPreference) => {
|
ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, gpuPreference) => {
|
||||||
try {
|
try {
|
||||||
@@ -369,20 +369,20 @@ ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, g
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath, gpuPreference);
|
const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath, gpuPreference);
|
||||||
|
|
||||||
if (result.success && result.launched) {
|
if (result.success && result.launched) {
|
||||||
const closeOnStart = loadCloseLauncherOnStart();
|
const closeOnStart = loadCloseLauncherOnStart();
|
||||||
if (closeOnStart) {
|
if (closeOnStart) {
|
||||||
console.log('Close Launcher on start enabled, quitting application...');
|
console.log('Close Launcher on start enabled, quitting application...');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
app.quit();
|
app.quit();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Launch error:', error);
|
console.error('Launch error:', error);
|
||||||
const errorMessage = error.message || error.toString();
|
const errorMessage = error.message || error.toString();
|
||||||
@@ -397,8 +397,10 @@ ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, g
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('install-game', async (event, playerName, javaPath, installPath) => {
|
ipcMain.handle('install-game', async (event, playerName, javaPath, installPath, branch) => {
|
||||||
try {
|
try {
|
||||||
|
console.log(`[IPC] install-game called with branch: ${branch || 'default'}`);
|
||||||
|
|
||||||
// Signal installation start
|
// Signal installation start
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send('installation-start');
|
mainWindow.webContents.send('installation-start');
|
||||||
@@ -417,7 +419,7 @@ ipcMain.handle('install-game', async (event, playerName, javaPath, installPath)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await installGame(playerName, progressCallback, javaPath, installPath);
|
const result = await installGame(playerName, progressCallback, javaPath, installPath, branch);
|
||||||
|
|
||||||
// Signal installation end
|
// Signal installation end
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
@@ -497,21 +499,21 @@ ipcMain.handle('save-language', (event, language) => {
|
|||||||
return { success: true };
|
return { success: true };
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('load-language', () => {
|
ipcMain.handle('load-language', () => {
|
||||||
return loadLanguage();
|
return loadLanguage();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('save-close-launcher', (event, enabled) => {
|
ipcMain.handle('save-close-launcher', (event, enabled) => {
|
||||||
saveCloseLauncherOnStart(enabled);
|
saveCloseLauncherOnStart(enabled);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('load-close-launcher', () => {
|
ipcMain.handle('load-close-launcher', () => {
|
||||||
return loadCloseLauncherOnStart();
|
return loadCloseLauncherOnStart();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('select-install-path', async () => {
|
ipcMain.handle('select-install-path', async () => {
|
||||||
|
|
||||||
const result = await dialog.showOpenDialog(mainWindow, {
|
const result = await dialog.showOpenDialog(mainWindow, {
|
||||||
properties: ['openDirectory'],
|
properties: ['openDirectory'],
|
||||||
title: 'Select Installation Folder'
|
title: 'Select Installation Folder'
|
||||||
@@ -836,10 +838,10 @@ ipcMain.handle('open-download-page', async () => {
|
|||||||
try {
|
try {
|
||||||
await shell.openExternal(updateManager.getDownloadUrl());
|
await shell.openExternal(updateManager.getDownloadUrl());
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
app.quit();
|
app.quit();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -881,10 +883,26 @@ ipcMain.handle('get-detected-gpu', () => {
|
|||||||
return global.detectedGpu;
|
return global.detectedGpu;
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('window-close', () => {
|
ipcMain.handle('save-version-branch', (event, branch) => {
|
||||||
app.quit();
|
const { saveVersionBranch } = require('./backend/launcher');
|
||||||
});
|
saveVersionBranch(branch);
|
||||||
|
return { success: true };
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('load-version-branch', () => {
|
||||||
|
const { loadVersionBranch } = require('./backend/launcher');
|
||||||
|
return loadVersionBranch();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('load-version-client', () => {
|
||||||
|
const { loadVersionClient } = require('./backend/launcher');
|
||||||
|
return loadVersionClient();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('window-close', () => {
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
ipcMain.handle('window-minimize', () => {
|
ipcMain.handle('window-minimize', () => {
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
"discord-rpc": "^4.0.1",
|
"discord-rpc": "^4.0.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"electron-updater": "^6.7.3",
|
"electron-updater": "^6.7.3",
|
||||||
|
"fs-extra": "^11.3.3",
|
||||||
"tar": "^6.2.1",
|
"tar": "^6.2.1",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
loadGpuPreference: () => ipcRenderer.invoke('load-gpu-preference'),
|
loadGpuPreference: () => ipcRenderer.invoke('load-gpu-preference'),
|
||||||
getDetectedGpu: () => ipcRenderer.invoke('get-detected-gpu'),
|
getDetectedGpu: () => ipcRenderer.invoke('get-detected-gpu'),
|
||||||
|
|
||||||
|
saveVersionBranch: (branch) => ipcRenderer.invoke('save-version-branch', branch),
|
||||||
|
loadVersionBranch: () => ipcRenderer.invoke('load-version-branch'),
|
||||||
|
loadVersionClient: () => ipcRenderer.invoke('load-version-client'),
|
||||||
|
|
||||||
acceptFirstLaunchUpdate: (existingGame) => ipcRenderer.invoke('accept-first-launch-update', existingGame),
|
acceptFirstLaunchUpdate: (existingGame) => ipcRenderer.invoke('accept-first-launch-update', existingGame),
|
||||||
markAsLaunched: () => ipcRenderer.invoke('mark-as-launched'),
|
markAsLaunched: () => ipcRenderer.invoke('mark-as-launched'),
|
||||||
onFirstLaunchUpdate: (callback) => {
|
onFirstLaunchUpdate: (callback) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user