mirror of
https://github.com/amiayweb/Hytale-F2P.git
synced 2026-02-26 09:31:45 -03:00
Merge branch 'develop' of https://github.com/amiayweb/Hytale-F2P into develop
This commit is contained in:
18
.github/README1.md
vendored
18
.github/README1.md
vendored
@@ -22,13 +22,25 @@ All builds run in parallel:
|
|||||||
|
|
||||||
### Creating a Release
|
### Creating a Release
|
||||||
|
|
||||||
1. Update version in `package.json`
|
**⚠️ IMPORTANT: Semantic Versioning Required**
|
||||||
|
|
||||||
|
This project uses **strict semantic versioning with numerical versions only**:
|
||||||
|
- ✅ **Valid**: `2.0.1`, `2.0.11`, `2.1.0`, `3.0.0`
|
||||||
|
- ❌ **Invalid**: `2.0.2b`, `2.0.2a`, `2.0.1-beta`
|
||||||
|
|
||||||
|
**Format**: `MAJOR.MINOR.PATCH` (e.g., `2.0.11`)
|
||||||
|
|
||||||
|
The auto-update system requires semantic versioning for proper version comparison. Letter suffixes are not supported.
|
||||||
|
|
||||||
|
**Steps:**
|
||||||
|
|
||||||
|
1. Update version in `package.json` (use numerical format only, e.g., `2.0.11`)
|
||||||
2. Commit and push to `main`
|
2. Commit and push to `main`
|
||||||
3. Create and push a version tag:
|
3. Create and push a version tag:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git tag v2.0.1
|
git tag v2.0.11
|
||||||
git push origin v2.0.1
|
git push origin v2.0.11
|
||||||
```
|
```
|
||||||
|
|
||||||
The workflow will:
|
The workflow will:
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -33,10 +33,11 @@ jobs:
|
|||||||
name: linux-builds
|
name: linux-builds
|
||||||
path: |
|
path: |
|
||||||
dist/*.AppImage
|
dist/*.AppImage
|
||||||
|
dist/*.AppImage.blockmap
|
||||||
dist/*.deb
|
dist/*.deb
|
||||||
dist/*.rpm
|
dist/*.rpm
|
||||||
dist/*.pacman
|
dist/*.pacman
|
||||||
dist/latest.yml
|
dist/latest-linux.yml
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
@@ -53,6 +54,7 @@ jobs:
|
|||||||
name: windows-builds
|
name: windows-builds
|
||||||
path: |
|
path: |
|
||||||
dist/*.exe
|
dist/*.exe
|
||||||
|
dist/*.exe.blockmap
|
||||||
dist/latest.yml
|
dist/latest.yml
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
|
|||||||
214
GUI/js/update.js
214
GUI/js/update.js
@@ -10,6 +10,23 @@ class ClientUpdateManager {
|
|||||||
this.showUpdatePopup(updateInfo);
|
this.showUpdatePopup(updateInfo);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Listen for electron-updater events
|
||||||
|
window.electronAPI.onUpdateAvailable((updateInfo) => {
|
||||||
|
this.showUpdatePopup(updateInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.electronAPI.onUpdateDownloadProgress((progress) => {
|
||||||
|
this.updateDownloadProgress(progress);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.electronAPI.onUpdateDownloaded((updateInfo) => {
|
||||||
|
this.showUpdateDownloaded(updateInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.electronAPI.onUpdateError((errorInfo) => {
|
||||||
|
this.handleUpdateError(errorInfo);
|
||||||
|
});
|
||||||
|
|
||||||
this.checkForUpdatesOnDemand();
|
this.checkForUpdatesOnDemand();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,23 +50,46 @@ class ClientUpdateManager {
|
|||||||
<div class="update-popup-versions">
|
<div class="update-popup-versions">
|
||||||
<div class="version-row">
|
<div class="version-row">
|
||||||
<span class="version-label">Current Version:</span>
|
<span class="version-label">Current Version:</span>
|
||||||
<span class="version-current">${updateInfo.currentVersion}</span>
|
<span class="version-current">${updateInfo.currentVersion || updateInfo.version || 'Unknown'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-row">
|
<div class="version-row">
|
||||||
<span class="version-label">New Version:</span>
|
<span class="version-label">New Version:</span>
|
||||||
<span class="version-new">${updateInfo.newVersion}</span>
|
<span class="version-new">${updateInfo.newVersion || updateInfo.version || 'Unknown'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="update-popup-message">
|
<div class="update-popup-message">
|
||||||
A new version of Hytale F2P Launcher is available.<br>
|
A new version of Hytale F2P Launcher is available.<br>
|
||||||
Please download the latest version to continue using the launcher.
|
<span id="update-status-text">Downloading update automatically...</span>
|
||||||
|
<div id="update-error-message" style="display: none; margin-top: 0.75rem; padding: 0.75rem; background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.3); border-radius: 0.5rem; color: #fca5a5; font-size: 0.875rem;">
|
||||||
|
<i class="fas fa-exclamation-triangle" style="margin-right: 0.5rem;"></i>
|
||||||
|
<span id="update-error-text"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="update-download-btn" class="update-download-btn">
|
<div id="update-progress-container" style="display: none; margin-bottom: 1rem;">
|
||||||
<i class="fas fa-external-link-alt" style="margin-right: 0.5rem;"></i>
|
<div style="display: flex; justify-content: space-between; margin-bottom: 0.5rem; font-size: 0.75rem; color: #9ca3af;">
|
||||||
Download Update
|
<span id="update-progress-percent">0%</span>
|
||||||
|
<span id="update-progress-speed">0 KB/s</span>
|
||||||
|
</div>
|
||||||
|
<div style="width: 100%; height: 8px; background: rgba(255, 255, 255, 0.1); border-radius: 4px; overflow: hidden;">
|
||||||
|
<div id="update-progress-bar" style="width: 0%; height: 100%; background: linear-gradient(90deg, #3b82f6, #9333ea); transition: width 0.3s ease;"></div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 0.5rem; font-size: 0.75rem; color: #9ca3af; text-align: center;">
|
||||||
|
<span id="update-progress-size">0 MB / 0 MB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="update-buttons-container" style="display: none;">
|
||||||
|
<button id="update-install-btn" class="update-download-btn">
|
||||||
|
<i class="fas fa-check" style="margin-right: 0.5rem;"></i>
|
||||||
|
Install & Restart
|
||||||
</button>
|
</button>
|
||||||
|
<button id="update-download-btn" class="update-download-btn update-download-btn-secondary" style="margin-top: 0.75rem;">
|
||||||
|
<i class="fas fa-external-link-alt" style="margin-right: 0.5rem;"></i>
|
||||||
|
Manually Download
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="update-popup-footer">
|
<div class="update-popup-footer">
|
||||||
This popup cannot be closed until you update the launcher
|
This popup cannot be closed until you update the launcher
|
||||||
@@ -62,6 +102,31 @@ class ClientUpdateManager {
|
|||||||
|
|
||||||
this.blockInterface();
|
this.blockInterface();
|
||||||
|
|
||||||
|
// Show progress container immediately (auto-download is enabled)
|
||||||
|
const progressContainer = document.getElementById('update-progress-container');
|
||||||
|
if (progressContainer) {
|
||||||
|
progressContainer.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
const installBtn = document.getElementById('update-install-btn');
|
||||||
|
if (installBtn) {
|
||||||
|
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();
|
||||||
|
} 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';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const downloadBtn = document.getElementById('update-download-btn');
|
const downloadBtn = document.getElementById('update-download-btn');
|
||||||
if (downloadBtn) {
|
if (downloadBtn) {
|
||||||
downloadBtn.addEventListener('click', async (e) => {
|
downloadBtn.addEventListener('click', async (e) => {
|
||||||
@@ -80,7 +145,7 @@ class ClientUpdateManager {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error opening download page:', error);
|
console.error('❌ Error opening download page:', error);
|
||||||
downloadBtn.disabled = false;
|
downloadBtn.disabled = false;
|
||||||
downloadBtn.innerHTML = '<i class="fas fa-external-link-alt" style="margin-right: 0.5rem;"></i>Download Update';
|
downloadBtn.innerHTML = '<i class="fas fa-external-link-alt" style="margin-right: 0.5rem;"></i>Manually Download';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -99,6 +164,134 @@ class ClientUpdateManager {
|
|||||||
console.log('🔔 Update popup displayed with new style');
|
console.log('🔔 Update popup displayed with new style');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateDownloadProgress(progress) {
|
||||||
|
const progressBar = document.getElementById('update-progress-bar');
|
||||||
|
const progressPercent = document.getElementById('update-progress-percent');
|
||||||
|
const progressSpeed = document.getElementById('update-progress-speed');
|
||||||
|
const progressSize = document.getElementById('update-progress-size');
|
||||||
|
|
||||||
|
if (progressBar && progress) {
|
||||||
|
const percent = Math.round(progress.percent || 0);
|
||||||
|
progressBar.style.width = `${percent}%`;
|
||||||
|
|
||||||
|
if (progressPercent) {
|
||||||
|
progressPercent.textContent = `${percent}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressSpeed && progress.bytesPerSecond) {
|
||||||
|
const speedMBps = (progress.bytesPerSecond / 1024 / 1024).toFixed(2);
|
||||||
|
progressSpeed.textContent = `${speedMBps} MB/s`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressSize && progress.transferred && progress.total) {
|
||||||
|
const transferredMB = (progress.transferred / 1024 / 1024).toFixed(2);
|
||||||
|
const totalMB = (progress.total / 1024 / 1024).toFixed(2);
|
||||||
|
progressSize.textContent = `${transferredMB} MB / ${totalMB} MB`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't update status text here - it's already set and the progress bar shows the percentage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showUpdateDownloaded(updateInfo) {
|
||||||
|
const statusText = document.getElementById('update-status-text');
|
||||||
|
const progressContainer = document.getElementById('update-progress-container');
|
||||||
|
const buttonsContainer = document.getElementById('update-buttons-container');
|
||||||
|
|
||||||
|
if (statusText) {
|
||||||
|
statusText.textContent = 'Update downloaded! Ready to install.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressContainer) {
|
||||||
|
progressContainer.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonsContainer) {
|
||||||
|
buttonsContainer.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Update downloaded, ready to install');
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUpdateError(errorInfo) {
|
||||||
|
console.error('Update error:', errorInfo);
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
message = 'Auto-update requires code signing. Please download manually.';
|
||||||
|
}
|
||||||
|
errorText.textContent = message;
|
||||||
|
errorMessage.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showManualDownloadRequired(errorInfo) {
|
||||||
|
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 errorMessage = document.getElementById('update-error-message');
|
||||||
|
const errorText = document.getElementById('update-error-text');
|
||||||
|
|
||||||
|
// Hide progress and install button
|
||||||
|
if (progressContainer) {
|
||||||
|
progressContainer.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (installBtn) {
|
||||||
|
installBtn.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update status message (only once, don't change it again)
|
||||||
|
if (statusText && !statusText.dataset.manualMode) {
|
||||||
|
statusText.textContent = 'Please download and install the update manually.';
|
||||||
|
statusText.dataset.manualMode = 'true'; // Mark that we've set manual mode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show error message with details
|
||||||
|
if (errorMessage && errorText) {
|
||||||
|
let message = 'Auto-update is not available. ';
|
||||||
|
if (errorInfo.isMacSigningError) {
|
||||||
|
message = 'This app requires code signing for automatic updates.';
|
||||||
|
} else if (errorInfo.isLinuxInstallError) {
|
||||||
|
message = 'Auto-installation requires root privileges. Please download and install the update manually using your package manager.';
|
||||||
|
} else if (errorInfo.message) {
|
||||||
|
message = errorInfo.message;
|
||||||
|
} else {
|
||||||
|
message = 'An error occurred during the update process.';
|
||||||
|
}
|
||||||
|
errorText.textContent = message;
|
||||||
|
errorMessage.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show and enable the manual download button (make it primary since it's the only option)
|
||||||
|
if (downloadBtn) {
|
||||||
|
downloadBtn.style.display = 'block';
|
||||||
|
downloadBtn.disabled = false;
|
||||||
|
downloadBtn.classList.remove('update-download-btn-secondary');
|
||||||
|
downloadBtn.innerHTML = '<i class="fas fa-external-link-alt" style="margin-right: 0.5rem;"></i>Download Update Manually';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show buttons container if not already visible
|
||||||
|
if (buttonsContainer) {
|
||||||
|
buttonsContainer.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('⚠️ Manual download required due to update error');
|
||||||
|
}
|
||||||
|
|
||||||
blockInterface() {
|
blockInterface() {
|
||||||
const mainContent = document.querySelector('.flex.w-full.h-screen');
|
const mainContent = document.querySelector('.flex.w-full.h-screen');
|
||||||
if (mainContent) {
|
if (mainContent) {
|
||||||
@@ -144,7 +337,12 @@ class ClientUpdateManager {
|
|||||||
async checkForUpdatesOnDemand() {
|
async checkForUpdatesOnDemand() {
|
||||||
try {
|
try {
|
||||||
const updateInfo = await window.electronAPI.checkForUpdates();
|
const updateInfo = await window.electronAPI.checkForUpdates();
|
||||||
if (updateInfo.updateAvailable) {
|
|
||||||
|
// Double-check that versions are actually different before showing popup
|
||||||
|
if (updateInfo.updateAvailable &&
|
||||||
|
updateInfo.newVersion &&
|
||||||
|
updateInfo.currentVersion &&
|
||||||
|
updateInfo.newVersion !== updateInfo.currentVersion) {
|
||||||
this.showUpdatePopup(updateInfo);
|
this.showUpdatePopup(updateInfo);
|
||||||
}
|
}
|
||||||
return updateInfo;
|
return updateInfo;
|
||||||
|
|||||||
@@ -4571,6 +4571,27 @@ select.settings-input option {
|
|||||||
0 0 0 1px rgba(255, 255, 255, 0.05) !important;
|
0 0 0 1px rgba(255, 255, 255, 0.05) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.update-download-btn-secondary {
|
||||||
|
background: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 2px 8px rgba(0, 0, 0, 0.2),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-download-btn-secondary:hover:not(:disabled) {
|
||||||
|
background: rgba(255, 255, 255, 0.15) !important;
|
||||||
|
border-color: rgba(255, 255, 255, 0.3) !important;
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 4px 12px rgba(0, 0, 0, 0.3),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-download-btn-secondary:active:not(:disabled) {
|
||||||
|
transform: translateY(0) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.update-popup-footer {
|
.update-popup-footer {
|
||||||
text-align: center !important;
|
text-align: center !important;
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -76,6 +76,25 @@ See [BUILD.md](BUILD.md) for comprehensive build instructions.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 📌 Versioning Policy
|
||||||
|
|
||||||
|
**⚠️ Important: Semantic Versioning Required**
|
||||||
|
|
||||||
|
This project follows **strict semantic versioning** with **numerical versions only**:
|
||||||
|
|
||||||
|
- ✅ **Valid**: `2.0.1`, `2.0.11`, `2.1.0`, `3.0.0`
|
||||||
|
- ❌ **Invalid**: `2.0.2b`, `2.0.2a`, `2.0.1-beta`, `v2.0.2b`
|
||||||
|
|
||||||
|
**Format**: `MAJOR.MINOR.PATCH` (e.g., `2.0.11`)
|
||||||
|
|
||||||
|
- **MAJOR**: Breaking changes
|
||||||
|
- **MINOR**: New features (backward compatible)
|
||||||
|
- **PATCH**: Bug fixes (backward compatible)
|
||||||
|
|
||||||
|
**Why?** The auto-update system requires semantic versioning for proper version comparison. Letter suffixes (like `2.0.2b`) are not supported and will cause update detection issues.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 📋 Changelog
|
## 📋 Changelog
|
||||||
|
|
||||||
### 🆕 v2.0.2b *(Minor Update: Performance & Utilities)*
|
### 🆕 v2.0.2b *(Minor Update: Performance & Utilities)*
|
||||||
|
|||||||
379
backend/appUpdater.js
Normal file
379
backend/appUpdater.js
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
const { autoUpdater } = require('electron-updater');
|
||||||
|
const { app } = require('electron');
|
||||||
|
const logger = require('./logger');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
|
class AppUpdater {
|
||||||
|
constructor(mainWindow) {
|
||||||
|
this.mainWindow = mainWindow;
|
||||||
|
this.autoUpdateAvailable = true; // Track if auto-update is possible
|
||||||
|
this.updateAvailable = false; // Track if an update was detected
|
||||||
|
this.updateVersion = null; // Store the available update version
|
||||||
|
this.setupAutoUpdater();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupAutoUpdater() {
|
||||||
|
// Enable dev mode for testing (reads dev-app-update.yml)
|
||||||
|
// Only enable in development, not in production builds
|
||||||
|
if (process.env.NODE_ENV === 'development' || !app.isPackaged) {
|
||||||
|
autoUpdater.forceDevUpdateConfig = true;
|
||||||
|
console.log('Dev update mode enabled - using dev-app-update.yml');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure logger for electron-updater
|
||||||
|
// Create a compatible logger interface
|
||||||
|
autoUpdater.logger = {
|
||||||
|
info: (...args) => logger.info(...args),
|
||||||
|
warn: (...args) => logger.warn(...args),
|
||||||
|
error: (...args) => logger.error(...args),
|
||||||
|
debug: (...args) => logger.log(...args)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Auto download updates
|
||||||
|
autoUpdater.autoDownload = true;
|
||||||
|
// Auto install on quit (after download)
|
||||||
|
autoUpdater.autoInstallOnAppQuit = true;
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
autoUpdater.on('checking-for-update', () => {
|
||||||
|
console.log('Checking for updates...');
|
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||||
|
this.mainWindow.webContents.send('update-checking');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-available', (info) => {
|
||||||
|
console.log('Update available:', info.version);
|
||||||
|
const currentVersion = app.getVersion();
|
||||||
|
const newVersion = info.version;
|
||||||
|
|
||||||
|
// Only proceed if the new version is actually different from current
|
||||||
|
if (newVersion === currentVersion) {
|
||||||
|
console.log('Update version matches current version, ignoring update-available event');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateAvailable = true;
|
||||||
|
this.updateVersion = newVersion;
|
||||||
|
this.autoUpdateAvailable = true; // Reset flag when new update is available
|
||||||
|
|
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||||
|
this.mainWindow.webContents.send('update-available', {
|
||||||
|
version: newVersion,
|
||||||
|
newVersion: newVersion,
|
||||||
|
currentVersion: currentVersion,
|
||||||
|
releaseName: info.releaseName,
|
||||||
|
releaseNotes: info.releaseNotes
|
||||||
|
});
|
||||||
|
// Also send to the old popup handler for compatibility
|
||||||
|
this.mainWindow.webContents.send('show-update-popup', {
|
||||||
|
currentVersion: currentVersion,
|
||||||
|
newVersion: newVersion,
|
||||||
|
version: newVersion
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-not-available', (info) => {
|
||||||
|
console.log('Update not available. Current version is latest.');
|
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||||
|
this.mainWindow.webContents.send('update-not-available', {
|
||||||
|
version: info.version
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('error', (err) => {
|
||||||
|
console.error('Error in auto-updater:', err);
|
||||||
|
|
||||||
|
// Check if this is a network error (not critical, don't show UI)
|
||||||
|
const errorMessage = err.message?.toLowerCase() || '';
|
||||||
|
const isNetworkError = errorMessage.includes('err_name_not_resolved') ||
|
||||||
|
errorMessage.includes('network') ||
|
||||||
|
errorMessage.includes('connection') ||
|
||||||
|
errorMessage.includes('timeout') ||
|
||||||
|
errorMessage.includes('enotfound');
|
||||||
|
|
||||||
|
if (isNetworkError) {
|
||||||
|
console.warn('Network error in auto-updater - will retry later. Not showing error UI.');
|
||||||
|
return; // Don't show error UI for network issues
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle SHA512 checksum mismatch - this can happen during updates, just retry
|
||||||
|
const isChecksumError = err.code === 'ERR_CHECKSUM_MISMATCH' ||
|
||||||
|
errorMessage.includes('sha512') ||
|
||||||
|
errorMessage.includes('checksum') ||
|
||||||
|
errorMessage.includes('mismatch');
|
||||||
|
|
||||||
|
if (isChecksumError) {
|
||||||
|
console.warn('SHA512 checksum mismatch detected - clearing cache and will retry automatically. This is normal during updates.');
|
||||||
|
// Clear the update cache and let it re-download
|
||||||
|
this.clearUpdateCache();
|
||||||
|
|
||||||
|
// Don't show error UI - just log and let it retry automatically on next check
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if this is a critical error that prevents auto-update
|
||||||
|
const isCriticalError = this.isCriticalUpdateError(err);
|
||||||
|
|
||||||
|
if (isCriticalError) {
|
||||||
|
this.autoUpdateAvailable = false;
|
||||||
|
console.warn('Auto-update failed. Manual download required.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle missing metadata files (platform-specific builds)
|
||||||
|
if (err.code === 'ERR_UPDATER_CHANNEL_FILE_NOT_FOUND') {
|
||||||
|
const platform = process.platform === 'darwin' ? 'macOS' :
|
||||||
|
process.platform === 'win32' ? 'Windows' : 'Linux';
|
||||||
|
const missingFile = process.platform === 'darwin' ? 'latest-mac.yml' :
|
||||||
|
process.platform === 'win32' ? 'latest.yml' : 'latest-linux.yml';
|
||||||
|
console.warn(`${platform} update metadata file (${missingFile}) not found in release.`);
|
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||||
|
this.mainWindow.webContents.send('update-error', {
|
||||||
|
message: `Update metadata file for ${platform} not found in release. Please download manually.`,
|
||||||
|
code: err.code,
|
||||||
|
requiresManualDownload: true,
|
||||||
|
updateVersion: this.updateVersion,
|
||||||
|
isMissingMetadata: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linux-specific: Handle installation permission errors
|
||||||
|
if (process.platform === 'linux') {
|
||||||
|
const errorMessage = err.message?.toLowerCase() || '';
|
||||||
|
const errorStack = err.stack?.toLowerCase() || '';
|
||||||
|
const isInstallError = errorMessage.includes('pkexec') ||
|
||||||
|
errorMessage.includes('gksudo') ||
|
||||||
|
errorMessage.includes('kdesudo') ||
|
||||||
|
errorMessage.includes('setuid root') ||
|
||||||
|
errorMessage.includes('exited with code 127') ||
|
||||||
|
errorStack.includes('pacmanupdater') ||
|
||||||
|
errorStack.includes('doinstall') ||
|
||||||
|
errorMessage.includes('installation failed');
|
||||||
|
|
||||||
|
if (isInstallError) {
|
||||||
|
console.warn('Linux installation error: Package installation requires root privileges. Manual installation required.');
|
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||||
|
this.mainWindow.webContents.send('update-error', {
|
||||||
|
message: 'Auto-installation requires root privileges. Please download and install the update manually.',
|
||||||
|
code: err.code || 'ERR_LINUX_INSTALL_PERMISSION',
|
||||||
|
isLinuxInstallError: true,
|
||||||
|
requiresManualDownload: true,
|
||||||
|
updateVersion: this.updateVersion
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// macOS-specific: Handle unsigned app errors gracefully
|
||||||
|
if (process.platform === 'darwin' && err.code === 2) {
|
||||||
|
console.warn('macOS update error: App may not be code-signed. Auto-update requires code signing.');
|
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||||
|
this.mainWindow.webContents.send('update-error', {
|
||||||
|
message: 'Auto-update requires code signing. Please download manually from GitHub.',
|
||||||
|
code: err.code,
|
||||||
|
isMacSigningError: true,
|
||||||
|
requiresManualDownload: true,
|
||||||
|
updateVersion: this.updateVersion
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||||
|
this.mainWindow.webContents.send('update-error', {
|
||||||
|
message: err.message,
|
||||||
|
code: err.code,
|
||||||
|
requiresManualDownload: isCriticalError,
|
||||||
|
updateVersion: this.updateVersion
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('download-progress', (progressObj) => {
|
||||||
|
const message = `Download speed: ${progressObj.bytesPerSecond} - Downloaded ${progressObj.percent}% (${progressObj.transferred}/${progressObj.total})`;
|
||||||
|
console.log(message);
|
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||||
|
this.mainWindow.webContents.send('update-download-progress', {
|
||||||
|
percent: progressObj.percent,
|
||||||
|
bytesPerSecond: progressObj.bytesPerSecond,
|
||||||
|
transferred: progressObj.transferred,
|
||||||
|
total: progressObj.total
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-downloaded', (info) => {
|
||||||
|
console.log('Update downloaded:', info.version);
|
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||||
|
this.mainWindow.webContents.send('update-downloaded', {
|
||||||
|
version: info.version,
|
||||||
|
releaseName: info.releaseName,
|
||||||
|
releaseNotes: info.releaseNotes
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkForUpdatesAndNotify() {
|
||||||
|
// Check for updates and notify if available
|
||||||
|
autoUpdater.checkForUpdatesAndNotify().catch(err => {
|
||||||
|
console.error('Failed to check for updates:', err);
|
||||||
|
|
||||||
|
// Network errors are not critical - just log and continue
|
||||||
|
const errorMessage = err.message?.toLowerCase() || '';
|
||||||
|
const isNetworkError = errorMessage.includes('err_name_not_resolved') ||
|
||||||
|
errorMessage.includes('network') ||
|
||||||
|
errorMessage.includes('connection') ||
|
||||||
|
errorMessage.includes('timeout') ||
|
||||||
|
errorMessage.includes('enotfound');
|
||||||
|
|
||||||
|
if (isNetworkError) {
|
||||||
|
console.warn('Network error checking for updates - will retry later. This is not critical.');
|
||||||
|
return; // Don't show error UI for network issues
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCritical = this.isCriticalUpdateError(err);
|
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed() && isCritical) {
|
||||||
|
this.mainWindow.webContents.send('update-error', {
|
||||||
|
message: err.message || 'Failed to check for updates',
|
||||||
|
code: err.code,
|
||||||
|
requiresManualDownload: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkForUpdates() {
|
||||||
|
// Manual check for updates (returns promise)
|
||||||
|
return autoUpdater.checkForUpdates().catch(err => {
|
||||||
|
console.error('Failed to check for updates:', err);
|
||||||
|
|
||||||
|
// Network errors are not critical - just return no update available
|
||||||
|
const errorMessage = err.message?.toLowerCase() || '';
|
||||||
|
const isNetworkError = errorMessage.includes('err_name_not_resolved') ||
|
||||||
|
errorMessage.includes('network') ||
|
||||||
|
errorMessage.includes('connection') ||
|
||||||
|
errorMessage.includes('timeout') ||
|
||||||
|
errorMessage.includes('enotfound');
|
||||||
|
|
||||||
|
if (isNetworkError) {
|
||||||
|
console.warn('Network error - update check unavailable');
|
||||||
|
return { updateInfo: null }; // Return empty result for network errors
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCritical = this.isCriticalUpdateError(err);
|
||||||
|
if (isCritical) {
|
||||||
|
this.autoUpdateAvailable = false;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
quitAndInstall() {
|
||||||
|
// Quit and install the update
|
||||||
|
autoUpdater.quitAndInstall(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUpdateInfo() {
|
||||||
|
return {
|
||||||
|
currentVersion: app.getVersion(),
|
||||||
|
updateAvailable: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
clearUpdateCache() {
|
||||||
|
try {
|
||||||
|
// Get the cache directory based on platform
|
||||||
|
const cacheDir = process.platform === 'darwin'
|
||||||
|
? path.join(os.homedir(), 'Library', 'Caches', `${app.getName()}-updater`)
|
||||||
|
: process.platform === 'win32'
|
||||||
|
? path.join(os.homedir(), 'AppData', 'Local', `${app.getName()}-updater`)
|
||||||
|
: path.join(os.homedir(), '.cache', `${app.getName()}-updater`);
|
||||||
|
|
||||||
|
if (fs.existsSync(cacheDir)) {
|
||||||
|
fs.rmSync(cacheDir, { recursive: true, force: true });
|
||||||
|
console.log('Update cache cleared successfully');
|
||||||
|
} else {
|
||||||
|
console.log('Update cache directory does not exist');
|
||||||
|
}
|
||||||
|
} catch (cacheError) {
|
||||||
|
console.warn('Could not clear update cache:', cacheError.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isCriticalUpdateError(err) {
|
||||||
|
// Check for errors that prevent auto-update
|
||||||
|
const errorMessage = err.message?.toLowerCase() || '';
|
||||||
|
const errorCode = err.code;
|
||||||
|
|
||||||
|
// Missing update metadata files (platform-specific)
|
||||||
|
if (errorCode === 'ERR_UPDATER_CHANNEL_FILE_NOT_FOUND' ||
|
||||||
|
errorMessage.includes('cannot find latest') ||
|
||||||
|
errorMessage.includes('latest-linux.yml') ||
|
||||||
|
errorMessage.includes('latest-mac.yml') ||
|
||||||
|
errorMessage.includes('latest.yml')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// macOS code signing errors
|
||||||
|
if (process.platform === 'darwin' && (errorCode === 2 || errorMessage.includes('shipit'))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download failures
|
||||||
|
if (errorMessage.includes('download') && errorMessage.includes('fail')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network errors that prevent download (but we handle these separately as non-critical)
|
||||||
|
// Installation errors
|
||||||
|
if (errorMessage.includes('install') && errorMessage.includes('fail')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permission errors
|
||||||
|
if (errorMessage.includes('permission') || errorMessage.includes('access denied')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linux installation errors (pkexec, sudo issues)
|
||||||
|
if (process.platform === 'linux' && (
|
||||||
|
errorMessage.includes('pkexec') ||
|
||||||
|
errorMessage.includes('setuid root') ||
|
||||||
|
errorMessage.includes('exited with code 127') ||
|
||||||
|
errorMessage.includes('gksudo') ||
|
||||||
|
errorMessage.includes('kdesudo'))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File system errors (but not "not found" for metadata files - handled above)
|
||||||
|
if (errorMessage.includes('enoent') || errorMessage.includes('cannot find')) {
|
||||||
|
// Only if it's not about metadata files
|
||||||
|
if (!errorMessage.includes('latest') && !errorMessage.includes('.yml')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic critical error codes (but not checksum errors - those are handled separately)
|
||||||
|
if (errorCode && (errorCode >= 100 ||
|
||||||
|
errorCode === 'ERR_UPDATER_INVALID_RELEASE_FEED' ||
|
||||||
|
errorCode === 'ERR_UPDATER_CHANNEL_FILE_NOT_FOUND')) {
|
||||||
|
// Don't treat checksum errors as critical - they're handled separately
|
||||||
|
if (errorCode === 'ERR_CHECKSUM_MISMATCH') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AppUpdater;
|
||||||
3
dev-app-update.yml
Normal file
3
dev-app-update.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
provider: github
|
||||||
|
owner: amiayweb # Change to your own GitHub username
|
||||||
|
repo: Hytale-F2P
|
||||||
284
docs/AUTO-UPDATES.md
Normal file
284
docs/AUTO-UPDATES.md
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
# Auto-Updates System
|
||||||
|
|
||||||
|
This document explains how the automatic update system works in the Hytale F2P Launcher.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The launcher uses [electron-updater](https://www.electron.build/auto-update) to automatically check for, download, and install updates. When a new version is available, users are notified and the update is downloaded in the background.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### 1. Update Checking
|
||||||
|
|
||||||
|
- **Automatic Check**: The app automatically checks for updates 3 seconds after startup
|
||||||
|
- **Manual Check**: Users can manually check for updates through the UI
|
||||||
|
- **Update Source**: Updates are fetched from GitHub Releases
|
||||||
|
|
||||||
|
### 2. Update Process
|
||||||
|
|
||||||
|
1. **Check for Updates**: The app queries GitHub Releases for a newer version
|
||||||
|
2. **Notify User**: If an update is available, the user is notified via the UI
|
||||||
|
3. **Download**: The update is automatically downloaded in the background
|
||||||
|
4. **Progress Tracking**: Download progress is shown to the user
|
||||||
|
5. **Install**: When the download completes, the user can choose to install immediately or wait until the app restarts
|
||||||
|
|
||||||
|
### 3. Installation
|
||||||
|
|
||||||
|
- Updates are installed when the app quits (if `autoInstallOnAppQuit` is enabled)
|
||||||
|
- Users can also manually trigger installation through the UI
|
||||||
|
- The app will restart automatically after installation
|
||||||
|
|
||||||
|
## Version Detection & Comparison
|
||||||
|
|
||||||
|
### Current Version Source
|
||||||
|
|
||||||
|
The app's current version is read from `package.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "2.0.2b"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This version is embedded into the built application and is accessible via `app.getVersion()` in Electron. When the app is built, electron-builder also creates an internal `app-update.yml` file in the app's resources that contains this version information.
|
||||||
|
|
||||||
|
### How Version Detection Works
|
||||||
|
|
||||||
|
1. **Current Version**: The app knows its own version from `package.json`, which is:
|
||||||
|
- Read at build time
|
||||||
|
- Embedded in the application binary
|
||||||
|
- Stored in the app's metadata
|
||||||
|
|
||||||
|
2. **Fetching Latest Version**: When checking for updates, electron-updater:
|
||||||
|
- Queries the GitHub Releases API: `https://api.github.com/repos/amiayweb/Hytale-F2P/releases/latest`
|
||||||
|
- Or reads the update metadata file: `https://github.com/amiayweb/Hytale-F2P/releases/download/latest/latest.yml` (or `latest-mac.yml` for macOS)
|
||||||
|
- The metadata file contains:
|
||||||
|
```yaml
|
||||||
|
version: 2.0.3
|
||||||
|
releaseDate: '2024-01-15T10:30:00.000Z'
|
||||||
|
path: Hytale-F2P-Launcher-2.0.3-x64.exe
|
||||||
|
sha512: ...
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Version Comparison**: electron-updater uses semantic versioning comparison:
|
||||||
|
- Compares the **current version** (from `package.json`) with the **latest version** (from GitHub Releases)
|
||||||
|
- Uses semantic versioning rules: `major.minor.patch` (e.g., `2.0.2` vs `2.0.3`)
|
||||||
|
- An update is available if the remote version is **greater than** the current version
|
||||||
|
- Examples:
|
||||||
|
- Current: `2.0.2` → Remote: `2.0.3` ✅ Update available
|
||||||
|
- Current: `2.0.2` → Remote: `2.0.2` ❌ No update (same version)
|
||||||
|
- Current: `2.0.3` → Remote: `2.0.2` ❌ No update (current is newer)
|
||||||
|
- Current: `2.0.2b` → Remote: `2.0.3` ✅ Update available (prerelease tags are handled)
|
||||||
|
|
||||||
|
4. **Version Format Handling**:
|
||||||
|
- **Semantic versions** (e.g., `1.0.0`, `2.1.3`) are compared numerically
|
||||||
|
- **Prerelease versions** (e.g., `2.0.2b`, `2.0.2-beta`) are compared with special handling
|
||||||
|
- **Non-semantic versions** may cause issues - it's recommended to use semantic versioning
|
||||||
|
|
||||||
|
### Update Metadata Files
|
||||||
|
|
||||||
|
When you build and publish a release, electron-builder generates platform-specific metadata files:
|
||||||
|
|
||||||
|
**Windows/Linux** (`latest.yml`):
|
||||||
|
```yaml
|
||||||
|
version: 2.0.3
|
||||||
|
files:
|
||||||
|
- url: Hytale-F2P-Launcher-2.0.3-x64.exe
|
||||||
|
sha512: abc123...
|
||||||
|
size: 12345678
|
||||||
|
path: Hytale-F2P-Launcher-2.0.3-x64.exe
|
||||||
|
sha512: abc123...
|
||||||
|
releaseDate: '2024-01-15T10:30:00.000Z'
|
||||||
|
```
|
||||||
|
|
||||||
|
**macOS** (`latest-mac.yml`):
|
||||||
|
```yaml
|
||||||
|
version: 2.0.3
|
||||||
|
files:
|
||||||
|
- url: Hytale-F2P-Launcher-2.0.3-arm64-mac.zip
|
||||||
|
sha512: def456...
|
||||||
|
size: 23456789
|
||||||
|
path: Hytale-F2P-Launcher-2.0.3-arm64-mac.zip
|
||||||
|
sha512: def456...
|
||||||
|
releaseDate: '2024-01-15T10:30:00.000Z'
|
||||||
|
```
|
||||||
|
|
||||||
|
These files are:
|
||||||
|
- Automatically generated during build
|
||||||
|
- Uploaded to GitHub Releases
|
||||||
|
- Fetched by electron-updater to check for updates
|
||||||
|
- Used to determine if an update is available and what to download
|
||||||
|
|
||||||
|
### The Check Process in Detail
|
||||||
|
|
||||||
|
When `appUpdater.checkForUpdatesAndNotify()` is called:
|
||||||
|
|
||||||
|
1. **Read Current Version**: Gets version from `app.getVersion()` (which reads from `package.json`)
|
||||||
|
2. **Fetch Update Info**:
|
||||||
|
- Makes HTTP request to GitHub Releases API or reads `latest.yml`
|
||||||
|
- Gets the version number from the metadata
|
||||||
|
3. **Compare Versions**:
|
||||||
|
- Uses semantic versioning comparison (e.g., `semver.gt(remoteVersion, currentVersion)`)
|
||||||
|
- If remote > current: update available
|
||||||
|
- If remote <= current: no update
|
||||||
|
4. **Emit Events**:
|
||||||
|
- `update-available` if newer version found
|
||||||
|
- `update-not-available` if already up to date
|
||||||
|
5. **Download if Available**: If `autoDownload` is enabled, starts downloading automatically
|
||||||
|
|
||||||
|
### Example Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
App Version: 2.0.2 (from package.json)
|
||||||
|
↓
|
||||||
|
Check GitHub Releases API
|
||||||
|
↓
|
||||||
|
Latest Release: 2.0.3
|
||||||
|
↓
|
||||||
|
Compare: 2.0.3 > 2.0.2? YES
|
||||||
|
↓
|
||||||
|
Emit: 'update-available' event
|
||||||
|
↓
|
||||||
|
Download update automatically
|
||||||
|
↓
|
||||||
|
Emit: 'update-downloaded' event
|
||||||
|
↓
|
||||||
|
User can install on next restart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### AppUpdater Class (`backend/appUpdater.js`)
|
||||||
|
|
||||||
|
The main class that handles all update operations:
|
||||||
|
|
||||||
|
- **`checkForUpdatesAndNotify()`**: Checks for updates and shows a system notification if available
|
||||||
|
- **`checkForUpdates()`**: Manually checks for updates (returns a promise)
|
||||||
|
- **`quitAndInstall()`**: Quits the app and installs the downloaded update
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
The AppUpdater emits the following events that the UI can listen to:
|
||||||
|
|
||||||
|
- `update-checking`: Update check has started
|
||||||
|
- `update-available`: A new update is available
|
||||||
|
- `update-not-available`: App is up to date
|
||||||
|
- `update-download-progress`: Download progress updates
|
||||||
|
- `update-downloaded`: Update has finished downloading
|
||||||
|
- `update-error`: An error occurred during the update process
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Package.json
|
||||||
|
|
||||||
|
The publish configuration in `package.json` tells electron-builder where to publish updates:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"publish": {
|
||||||
|
"provider": "github",
|
||||||
|
"owner": "amiayweb",
|
||||||
|
"repo": "Hytale-F2P"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This means updates will be fetched from GitHub Releases for the `amiayweb/Hytale-F2P` repository.
|
||||||
|
|
||||||
|
## Publishing Updates
|
||||||
|
|
||||||
|
### For Developers
|
||||||
|
|
||||||
|
1. **Update Version**: Bump the version in `package.json` (e.g., `2.0.2b` → `2.0.3`)
|
||||||
|
|
||||||
|
2. **Build the App**: Run the build command for your platform:
|
||||||
|
```bash
|
||||||
|
npm run build:win # Windows
|
||||||
|
npm run build:mac # macOS
|
||||||
|
npm run build:linux # Linux
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Publish to GitHub**: When building with electron-builder, it will:
|
||||||
|
- Generate update metadata files (`latest.yml`, `latest-mac.yml`, etc.)
|
||||||
|
- Upload the built files to GitHub Releases (if configured with `GH_TOKEN`)
|
||||||
|
- Make them available for auto-update
|
||||||
|
|
||||||
|
4. **Release on GitHub**: Create a GitHub Release with the new version tag
|
||||||
|
|
||||||
|
### Important Notes
|
||||||
|
|
||||||
|
- **macOS Code Signing**: macOS apps **must** be code-signed for auto-updates to work
|
||||||
|
- **Version Format**: Use semantic versioning (e.g., `1.0.0`, `2.0.1`) for best compatibility
|
||||||
|
- **Update Files**: electron-builder automatically generates the required metadata files (`latest.yml`, etc.)
|
||||||
|
|
||||||
|
## Testing Updates
|
||||||
|
|
||||||
|
### Development Mode
|
||||||
|
|
||||||
|
To test updates during development, create a `dev-app-update.yml` file in the project root:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
owner: amiayweb
|
||||||
|
repo: Hytale-F2P
|
||||||
|
provider: github
|
||||||
|
```
|
||||||
|
|
||||||
|
Then enable dev mode in the code:
|
||||||
|
```javascript
|
||||||
|
autoUpdater.forceDevUpdateConfig = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local Testing
|
||||||
|
|
||||||
|
For local testing, you can use a local server (like Minio) or a generic HTTP server to host update files.
|
||||||
|
|
||||||
|
## User Experience
|
||||||
|
|
||||||
|
### What Users See
|
||||||
|
|
||||||
|
1. **On Startup**: The app silently checks for updates in the background
|
||||||
|
2. **Update Available**: A notification appears if an update is found
|
||||||
|
3. **Downloading**: Progress bar shows download status
|
||||||
|
4. **Ready to Install**: User is notified when the update is ready
|
||||||
|
5. **Installation**: Update installs on app restart or when user clicks "Install Now"
|
||||||
|
|
||||||
|
### User Actions
|
||||||
|
|
||||||
|
- Users can manually check for updates through the settings/update menu
|
||||||
|
- Users can choose to install immediately or wait until next app launch
|
||||||
|
- Users can continue using the app while updates download in the background
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Updates Not Working
|
||||||
|
|
||||||
|
1. **Check GitHub Releases**: Ensure releases are published on GitHub
|
||||||
|
2. **Check Version**: Make sure the version in `package.json` is higher than the current release
|
||||||
|
3. **Check Logs**: Check the app logs for update-related errors
|
||||||
|
4. **Code Signing (macOS)**: Verify the app is properly code-signed
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
- **"Update not available"**: Version in `package.json` may not be higher than the current release
|
||||||
|
- **"Download failed"**: Network issues or GitHub API rate limits
|
||||||
|
- **"Installation failed"**: Permissions issue or app is running from an unsupported location
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Supported Platforms
|
||||||
|
|
||||||
|
- **Windows**: NSIS installer (auto-update supported)
|
||||||
|
- **macOS**: DMG + ZIP (auto-update supported, requires code signing)
|
||||||
|
- **Linux**: AppImage, DEB, RPM, Pacman (auto-update supported)
|
||||||
|
|
||||||
|
### Update Files Generated
|
||||||
|
|
||||||
|
When building, electron-builder generates:
|
||||||
|
- `latest.yml` (Windows/Linux)
|
||||||
|
- `latest-mac.yml` (macOS)
|
||||||
|
- `latest-linux.yml` (Linux)
|
||||||
|
|
||||||
|
These files contain metadata about the latest release and are automatically uploaded to GitHub Releases.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [electron-updater Documentation](https://www.electron.build/auto-update)
|
||||||
|
- [electron-builder Auto Update Guide](https://www.electron.build/auto-update)
|
||||||
78
docs/CLEAR-UPDATE-CACHE.md
Normal file
78
docs/CLEAR-UPDATE-CACHE.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# Clearing Electron-Updater Cache
|
||||||
|
|
||||||
|
To force electron-updater to re-download an update file, you need to clear the cached download.
|
||||||
|
|
||||||
|
## Quick Method (Terminal)
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
```bash
|
||||||
|
# Remove the entire cache directory
|
||||||
|
rm -rf ~/Library/Caches/hytale-f2p-launcher
|
||||||
|
|
||||||
|
# Or just remove pending downloads
|
||||||
|
rm -rf ~/Library/Caches/hytale-f2p-launcher/pending
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
```bash
|
||||||
|
# Remove the entire cache directory
|
||||||
|
rmdir /s "%LOCALAPPDATA%\hytale-f2p-launcher-updater"
|
||||||
|
|
||||||
|
# Or just remove pending downloads
|
||||||
|
rmdir /s "%LOCALAPPDATA%\hytale-f2p-launcher-updater\pending"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
```bash
|
||||||
|
# Remove the entire cache directory
|
||||||
|
rm -rf ~/.cache/hytale-f2p-launcher-updater
|
||||||
|
|
||||||
|
# Or just remove pending downloads
|
||||||
|
rm -rf ~/.cache/hytale-f2p-launcher-updater/pending
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cache Locations
|
||||||
|
|
||||||
|
electron-updater stores downloaded updates in:
|
||||||
|
|
||||||
|
- **macOS**: `~/Library/Caches/hytale-f2p-launcher/`
|
||||||
|
- **Windows**: `%LOCALAPPDATA%\hytale-f2p-launcher-updater\`
|
||||||
|
- **Linux**: `~/.cache/hytale-f2p-launcher-updater/`
|
||||||
|
|
||||||
|
The cache typically contains:
|
||||||
|
- `pending/` - Downloaded update files waiting to be installed
|
||||||
|
- Metadata files about available updates
|
||||||
|
|
||||||
|
## After Clearing
|
||||||
|
|
||||||
|
After clearing the cache:
|
||||||
|
1. Restart the launcher
|
||||||
|
2. It will check for updates again
|
||||||
|
3. The update will be re-downloaded from scratch
|
||||||
|
|
||||||
|
## Programmatic Method
|
||||||
|
|
||||||
|
You can also clear the cache programmatically by adding this to your code:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const { autoUpdater } = require('electron-updater');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
|
function clearUpdateCache() {
|
||||||
|
const cacheDir = path.join(
|
||||||
|
os.homedir(),
|
||||||
|
process.platform === 'win32'
|
||||||
|
? 'AppData/Local/hytale-f2p-launcher-updater'
|
||||||
|
: process.platform === 'darwin'
|
||||||
|
? 'Library/Caches/hytale-f2p-launcher'
|
||||||
|
: '.cache/hytale-f2p-launcher-updater'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fs.existsSync(cacheDir)) {
|
||||||
|
fs.rmSync(cacheDir, { recursive: true, force: true });
|
||||||
|
console.log('Update cache cleared');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
196
docs/TESTING-UPDATES.md
Normal file
196
docs/TESTING-UPDATES.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# Testing Auto-Updates
|
||||||
|
|
||||||
|
This guide explains how to test the auto-update system during development.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Option 1: Test with GitHub Releases (Easiest)
|
||||||
|
|
||||||
|
1. **Set up dev-app-update.yml** (already done):
|
||||||
|
```yaml
|
||||||
|
provider: github
|
||||||
|
owner: amiayweb
|
||||||
|
repo: Hytale-F2P
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Lower your current version** in `package.json`:
|
||||||
|
- Change version to something lower than what's on GitHub (e.g., `2.0.1` if GitHub has `2.0.3`)
|
||||||
|
|
||||||
|
3. **Run the app in dev mode**:
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **The app will check for updates** 3 seconds after startup
|
||||||
|
- If a newer version exists on GitHub, it will detect it
|
||||||
|
- Check the console logs for update messages
|
||||||
|
|
||||||
|
### Option 2: Test with Local HTTP Server
|
||||||
|
|
||||||
|
For more control, you can set up a local server:
|
||||||
|
|
||||||
|
1. **Create a test update server**:
|
||||||
|
```bash
|
||||||
|
# Create a test directory
|
||||||
|
mkdir -p test-updates
|
||||||
|
cd test-updates
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Build a test version** with a higher version number:
|
||||||
|
```bash
|
||||||
|
# In package.json, set version to 2.0.4
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Copy the generated files** to your test server:
|
||||||
|
- Copy `dist/latest.yml` (or `latest-mac.yml` for macOS)
|
||||||
|
- Copy the built installer/package
|
||||||
|
|
||||||
|
4. **Start a simple HTTP server**:
|
||||||
|
```bash
|
||||||
|
# Using Python
|
||||||
|
python3 -m http.server 8080
|
||||||
|
|
||||||
|
# Or using Node.js http-server
|
||||||
|
npx http-server -p 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Update dev-app-update.yml** to point to local server:
|
||||||
|
```yaml
|
||||||
|
provider: generic
|
||||||
|
url: http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Run the app** and it will check your local server
|
||||||
|
|
||||||
|
## Testing Steps
|
||||||
|
|
||||||
|
### 1. Prepare Test Environment
|
||||||
|
|
||||||
|
**Current version**: `2.0.3` (in package.json)
|
||||||
|
**Test version**: `2.0.4` (on GitHub or local server)
|
||||||
|
|
||||||
|
### 2. Run the App
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Watch for Update Events
|
||||||
|
|
||||||
|
The app will automatically check for updates 3 seconds after startup. Watch the console for:
|
||||||
|
|
||||||
|
```
|
||||||
|
Checking for updates...
|
||||||
|
Update available: 2.0.4
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Check Console Logs
|
||||||
|
|
||||||
|
Look for these messages:
|
||||||
|
- `Checking for updates...` - Update check started
|
||||||
|
- `Update available: 2.0.4` - New version found
|
||||||
|
- `Download speed: ...` - Download progress
|
||||||
|
- `Update downloaded: 2.0.4` - Download complete
|
||||||
|
|
||||||
|
### 5. Test UI Integration
|
||||||
|
|
||||||
|
The app sends these events to the renderer:
|
||||||
|
- `update-checking`
|
||||||
|
- `update-available` (with version info)
|
||||||
|
- `update-download-progress` (with progress data)
|
||||||
|
- `update-downloaded` (ready to install)
|
||||||
|
|
||||||
|
You can listen to these in your frontend code to show update notifications.
|
||||||
|
|
||||||
|
## Manual Testing
|
||||||
|
|
||||||
|
### Trigger Manual Update Check
|
||||||
|
|
||||||
|
You can also trigger a manual check via IPC:
|
||||||
|
```javascript
|
||||||
|
// In renderer process
|
||||||
|
const result = await window.electronAPI.invoke('check-for-updates');
|
||||||
|
console.log(result);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install Update
|
||||||
|
|
||||||
|
After an update is downloaded:
|
||||||
|
```javascript
|
||||||
|
// In renderer process
|
||||||
|
await window.electronAPI.invoke('quit-and-install-update');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Scenarios
|
||||||
|
|
||||||
|
### Scenario 1: Update Available
|
||||||
|
1. Set `package.json` version to `2.0.1`
|
||||||
|
2. Ensure GitHub has version `2.0.3` or higher
|
||||||
|
3. Run app → Should detect update
|
||||||
|
|
||||||
|
### Scenario 2: Already Up to Date
|
||||||
|
1. Set `package.json` version to `2.0.3`
|
||||||
|
2. Ensure GitHub has version `2.0.3` or lower
|
||||||
|
3. Run app → Should show "no update available"
|
||||||
|
|
||||||
|
### Scenario 3: Prerelease Version
|
||||||
|
1. Set `package.json` version to `2.0.2b`
|
||||||
|
2. Ensure GitHub has version `2.0.3`
|
||||||
|
3. Run app → Should detect update (prerelease < release)
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Update Not Detected
|
||||||
|
|
||||||
|
1. **Check dev-app-update.yml exists** in project root
|
||||||
|
2. **Verify dev mode is enabled** - Check console for "Dev update mode enabled"
|
||||||
|
3. **Check version numbers** - Remote version must be higher than current
|
||||||
|
4. **Check network** - App needs internet to reach GitHub/local server
|
||||||
|
5. **Check logs** - Look for error messages in console
|
||||||
|
|
||||||
|
### Common Errors
|
||||||
|
|
||||||
|
- **"Cannot find module 'electron-updater'"**: Run `npm install`
|
||||||
|
- **"Update check failed"**: Check network connection or GitHub API access
|
||||||
|
- **"No update available"**: Version comparison issue - check versions
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
|
||||||
|
Enable more verbose logging by checking the console output. The logger will show:
|
||||||
|
- Update check requests
|
||||||
|
- Version comparisons
|
||||||
|
- Download progress
|
||||||
|
- Any errors
|
||||||
|
|
||||||
|
## Testing with Real GitHub Releases
|
||||||
|
|
||||||
|
For the most realistic test:
|
||||||
|
|
||||||
|
1. **Create a test release on GitHub**:
|
||||||
|
- Build the app with version `2.0.4`
|
||||||
|
- Create a GitHub release with tag `v2.0.4`
|
||||||
|
- Upload the built files
|
||||||
|
|
||||||
|
2. **Lower your local version**:
|
||||||
|
- Set `package.json` to `2.0.3`
|
||||||
|
|
||||||
|
3. **Run the app**:
|
||||||
|
- It will check GitHub and find `2.0.4`
|
||||||
|
- Download and install the update
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- **Dev mode only works when app is NOT packaged** (`!app.isPackaged`)
|
||||||
|
- **Production builds** ignore `dev-app-update.yml` and use the built-in `app-update.yml`
|
||||||
|
- **macOS**: Code signing is required for updates to work in production
|
||||||
|
- **Windows**: NSIS installer is required for auto-updates
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
Once testing is complete:
|
||||||
|
1. Remove or comment out `forceDevUpdateConfig` for production
|
||||||
|
2. Ensure proper code signing for macOS
|
||||||
|
3. Set up CI/CD to automatically publish releases
|
||||||
129
package-lock.json
generated
129
package-lock.json
generated
@@ -1,17 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "hytale-f2p-launcher",
|
"name": "hytale-f2p-launcher",
|
||||||
"version": "2.0.2b",
|
"version": "2.0.11",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "hytale-f2p-launcher",
|
"name": "hytale-f2p-launcher",
|
||||||
"version": "2.0.2b",
|
"version": "2.0.11",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "^0.5.10",
|
"adm-zip": "^0.5.10",
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
"discord-rpc": "^4.0.1",
|
"discord-rpc": "^4.0.1",
|
||||||
|
"dotenv": "^17.2.3",
|
||||||
|
"electron-updater": "^6.7.3",
|
||||||
"tar": "^6.2.1",
|
"tar": "^6.2.1",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
},
|
},
|
||||||
@@ -1018,6 +1020,19 @@
|
|||||||
"electron-builder-squirrel-windows": "26.4.0"
|
"electron-builder-squirrel-windows": "26.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/app-builder-lib/node_modules/dotenv": {
|
||||||
|
"version": "16.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||||
|
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/app-builder-lib/node_modules/fs-extra": {
|
"node_modules/app-builder-lib/node_modules/fs-extra": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||||
@@ -1073,7 +1088,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "Python-2.0"
|
"license": "Python-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/assert-plus": {
|
"node_modules/assert-plus": {
|
||||||
@@ -1283,7 +1297,6 @@
|
|||||||
"version": "9.5.1",
|
"version": "9.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.5.1.tgz",
|
||||||
"integrity": "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==",
|
"integrity": "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
@@ -1711,7 +1724,6 @@
|
|||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "^2.1.3"
|
"ms": "^2.1.3"
|
||||||
@@ -1962,10 +1974,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "16.6.1",
|
"version": "17.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
|
||||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
"integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -1990,6 +2001,19 @@
|
|||||||
"url": "https://dotenvx.com"
|
"url": "https://dotenvx.com"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv-expand/node_modules/dotenv": {
|
||||||
|
"version": "16.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||||
|
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@@ -2178,6 +2202,69 @@
|
|||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/electron-updater": {
|
||||||
|
"version": "6.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.7.3.tgz",
|
||||||
|
"integrity": "sha512-EgkT8Z9noqXKbwc3u5FkJA+r48jwZ5DTUiOkJMOTEEH//n5Am6wfQGz7nvSFEA2oIAMv9jRzn5JKTyWeSKOPgg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"builder-util-runtime": "9.5.1",
|
||||||
|
"fs-extra": "^10.1.0",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
|
"lazy-val": "^1.0.5",
|
||||||
|
"lodash.escaperegexp": "^4.1.2",
|
||||||
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"semver": "~7.7.3",
|
||||||
|
"tiny-typed-emitter": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/electron-updater/node_modules/fs-extra": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/electron-updater/node_modules/jsonfile": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/electron-updater/node_modules/semver": {
|
||||||
|
"version": "7.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||||
|
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/electron-updater/node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/electron-winstaller": {
|
"node_modules/electron-winstaller": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz",
|
||||||
@@ -2759,7 +2846,6 @@
|
|||||||
"version": "4.2.11",
|
"version": "4.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/has-flag": {
|
"node_modules/has-flag": {
|
||||||
@@ -3082,7 +3168,6 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^2.0.1"
|
"argparse": "^2.0.1"
|
||||||
@@ -3150,7 +3235,6 @@
|
|||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
|
||||||
"integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
|
"integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
@@ -3160,6 +3244,19 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.escaperegexp": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isequal": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||||
|
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/log-symbols": {
|
"node_modules/log-symbols": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
|
||||||
@@ -3476,7 +3573,6 @@
|
|||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
@@ -4131,7 +4227,6 @@
|
|||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz",
|
||||||
"integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==",
|
"integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==",
|
||||||
"dev": true,
|
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=11.0.0"
|
"node": ">=11.0.0"
|
||||||
@@ -4602,6 +4697,12 @@
|
|||||||
"semver": "bin/semver"
|
"semver": "bin/semver"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-typed-emitter": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.15",
|
"version": "0.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||||
|
|||||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hytale-f2p-launcher",
|
"name": "hytale-f2p-launcher",
|
||||||
"version": "2.0.2b",
|
"version": "2.0.11",
|
||||||
"description": "A modern, cross-platform launcher for Hytale with automatic updates and multi-client support",
|
"description": "A modern, cross-platform launcher for Hytale with automatic updates and multi-client support",
|
||||||
"homepage": "https://github.com/amiayweb/Hytale-F2P",
|
"homepage": "https://github.com/amiayweb/Hytale-F2P",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
"discord-rpc": "^4.0.1",
|
"discord-rpc": "^4.0.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
|
"electron-updater": "^6.7.3",
|
||||||
"tar": "^6.2.1",
|
"tar": "^6.2.1",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
},
|
},
|
||||||
@@ -78,12 +79,6 @@
|
|||||||
"x64",
|
"x64",
|
||||||
"arm64"
|
"arm64"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"target": "portable",
|
|
||||||
"arch": [
|
|
||||||
"x64"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon.ico"
|
"icon": "icon.ico"
|
||||||
@@ -145,6 +140,11 @@
|
|||||||
"allowToChangeInstallationDirectory": true,
|
"allowToChangeInstallationDirectory": true,
|
||||||
"createDesktopShortcut": true,
|
"createDesktopShortcut": true,
|
||||||
"createStartMenuShortcut": true
|
"createStartMenuShortcut": true
|
||||||
|
},
|
||||||
|
"publish": {
|
||||||
|
"provider": "github",
|
||||||
|
"owner": "amiayweb",
|
||||||
|
"repo": "Hytale-F2P"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user