Compare commits

..

4 Commits

Author SHA1 Message Date
sanasol
dd2dbc6f08 fix: improve update system UX and macOS compatibility
Update System Improvements:
- Fix duplicate update popups by disabling legacy updater.js
- Add skip button to update popup (shows after 30s, on error, or after download)
- Add macOS-specific handling with manual download as primary option
- Add missing open-download-page IPC handler
- Add missing unblockInterface() method to properly clean up after popup close
- Add quitAndInstallUpdate alias in preload for compatibility
- Remove pulse animation when download completes
- Fix manual download button to show correct status and close popup
- Sync player name to settings input after first install

Client Patcher Cleanup:
- Remove server patching code (server uses pre-patched JAR from CDN)
- Simplify to client-only patching
- Remove unused imports (crypto, AdmZip, execSync, spawn, javaManager)
- Remove unused methods (stringToUtf8, findAndReplaceDomainUtf8)
- Move localhost dev code to backup file for reference

Code Quality Fixes:
- Fix duplicate DOMContentLoaded handlers in install.js
- Fix duplicate checkForUpdates definition in preload.js
- Fix redundant if/else in onProgressUpdate callback
- Fix typo "Harwadre" -> "Hardware" in preload.js

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 01:48:58 +01:00
Fazri Gading
c4acb32fcd Update support_request.yml 2026-01-28 05:16:00 +08:00
Fazri Gading
fbcbafb9b5 chore: remove Windows and Linux ARM64 information on the README.md 2026-01-28 04:26:42 +08:00
Terromur
86ed33358c Merge pull request #210 from amiayweb/fix/steamdeck-libzstd
fix: Steam Deck/Ubuntu crash - use system libzstd.so
2026-01-27 23:51:04 +05:00
8 changed files with 564 additions and 570 deletions

View File

@@ -1,8 +1,22 @@
name: Support Request
description: Request help or support
title: "[SUPPORT] "
title: "[SUPPORT] <ADD YOUR TITLE HERE>"
labels: ["support"]
body:
- type: dropdown
id: acknowledge
attributes:
label: Checklist
options:
- label: I have read the README.md before asking Support Request.
required: true
- label: I have read the TROUBLESHOOTING.md before asking Support Request.
required: true
- label: I have added title before submitting this Support Request.
required: true
- label: I acknowledge that my Support Request will not be responded as quick as in Discord Open-A-Ticket, I prefer this way.
required: true
- type: markdown
attributes:
value: |
@@ -24,7 +38,7 @@ body:
attributes:
label: Context
description: Provide any relevant context or background information.
placeholder: "I've tried..., but got..."
placeholder: "I've tried these steps, but got..."
validations:
required: true
@@ -37,12 +51,17 @@ body:
validations:
required: true
- type: input
- type: dropdown
id: version
attributes:
label: Version
description: What version are you using?
placeholder: "e.g. v2.0.11 stable/pre-release"
options:
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.11
- v2.0.2
validations:
required: true
@@ -52,13 +71,12 @@ body:
label: Platform
description: What platform are you using?
options:
- Windows 10
- Windows 11
- macOS (Apple Silicon)
- macOS (Intel)
- Linux Ubuntu/Debian-based
- Linux Fedora/RHEL-based
- Linux Arch-based
- Windows 11 x64
- Windows 10 x64
- macOS ARM64 (Apple Silicon)
- Linux x64 Ubuntu/Debian-based
- Linux x64 Fedora/RHEL-based
- Linux x64 Arch-based
validations:
required: true

View File

@@ -882,7 +882,7 @@
<script src="js/i18n.js"></script>
<script type="module" src="js/settings.js"></script>
<script type="module" src="js/update.js"></script>
<script src="js/updater.js"></script>
<!-- updater.js disabled - using update.js instead which has skip button and macOS handling -->
</body>

View File

@@ -72,8 +72,11 @@ export async function installGame() {
setTimeout(() => {
window.LauncherUI.hideProgress();
window.LauncherUI.showLauncherOrInstall(true);
// Sync player name to both launcher and settings inputs
const playerNameInput = document.getElementById('playerName');
if (playerNameInput) playerNameInput.value = playerName;
const settingsPlayerName = document.getElementById('settingsPlayerName');
if (settingsPlayerName) settingsPlayerName.value = playerName;
resetInstallButton();
}, 2000);
}
@@ -125,8 +128,11 @@ function simulateInstallation(playerName) {
setTimeout(() => {
window.LauncherUI.hideProgress();
window.LauncherUI.showLauncherOrInstall(true);
// Sync player name to both launcher and settings inputs
const playerNameInput = document.getElementById('playerName');
if (playerNameInput) playerNameInput.value = playerName;
const settingsPlayerName = document.getElementById('settingsPlayerName');
if (settingsPlayerName) settingsPlayerName.value = playerName;
resetInstallButton();
}, 2000);
}
@@ -246,9 +252,3 @@ document.addEventListener('DOMContentLoaded', async () => {
setupInstallation();
await checkGameStatusAndShowInterface();
});
window.browseInstallPath = browseInstallPath;
document.addEventListener('DOMContentLoaded', async () => {
setupInstallation();
await checkGameStatusAndShowInterface();
});

View File

@@ -6,12 +6,12 @@ class ClientUpdateManager {
}
init() {
window.electronAPI.onUpdatePopup((updateInfo) => {
this.showUpdatePopup(updateInfo);
});
console.log('🔧 ClientUpdateManager initializing...');
// Listen for electron-updater events
// Listen for electron-updater events from main.js
// This is the primary update trigger - main.js checks for updates on startup
window.electronAPI.onUpdateAvailable((updateInfo) => {
console.log('📥 update-available event received:', updateInfo);
this.showUpdatePopup(updateInfo);
});
@@ -20,18 +20,30 @@ class ClientUpdateManager {
});
window.electronAPI.onUpdateDownloaded((updateInfo) => {
console.log('📦 update-downloaded event received:', updateInfo);
this.showUpdateDownloaded(updateInfo);
});
window.electronAPI.onUpdateError((errorInfo) => {
console.log('❌ update-error event received:', errorInfo);
this.handleUpdateError(errorInfo);
});
this.checkForUpdatesOnDemand();
console.log('✅ ClientUpdateManager initialized');
// Note: Don't call checkForUpdatesOnDemand() here - main.js already checks
// for updates after 3 seconds and sends 'update-available' event.
// Calling it here would cause duplicate popups.
}
showUpdatePopup(updateInfo) {
if (this.updatePopupVisible) return;
console.log('🔔 showUpdatePopup called, updatePopupVisible:', this.updatePopupVisible);
// Check if popup already exists in DOM (extra safety)
if (this.updatePopupVisible || document.getElementById('update-popup-overlay')) {
console.log('⚠️ Update popup already visible, skipping');
return;
}
this.updatePopupVisible = true;
@@ -92,7 +104,10 @@ class ClientUpdateManager {
</div>
<div class="update-popup-footer">
This popup cannot be closed until you update the launcher
<span id="update-footer-text">Downloading update...</span>
<button id="update-skip-btn" class="update-skip-btn" style="display: none; margin-top: 0.5rem; background: transparent; border: 1px solid rgba(255,255,255,0.2); color: #9ca3af; padding: 0.5rem 1rem; border-radius: 0.25rem; cursor: pointer; font-size: 0.75rem;">
Skip for now (not recommended)
</button>
</div>
</div>
</div>
@@ -113,16 +128,43 @@ class ClientUpdateManager {
installBtn.addEventListener('click', async (e) => {
e.preventDefault();
e.stopPropagation();
installBtn.disabled = true;
installBtn.innerHTML = '<i class="fas fa-spinner fa-spin" style="margin-right: 0.5rem;"></i>Installing...';
try {
await window.electronAPI.quitAndInstallUpdate();
// If we're still here after 5 seconds, the install probably failed
setTimeout(() => {
console.log('⚠️ Install may have failed - showing skip option');
installBtn.disabled = false;
installBtn.innerHTML = '<i class="fas fa-check" style="margin-right: 0.5rem;"></i>Try Again';
// Show skip button
const skipBtn = document.getElementById('update-skip-btn');
const footerText = document.getElementById('update-footer-text');
if (skipBtn) {
skipBtn.style.display = 'inline-block';
if (footerText) {
footerText.textContent = 'Install not working? Skip for now:';
}
}
}, 5000);
} catch (error) {
console.error('❌ Error installing update:', error);
installBtn.disabled = false;
installBtn.innerHTML = '<i class="fas fa-check" style="margin-right: 0.5rem;"></i>Install & Restart';
// Show skip button on error
const skipBtn = document.getElementById('update-skip-btn');
const footerText = document.getElementById('update-footer-text');
if (skipBtn) {
skipBtn.style.display = 'inline-block';
if (footerText) {
footerText.textContent = 'Install failed. Skip for now:';
}
}
}
});
}
@@ -138,10 +180,15 @@ class ClientUpdateManager {
try {
await window.electronAPI.openDownloadPage();
console.log('✅ Download page opened, launcher will close...');
downloadBtn.innerHTML = '<i class="fas fa-check" style="margin-right: 0.5rem;"></i>Launcher closing...';
console.log('✅ Download page opened');
downloadBtn.innerHTML = '<i class="fas fa-check" style="margin-right: 0.5rem;"></i>Opened in browser';
// Close the popup after opening download page
setTimeout(() => {
this.closeUpdatePopup();
}, 1500);
} catch (error) {
console.error('❌ Error opening download page:', error);
downloadBtn.disabled = false;
@@ -161,9 +208,39 @@ class ClientUpdateManager {
});
}
// Show skip button after 30 seconds as fallback (in case update is stuck)
setTimeout(() => {
const skipBtn = document.getElementById('update-skip-btn');
const footerText = document.getElementById('update-footer-text');
if (skipBtn) {
skipBtn.style.display = 'inline-block';
if (footerText) {
footerText.textContent = 'Update taking too long?';
}
}
}, 30000);
const skipBtn = document.getElementById('update-skip-btn');
if (skipBtn) {
skipBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
this.closeUpdatePopup();
});
}
console.log('🔔 Update popup displayed with new style');
}
closeUpdatePopup() {
const overlay = document.getElementById('update-popup-overlay');
if (overlay) {
overlay.remove();
}
this.updatePopupVisible = false;
this.unblockInterface();
}
updateDownloadProgress(progress) {
const progressBar = document.getElementById('update-progress-bar');
const progressPercent = document.getElementById('update-progress-percent');
@@ -197,35 +274,96 @@ class ClientUpdateManager {
const statusText = document.getElementById('update-status-text');
const progressContainer = document.getElementById('update-progress-container');
const buttonsContainer = document.getElementById('update-buttons-container');
const installBtn = document.getElementById('update-install-btn');
const downloadBtn = document.getElementById('update-download-btn');
const skipBtn = document.getElementById('update-skip-btn');
const footerText = document.getElementById('update-footer-text');
const popupContainer = document.querySelector('.update-popup-container');
if (statusText) {
statusText.textContent = 'Update downloaded! Ready to install.';
// Remove breathing/pulse animation when download is complete
if (popupContainer) {
popupContainer.classList.remove('update-popup-pulse');
}
if (progressContainer) {
progressContainer.style.display = 'none';
}
// Use platform info from main process if available, fallback to browser detection
const autoInstallSupported = updateInfo.autoInstallSupported !== undefined
? updateInfo.autoInstallSupported
: navigator.platform.toUpperCase().indexOf('MAC') < 0;
if (!autoInstallSupported) {
// macOS: Show manual download as primary since auto-update doesn't work
if (statusText) {
statusText.textContent = 'Update downloaded but auto-install may not work on macOS.';
}
if (installBtn) {
// Still show install button but as secondary option
installBtn.classList.add('update-download-btn-secondary');
installBtn.innerHTML = '<i class="fas fa-check" style="margin-right: 0.5rem;"></i>Try Install & Restart';
}
if (downloadBtn) {
// Make manual download primary
downloadBtn.classList.remove('update-download-btn-secondary');
downloadBtn.innerHTML = '<i class="fas fa-external-link-alt" style="margin-right: 0.5rem;"></i>Download Manually (Recommended)';
}
if (footerText) {
footerText.textContent = 'Auto-install often fails on macOS:';
}
} else {
// Windows/Linux: Auto-install should work
if (statusText) {
statusText.textContent = 'Update downloaded! Ready to install.';
}
if (footerText) {
footerText.textContent = 'Click to install the update:';
}
}
if (buttonsContainer) {
buttonsContainer.style.display = 'block';
}
console.log('✅ Update downloaded, ready to install');
// Always show skip button in downloaded state
if (skipBtn) {
skipBtn.style.display = 'inline-block';
console.log('✅ Skip button made visible');
} else {
console.error('❌ Skip button not found in DOM!');
}
console.log('✅ Update downloaded, ready to install. autoInstallSupported:', autoInstallSupported);
}
handleUpdateError(errorInfo) {
console.error('Update error:', errorInfo);
// Show skip button immediately on any error
const skipBtn = document.getElementById('update-skip-btn');
const footerText = document.getElementById('update-footer-text');
if (skipBtn) {
skipBtn.style.display = 'inline-block';
if (footerText) {
footerText.textContent = 'Update failed. You can skip for now.';
}
}
// If manual download is required, update the UI (this will handle status text)
if (errorInfo.requiresManualDownload) {
this.showManualDownloadRequired(errorInfo);
return; // Don't do anything else, showManualDownloadRequired handles everything
}
// For non-critical errors, just show error message without changing status
const errorMessage = document.getElementById('update-error-message');
const errorText = document.getElementById('update-error-text');
if (errorMessage && errorText) {
let message = errorInfo.message || 'An error occurred during the update process.';
if (errorInfo.isMacSigningError) {
@@ -289,6 +427,16 @@ class ClientUpdateManager {
buttonsContainer.style.display = 'block';
}
// Show skip button for manual download errors
const skipBtn = document.getElementById('update-skip-btn');
const footerText = document.getElementById('update-footer-text');
if (skipBtn) {
skipBtn.style.display = 'inline-block';
if (footerText) {
footerText.textContent = 'Or continue without updating:';
}
}
console.log('⚠️ Manual download required due to update error');
}
@@ -300,13 +448,35 @@ class ClientUpdateManager {
document.body.classList.add('no-select');
document.addEventListener('keydown', this.blockKeyEvents.bind(this), true);
document.addEventListener('contextmenu', this.blockContextMenu.bind(this), true);
// Store bound functions so we can remove them later
this._boundBlockKeyEvents = this.blockKeyEvents.bind(this);
this._boundBlockContextMenu = this.blockContextMenu.bind(this);
document.addEventListener('keydown', this._boundBlockKeyEvents, true);
document.addEventListener('contextmenu', this._boundBlockContextMenu, true);
console.log('🚫 Interface blocked for update');
}
unblockInterface() {
const mainContent = document.querySelector('.flex.w-full.h-screen');
if (mainContent) {
mainContent.classList.remove('interface-blocked');
}
document.body.classList.remove('no-select');
// Remove event listeners
if (this._boundBlockKeyEvents) {
document.removeEventListener('keydown', this._boundBlockKeyEvents, true);
}
if (this._boundBlockContextMenu) {
document.removeEventListener('contextmenu', this._boundBlockContextMenu, true);
}
console.log('✅ Interface unblocked');
}
blockKeyEvents(event) {
if (event.target.closest('#update-popup-overlay')) {
if ((event.key === 'Enter' || event.key === ' ') &&

View File

@@ -4,7 +4,7 @@
<h1>🎮 Hytale F2P Launcher 🚀</h1>
<h2>💻 Cross-Platform Multiplayer 🖥️</h2>
<h3>Available for Windows 🪟, macOS 🍎, and Linux 🐧</h3>
<p><small>An unofficial cross-platform launcher for Hytale with automatic updates and multiplayer support (all OS supported)</small></p>
<p><small>An unofficial cross-platform launcher for Hytale with automatic updates and multiplayer support!</small></p>
</header>
![GitHub Downloads](https://img.shields.io/github/downloads/amiayweb/Hytale-F2P/total?style=for-the-badge)
@@ -119,9 +119,9 @@
<tr>
<td><b>🖥️ OS</b></td>
<td colspan="3" align="center">
Windows 10/11 (64-bit; X64/ARM64) | Linux (x64/ARM64) | macOS (Apple Silicon only)
Windows 10/11 (64-bit X64) | Linux (x64) | macOS (ARM64/Apple Silicon)
<br />
<small><i>⚠️ Note: macOS Intel (x86) is not yet supported <sup><a href="#fn1" id="ref1">1</a></sup></i></small>
<small><i>⚠️ Note: ARM64 (Windows & Linux), macOS (x86/Intel) <b>are not supported!</b> ⚠️</i></small>
</td>
</tr>
<tr>
@@ -132,7 +132,7 @@
</tr>
<tr>
<td><b>🧠 RAM</b></td>
<td>8GB (dGPU)<sup><a href="#fn1" id="ref2">2</a></sup> /<br>12GB (iGPU)<sup><a href="#fn1" id="ref3">3</a></sup></td>
<td>8GB (dGPU) / 12GB (iGPU)<sup><a href="#fn1" id="ref1">1</a></sup></td>
<td>16 GB</td>
<td>32 GB</td>
</tr>
@@ -157,9 +157,7 @@
</tbody>
</table>
</div>
<p id="fn1"><sup>Note 1</sup> Hytale did not provide game files for macOS Intel, yet.</p>
<p id="fn2"><sup>Note 2</sup> Using Discrete/Dedicated GPU (dGPU) must have 8 GB RAM minimum.</p>
<p id="fn3"><sup>Note 3</sup> Using Integrated GPU (dGPU) must have 12 GB RAM minimum.</p>
<p id="fn1"><sup>Note 1</sup> Using Discrete/Dedicated GPU (dGPU) must have 8 GB RAM minimum, while using Integrated GPU (iGPU) must have 12 GB RAM.</p>
> [!WARNING]
> Our launcher has **not yet** supported Offline Mode (playing Hytale without internet).
@@ -169,10 +167,9 @@
### 🪟 Windows Prequisites
* **Java JDK 25:**
* [Oracle](https://www.oracle.com/java/technologies/downloads/#jdk25-windows), **no** support for Windows ARM64 in both version 25 and 21.
* [Adoptium](https://adoptium.net/temurin/releases/?version=25), has Windows ARM64 support in version 21 only.
* [Oracle](https://www.oracle.com/java/technologies/downloads/#jdk25-windows)
* [Adoptium](https://adoptium.net/temurin/releases/?version=25)
* [Microsoft](https://learn.microsoft.com/en-us/java/openjdk/download), has Windows ARM64 support in version 25.
* Download from any vendor if your OS is not Windows with ARM64 architecture.
* **Latest Visual Studio Redist:**
* Download via [Microsoft Visual C++ Redistributable](https://aka.ms/vc14/vc_redist.x64.exe)
* Or [All-in-One by Techpowerup](https://www.techpowerup.com/download/visual-c-redistributable-runtime-package-all-in-one/)
@@ -184,7 +181,7 @@
* Make sure you have already installed newest **GPU driver** especially proprietary NVIDIA, consult your distro docs or wiki.
* Also make sure that your GPU can be connected to EGL, try checking it first (again, consult your distro docs or wiki) before installing Hytale game via our launcher.
* Install `libpng` package to avoid SDL3_Image error:
* Install `libpng` package to avoid `SDL3_Image` error:
* `libpng16-16 libpng-dev` for Ubuntu/Debian-based Distro
* `libpng libpng-devel` for Fedora/RHEL-based Distro
* `libpng` for Arch-based Distro
@@ -198,16 +195,13 @@
1. **Prerequisites:** Ensure you have installed all [**Windows Prerequisites**](https://github.com/amiayweb/Hytale-F2P/tree/main?tab=readme-ov-file#-windows-prequisites) listed above.
2. **Download:** Get the latest `Hytale-F2P-Launcher.exe` from the [**Releases**](https://github.com/amiayweb/Hytale-F2P/releases/latest/) page.
3. **SmartScreen Note:** Since the executable is currently unsigned, Windows may show a "Windows protected your PC" popup.
* Click **More info**.
* Click **Run anyway**.
* Click **More info**, then click **Run anyway**.
4. **Launch:** Once installed, you can launch the app directly from your Desktop or the Start menu.
5. **Whitelist in Windows Firewall to Avoid "Server Failed to Boot" Error** [#192](https://github.com/amiayweb/Hytale-F2P/issues/192#issuecomment-3803042908)
5. **Whitelist in Windows Firewall** [#192](https://github.com/amiayweb/Hytale-F2P/issues/192#issuecomment-3803042908)
* Open the Windows Start Menu and search for `Allow an app through Windows Firewall`
* Click "Change settings" (you may need Admin privileges) and Locate `HytaleClient.exe` in the list.
* Ensure both the Private and Public checkboxes are checked. Click OK to save.
---
### 🐧 Linux Installation
1. **Prerequisites:** Ensure you have installed all [**Linux Prerequisites**](https://github.com/amiayweb/Hytale-F2P/tree/main?tab=readme-ov-file#-linux-prequisites) above.
@@ -251,8 +245,6 @@
* **Desktop Entry:** After installing via `.rpm`, `.deb`, or `.pkg.tar.zst`, the launcher should automatically appear in your App Library/Grid.
* Missing libxcrypt.so.1: Install `libxcrypt-compat` using your package manager
---
### 🍎 macOS Installation
> [!NOTE]
@@ -280,9 +272,9 @@ The `.zip` version is useful for users who prefer a portable installation or nee
---
# How to Host a Server
# 📢 How to Host a Server
## Host your Singleplayer Server (Online-Play Feature)
## 🌐 Host your Singleplayer Server (Online-Play Feature)
> [!NOTE]
> You have to play the game to host the server. See Dedicated Server section below if you want to host it without you playing as the host.
@@ -291,7 +283,7 @@ The `.zip` version is useful for users who prefer a portable installation or nee
2. Pause the game (Esc) > select Online Play > Turn on `Allow Other Players to Join` > Set password if needed > Press `Save`.
3. Check the status `Connected via STUN` or `Connected via UPnP`.
## Dedicated Server
## 🖧 Host a Dedicated Server
> [!NOTE]
> If you already have the patched `HytaleServer.jar` in `HytaleF2P/{release/pre-release}/package/game/latest/Server`, you can use it to host local dedicated server.
@@ -300,14 +292,20 @@ The `.zip` version is useful for users who prefer a portable installation or nee
> Use services like Playit.gg, Tailscale, Radmin VPN to share UDP connection if setting up router as an admin is not possible.
> [!WARNING]
> `Hytale-F2P-Server.rar` file is needed to set up a server on non-playing hardware (such as VPS/server hosting).
> `Hytale-F2P-Server.rar` file is needed to set up a server on non-playing hardware (such as VPS/server hosting). Linux ARM64 is supported for server only.
> [!IMPORTANT]
> See detailed information of setting up a server here: [SERVER.md](SERVER.md). Download the latest patched JAR, the patched RAR, or the SH/BAT scripts from channel `#open-public-server` in our Discord Server.
---
## 🛠️ Building from Source
## 🔧 Troubleshooting
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for detailed Troubleshooting guide.
---
## 🔨 Building from Source
See [BUILD.md](docs/BUILD.md) for comprehensive build instructions.

File diff suppressed because it is too large Load Diff

66
main.js
View File

@@ -176,7 +176,8 @@ function createWindow() {
initDiscordRPC();
// Configure and initialize electron-updater
autoUpdater.autoDownload = false;
// Enable auto-download so updates start immediately when available
autoUpdater.autoDownload = true;
autoUpdater.autoInstallOnAppQuit = true;
autoUpdater.on('checking-for-update', () => {
@@ -201,6 +202,20 @@ function createWindow() {
autoUpdater.on('error', (err) => {
console.error('Error in auto-updater:', err);
// Handle macOS code signing errors - requires manual download
if (mainWindow && !mainWindow.isDestroyed()) {
const isMacSigningError = process.platform === 'darwin' &&
(err.code === 'ERR_UPDATER_INVALID_SIGNATURE' ||
err.message.includes('signature') ||
err.message.includes('code sign'));
mainWindow.webContents.send('update-error', {
message: err.message,
isMacSigningError: isMacSigningError,
requiresManualDownload: isMacSigningError || process.platform === 'darwin'
});
}
});
autoUpdater.on('download-progress', (progressObj) => {
@@ -218,7 +233,10 @@ function createWindow() {
console.log('Update downloaded:', info.version);
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('update-downloaded', {
version: info.version
version: info.version,
platform: process.platform,
// macOS auto-install often fails on unsigned apps
autoInstallSupported: process.platform !== 'darwin'
});
}
});
@@ -859,6 +877,17 @@ ipcMain.handle('open-external', async (event, url) => {
}
});
ipcMain.handle('open-download-page', async () => {
try {
// Open GitHub releases page for manual download
await shell.openExternal('https://github.com/amiayweb/Hytale-F2P/releases/latest');
return { success: true };
} catch (error) {
console.error('Failed to open download page:', error);
return { success: false, error: error.message };
}
});
ipcMain.handle('open-game-location', async () => {
try {
const { getResolvedAppDir, loadVersionBranch } = require('./backend/launcher');
@@ -1086,8 +1115,37 @@ ipcMain.handle('download-update', async () => {
}
});
ipcMain.handle('install-update', () => {
autoUpdater.quitAndInstall(false, true);
ipcMain.handle('install-update', async () => {
console.log('[AutoUpdater] Installing update...');
// On macOS, quitAndInstall often fails silently
// Use a more aggressive approach
if (process.platform === 'darwin') {
console.log('[AutoUpdater] macOS detected, using force quit approach');
// Give user feedback that something is happening
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('update-installing');
}
// Small delay to show the "Installing..." state
await new Promise(resolve => setTimeout(resolve, 500));
try {
autoUpdater.quitAndInstall(false, true);
} catch (err) {
console.error('[AutoUpdater] quitAndInstall failed:', err);
// Force quit the app - the update should install on next launch
app.exit(0);
}
// If quitAndInstall didn't work, force exit after a delay
setTimeout(() => {
console.log('[AutoUpdater] Force exiting app...');
app.exit(0);
}, 2000);
} else {
autoUpdater.quitAndInstall(false, true);
}
});
ipcMain.handle('get-launcher-version', () => {

View File

@@ -24,7 +24,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
saveCloseLauncher: (enabled) => ipcRenderer.invoke('save-close-launcher', enabled),
loadCloseLauncher: () => ipcRenderer.invoke('load-close-launcher'),
// Harwadre Acceleration
// Hardware Acceleration
saveLauncherHardwareAcceleration: (enabled) => ipcRenderer.invoke('save-launcher-hw-accel', enabled),
loadLauncherHardwareAcceleration: () => ipcRenderer.invoke('load-launcher-hw-accel'),
@@ -50,14 +50,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
selectModFiles: () => ipcRenderer.invoke('select-mod-files'),
copyModFile: (sourcePath, modsPath) => ipcRenderer.invoke('copy-mod-file', sourcePath, modsPath),
onProgressUpdate: (callback) => {
ipcRenderer.on('progress-update', (event, data) => {
// Ensure data includes retry state if available
if (data && typeof data === 'object') {
callback(data);
} else {
callback(data);
}
});
ipcRenderer.on('progress-update', (event, data) => callback(data));
},
onProgressComplete: (callback) => {
ipcRenderer.on('progress-complete', () => callback());
@@ -69,7 +62,6 @@ contextBridge.exposeInMainWorld('electronAPI', {
ipcRenderer.on('installation-end', () => callback());
},
getUserId: () => ipcRenderer.invoke('get-user-id'),
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
openDownloadPage: () => ipcRenderer.invoke('open-download-page'),
getUpdateInfo: () => ipcRenderer.invoke('get-update-info'),
onUpdatePopup: (callback) => {
@@ -126,6 +118,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
downloadUpdate: () => ipcRenderer.invoke('download-update'),
installUpdate: () => ipcRenderer.invoke('install-update'),
quitAndInstallUpdate: () => ipcRenderer.invoke('install-update'), // Alias for update.js compatibility
getLauncherVersion: () => ipcRenderer.invoke('get-launcher-version'),
onUpdateAvailable: (callback) => {
ipcRenderer.on('update-available', (event, data) => callback(data));