v2.3.2: fix truncated download cache, update Discord link

Fix pre-release downloads failing with "unexpected EOF" by validating
cached PWR file sizes against manifest expected sizes. Previously only
checked > 1MB which accepted truncated files. Also update Discord
invite link to new server across all files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
sanasol
2026-02-20 15:14:20 +01:00
parent 11c6d40dfe
commit e7a033932f
10 changed files with 58 additions and 21 deletions

View File

@@ -36,7 +36,7 @@ This Code of Conduct applies within all community spaces, and also applies when
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [Discord Server, message Founders/Devs](https://discord.gg/hf2pdc). All complaints will be reviewed and investigated promptly and fairly. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [Discord Server, message Founders/Devs](https://discord.gg/Fhbb9Yk5WW). All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the reporter of any incident. All community leaders are obligated to respect the privacy and security of the reporter of any incident.

View File

@@ -22,7 +22,7 @@ body:
value: | value: |
If you need help or support with using the launcher, please fill out this support request. If you need help or support with using the launcher, please fill out this support request.
Provide as much detail as possible so we can assist you effectively. Provide as much detail as possible so we can assist you effectively.
**Need a quick assistance?** Please Open-A-Ticket in our [Discord Server](https://discord.gg/gME8rUy3MB)! **Need a quick assistance?** Please Open-A-Ticket in our [Discord Server](https://discord.gg/Fhbb9Yk5WW)!
- type: textarea - type: textarea
id: question id: question

View File

@@ -53,7 +53,7 @@ window.closeDiscordPopup = function() {
}; };
window.joinDiscord = async function() { window.joinDiscord = async function() {
await window.electronAPI?.openExternal('https://discord.gg/hf2pdc'); await window.electronAPI?.openExternal('https://discord.gg/Fhbb9Yk5WW');
try { try {
await window.electronAPI?.saveConfig({ discordPopup: true }); await window.electronAPI?.saveConfig({ discordPopup: true });

View File

@@ -1103,7 +1103,7 @@ function getRetryContextMessage() {
} }
window.openDiscordExternal = function() { window.openDiscordExternal = function() {
window.electronAPI?.openExternal('https://discord.gg/hf2pdc'); window.electronAPI?.openExternal('https://discord.gg/Fhbb9Yk5WW');
}; };
window.toggleMaximize = toggleMaximize; window.toggleMaximize = toggleMaximize;

View File

@@ -18,7 +18,7 @@
### ⚠️ **WARNING: READ [QUICK START](#-quick-start) before Downloading & Installing the Launcher!** ⚠️ ### ⚠️ **WARNING: READ [QUICK START](#-quick-start) before Downloading & Installing the Launcher!** ⚠️
#### 🛑 **Found a problem? [Join the HF2P Discord](https://discord.gg/hf2pdc) and head to `#-⚠️-community-help`** 🛑 #### 🛑 **Found a problem? [Join the HF2P Discord](https://discord.gg/Fhbb9Yk5WW) and head to `#-⚠️-community-help`** 🛑
<p> <p>
👍 If you like the project, <b>feel free to support us via Buy Me a Coffee!</b> ☕<br> 👍 If you like the project, <b>feel free to support us via Buy Me a Coffee!</b> ☕<br>
@@ -455,7 +455,7 @@ See [BUILD.md](docs/BUILD.md) for comprehensive build instructions.
<div align="center"> <div align="center">
**Questions? Ads? Collaboration? Endorsement? Other business-related?** **Questions? Ads? Collaboration? Endorsement? Other business-related?**
Message the founders at https://discord.gg/hf2pdc Message the founders at https://discord.gg/Fhbb9Yk5WW
</div> </div>

View File

@@ -2,7 +2,7 @@
Play with friends online! This guide covers both easy in-game hosting and advanced dedicated server setup. Play with friends online! This guide covers both easy in-game hosting and advanced dedicated server setup.
### **DOWNLOAD SERVER FILES (JAR/RAR/SCRIPTS) HERE: https://discord.gg/hf2pdc** ### **DOWNLOAD SERVER FILES (JAR/RAR/SCRIPTS) HERE: https://discord.gg/Fhbb9Yk5WW**
**Table of Contents** **Table of Contents**

View File

@@ -1,6 +1,6 @@
# Hytale F2P Launcher - Troubleshooting Guide # Hytale F2P Launcher - Troubleshooting Guide
This guide covers common issues and their solutions. If your issue isn't listed here, please check [existing issues](https://github.com/amiayweb/Hytale-F2P/issues) or join our [Discord](https://discord.gg/gME8rUy3MB). This guide covers common issues and their solutions. If your issue isn't listed here, please check [existing issues](https://github.com/amiayweb/Hytale-F2P/issues) or join our [Discord](https://discord.gg/Fhbb9Yk5WW).
--- ---
@@ -437,7 +437,7 @@ Game sessions have a 10-hour TTL. This is by design for security.
If your issue isn't resolved by this guide: If your issue isn't resolved by this guide:
1. **Check existing issues:** [GitHub Issues](https://github.com/amiayweb/Hytale-F2P/issues) 1. **Check existing issues:** [GitHub Issues](https://github.com/amiayweb/Hytale-F2P/issues)
2. **Join Discord:** [discord.gg/gME8rUy3MB](https://discord.gg/gME8rUy3MB) 2. **Join Discord:** [discord.gg/Fhbb9Yk5WW](https://discord.gg/Fhbb9Yk5WW)
3. **Open a new issue** with: 3. **Open a new issue** with:
- Your operating system and version - Your operating system and version
- Launcher version - Launcher version

View File

@@ -64,7 +64,7 @@ async function safeRemoveDirectory(dirPath, maxRetries = 3) {
} }
} }
async function downloadPWR(branch = 'release', fileName = 'v8', progressCallback, cacheDir = CACHE_DIR, manualRetry = false, directUrl = null) { async function downloadPWR(branch = 'release', fileName = 'v8', progressCallback, cacheDir = CACHE_DIR, manualRetry = false, directUrl = null, expectedSize = null) {
const osName = getOS(); const osName = getOS();
const arch = getArch(); const arch = getArch();
@@ -91,16 +91,46 @@ async function downloadPWR(branch = 'release', fileName = 'v8', progressCallback
} }
} }
// Look up expected file size from manifest if not provided
if (!expectedSize) {
try {
const { fetchMirrorManifest } = require('../services/versionManager');
const manifest = await fetchMirrorManifest();
// Try to match: "0_to_11" format or "v11" format
const versionMatch = fileName.match(/^(\d+)_to_(\d+)$/);
let manifestKey;
if (versionMatch) {
manifestKey = `${osName}/${arch}/${branch}/${fileName}.pwr`;
} else {
const buildNum = extractVersionNumber(fileName);
manifestKey = `${osName}/${arch}/${branch}/0_to_${buildNum}.pwr`;
}
if (manifest.files[manifestKey]) {
expectedSize = manifest.files[manifestKey].size;
console.log(`[PWR] Expected size from manifest: ${(expectedSize / 1024 / 1024).toFixed(2)} MB`);
}
} catch (e) {
console.log(`[PWR] Could not fetch expected size from manifest: ${e.message}`);
}
}
const dest = path.join(cacheDir, `${branch}_${fileName}.pwr`); const dest = path.join(cacheDir, `${branch}_${fileName}.pwr`);
// Check if file exists and validate it // Check if file exists and validate it
if (fs.existsSync(dest) && !manualRetry) { if (fs.existsSync(dest) && !manualRetry) {
const stats = fs.statSync(dest); const stats = fs.statSync(dest);
if (stats.size > 1024 * 1024) { if (stats.size > 1024 * 1024) {
console.log(`[PWR] Using cached file: ${dest} (${(stats.size / 1024 / 1024).toFixed(2)} MB)`); // Validate against expected size - reject if file is truncated (< 99% of expected)
return dest; if (expectedSize && stats.size < expectedSize * 0.99) {
console.log(`[PWR] Cached file truncated: ${(stats.size / 1024 / 1024).toFixed(2)} MB, expected ${(expectedSize / 1024 / 1024).toFixed(2)} MB. Deleting and re-downloading.`);
fs.unlinkSync(dest);
} else {
console.log(`[PWR] Using cached file: ${dest} (${(stats.size / 1024 / 1024).toFixed(2)} MB)`);
return dest;
}
} else {
console.log(`[PWR] Cached file too small (${stats.size} bytes), re-downloading`);
} }
console.log(`[PWR] Cached file too small (${stats.size} bytes), re-downloading`);
} }
console.log(`[DownloadPWR] Downloading from: ${url}`); console.log(`[DownloadPWR] Downloading from: ${url}`);
@@ -129,7 +159,7 @@ async function downloadPWR(branch = 'release', fileName = 'v8', progressCallback
const retryStats = fs.statSync(dest); const retryStats = fs.statSync(dest);
console.log(`PWR file downloaded (auto-retry), size: ${(retryStats.size / 1024 / 1024).toFixed(2)} MB`); console.log(`PWR file downloaded (auto-retry), size: ${(retryStats.size / 1024 / 1024).toFixed(2)} MB`);
if (!validatePWRFile(dest)) { if (!validatePWRFile(dest, expectedSize)) {
console.log(`[PWR Validation] PWR file validation failed after auto-retry, deleting corrupted file: ${dest}`); console.log(`[PWR Validation] PWR file validation failed after auto-retry, deleting corrupted file: ${dest}`);
fs.unlinkSync(dest); fs.unlinkSync(dest);
throw new Error('Downloaded PWR file is corrupted or invalid after automatic retry. Please retry manually'); throw new Error('Downloaded PWR file is corrupted or invalid after automatic retry. Please retry manually');
@@ -180,7 +210,7 @@ async function downloadPWR(branch = 'release', fileName = 'v8', progressCallback
const stats = fs.statSync(dest); const stats = fs.statSync(dest);
console.log(`PWR file downloaded, size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`); console.log(`PWR file downloaded, size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`);
if (!validatePWRFile(dest)) { if (!validatePWRFile(dest, expectedSize)) {
console.log(`[PWR Validation] PWR file validation failed, deleting corrupted file: ${dest}`); console.log(`[PWR Validation] PWR file validation failed, deleting corrupted file: ${dest}`);
fs.unlinkSync(dest); fs.unlinkSync(dest);
throw new Error('Downloaded PWR file is corrupted or invalid. Please retry'); throw new Error('Downloaded PWR file is corrupted or invalid. Please retry');
@@ -440,7 +470,7 @@ async function updateGameFiles(newVersion, progressCallback, gameDir = GAME_DIR,
progressCallback(`Downloading patch ${i + 1}/${updatePlan.steps.length} (${stepName})...`, progress, null, null, null); progressCallback(`Downloading patch ${i + 1}/${updatePlan.steps.length} (${stepName})...`, progress, null, null, null);
} }
const pwrFile = await downloadPWR(branch, stepName, progressCallback, cacheDir, false, step.url); const pwrFile = await downloadPWR(branch, stepName, progressCallback, cacheDir, false, step.url, step.size);
if (!pwrFile) { if (!pwrFile) {
throw new Error(`Failed to download patch ${stepName}`); throw new Error(`Failed to download patch ${stepName}`);
@@ -891,7 +921,7 @@ function validateGameDirectory(gameDir, stagingDir) {
// Enhanced PWR file validation // Enhanced PWR file validation
// Accepts intermediate patches (50+ MB) and full installs (1.5+ GB) // Accepts intermediate patches (50+ MB) and full installs (1.5+ GB)
function validatePWRFile(filePath) { function validatePWRFile(filePath, expectedSize = null) {
try { try {
if (!fs.existsSync(filePath)) { if (!fs.existsSync(filePath)) {
return false; return false;
@@ -906,6 +936,13 @@ function validatePWRFile(filePath) {
return false; return false;
} }
// Validate against expected size if known (reject if < 99% of expected)
if (expectedSize && stats.size < expectedSize * 0.99) {
const expectedMB = expectedSize / 1024 / 1024;
console.log(`[PWR Validation] File truncated: ${sizeInMB.toFixed(2)} MB, expected ${expectedMB.toFixed(2)} MB`);
return false;
}
console.log(`[PWR Validation] File size: ${sizeInMB.toFixed(2)} MB - OK`); console.log(`[PWR Validation] File size: ${sizeInMB.toFixed(2)} MB - OK`);
return true; return true;
} catch (error) { } catch (error) {

View File

@@ -89,7 +89,7 @@ function setDiscordActivity() {
}, },
{ {
label: 'Discord', label: 'Discord',
url: 'https://discord.gg/hf2pdc' url: 'https://discord.gg/Fhbb9Yk5WW'
} }
] ]
}); });

View File

@@ -1,6 +1,6 @@
{ {
"name": "hytale-f2p-launcher", "name": "hytale-f2p-launcher",
"version": "2.3.1", "version": "2.3.2",
"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",