mirror of
https://git.sanhost.net/sanasol/hytale-f2p.git
synced 2026-02-26 06:41:47 -03:00
v2.3.1: CDN redirect gateway, fix token username bug
- Migrate patch downloads to auth server redirect gateway (302 -> CDN) Allows instant CDN switching via admin panel without launcher update - Fix identity token "Player" username mismatch on fresh install Add token username verification with retry in fetchAuthTokens - Refactor versionManager to use mirror manifest via auth.sanasol.ws/patches - Add optimal patch routing (BFS) for differential updates - Add PATCH_CDN_INFRASTRUCTURE.md documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ const path = require('path');
|
||||
const { execFile } = require('child_process');
|
||||
const { downloadFile, retryDownload } = require('../utils/fileManager');
|
||||
const { getOS, getArch } = require('../utils/platformUtils');
|
||||
const { validateChecksum, extractVersionDetails, canUseDifferentialUpdate, needsIntermediatePatches, getInstalledClientVersion } = require('../services/versionManager');
|
||||
const { validateChecksum, extractVersionDetails, getInstalledClientVersion, getUpdatePlan, extractVersionNumber } = require('../services/versionManager');
|
||||
const { installButler } = require('./butlerManager');
|
||||
const { GAME_DIR, CACHE_DIR, TOOLS_DIR } = require('../core/paths');
|
||||
const { saveVersionClient } = require('../core/config');
|
||||
@@ -30,7 +30,7 @@ async function acquireGameArchive(downloadUrl, targetPath, checksum, progressCal
|
||||
}
|
||||
|
||||
console.log(`Downloading game archive from: ${downloadUrl}`);
|
||||
|
||||
|
||||
try {
|
||||
if (allowRetry) {
|
||||
await retryDownload(downloadUrl, targetPath, progressCallback);
|
||||
@@ -103,13 +103,13 @@ async function deployGameArchive(archivePath, destinationDir, toolsDir, progress
|
||||
if (error) {
|
||||
const cleanStderr = stderr.replace(/[\u2714\u2716\u2713\u2717\u26A0\uD83D[\uDC00-\uDFFF]]/g, '').trim();
|
||||
const cleanStdout = stdout.replace(/[\u2714\u2716\u2713\u2717\u26A0\uD83D[\uDC00-\uDFFF]]/g, '').trim();
|
||||
|
||||
|
||||
if (cleanStderr) console.error('Deployment stderr:', cleanStderr);
|
||||
if (cleanStdout) console.error('Deployment stdout:', cleanStdout);
|
||||
|
||||
|
||||
const errorText = (stderr + ' ' + error.message).toLowerCase();
|
||||
let message = 'Game deployment failed';
|
||||
|
||||
|
||||
if (errorText.includes('unexpected eof')) {
|
||||
message = 'Corrupted archive detected. Please retry download.';
|
||||
if (fs.existsSync(archivePath)) {
|
||||
@@ -156,20 +156,20 @@ async function performIntelligentUpdate(targetVersion, branch = 'release', progr
|
||||
console.log(`Initiating intelligent update to version ${targetVersion}`);
|
||||
|
||||
const currentVersion = getInstalledClientVersion();
|
||||
console.log(`Current version: ${currentVersion || 'none (clean install)'}`);
|
||||
console.log(`Target version: ${targetVersion}`);
|
||||
console.log(`Branch: ${branch}`);
|
||||
const currentBuild = extractVersionNumber(currentVersion) || 0;
|
||||
const targetBuild = extractVersionNumber(targetVersion);
|
||||
console.log(`Current build: ${currentBuild}, Target build: ${targetBuild}, Branch: ${branch}`);
|
||||
|
||||
// For non-release branches, always do full install
|
||||
if (branch !== 'release') {
|
||||
console.log(`Pre-release branch detected - forcing full archive download`);
|
||||
console.log('Pre-release branch detected - forcing full archive download');
|
||||
const versionDetails = await extractVersionDetails(targetVersion, branch);
|
||||
const archiveName = path.basename(versionDetails.fullUrl);
|
||||
const archivePath = path.join(cacheDir, `${branch}_${archiveName}`);
|
||||
|
||||
const archivePath = path.join(cacheDir, `${branch}_0_to_${targetBuild}.pwr`);
|
||||
|
||||
if (progressCallback) {
|
||||
progressCallback('Downloading full game archive (pre-release)...', 0, null, null, null);
|
||||
}
|
||||
|
||||
|
||||
await acquireGameArchive(versionDetails.fullUrl, archivePath, null, progressCallback);
|
||||
await deployGameArchive(archivePath, gameDir, toolsDir, progressCallback, false);
|
||||
saveVersionClient(targetVersion);
|
||||
@@ -177,16 +177,16 @@ async function performIntelligentUpdate(targetVersion, branch = 'release', progr
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentVersion) {
|
||||
// Clean install (no current version)
|
||||
if (currentBuild === 0) {
|
||||
console.log('No existing installation detected - downloading full archive');
|
||||
const versionDetails = await extractVersionDetails(targetVersion, branch);
|
||||
const archiveName = path.basename(versionDetails.fullUrl);
|
||||
const archivePath = path.join(cacheDir, `${branch}_${archiveName}`);
|
||||
|
||||
const archivePath = path.join(cacheDir, `${branch}_0_to_${targetBuild}.pwr`);
|
||||
|
||||
if (progressCallback) {
|
||||
progressCallback(`Downloading full game archive (first install - v${targetVersion})...`, 0, null, null, null);
|
||||
progressCallback(`Downloading full game archive (first install - v${targetBuild})...`, 0, null, null, null);
|
||||
}
|
||||
|
||||
|
||||
await acquireGameArchive(versionDetails.fullUrl, archivePath, null, progressCallback);
|
||||
await deployGameArchive(archivePath, gameDir, toolsDir, progressCallback, false);
|
||||
saveVersionClient(targetVersion);
|
||||
@@ -194,59 +194,67 @@ async function performIntelligentUpdate(targetVersion, branch = 'release', progr
|
||||
return;
|
||||
}
|
||||
|
||||
const patchesToApply = needsIntermediatePatches(currentVersion, targetVersion);
|
||||
|
||||
if (patchesToApply.length === 0) {
|
||||
console.log('Already at target version or invalid version sequence');
|
||||
// Already at target
|
||||
if (currentBuild >= targetBuild) {
|
||||
console.log('Already at target version or newer');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Applying ${patchesToApply.length} differential patch(es): ${patchesToApply.join(' -> ')}`);
|
||||
// Use mirror's update plan for optimal patch routing
|
||||
try {
|
||||
const plan = await getUpdatePlan(currentBuild, targetBuild, branch);
|
||||
|
||||
console.log(`Applying ${plan.steps.length} patch(es): ${plan.steps.map(s => `${s.from}\u2192${s.to}`).join(' + ')}`);
|
||||
|
||||
for (let i = 0; i < plan.steps.length; i++) {
|
||||
const step = plan.steps[i];
|
||||
const stepName = `${step.from}_to_${step.to}`;
|
||||
const archivePath = path.join(cacheDir, `${branch}_${stepName}.pwr`);
|
||||
const isDifferential = step.from !== 0;
|
||||
|
||||
for (let i = 0; i < patchesToApply.length; i++) {
|
||||
const patchVersion = patchesToApply[i];
|
||||
const versionDetails = await extractVersionDetails(patchVersion, branch);
|
||||
|
||||
const canDifferential = canUseDifferentialUpdate(getInstalledClientVersion(), versionDetails);
|
||||
|
||||
if (!canDifferential || !versionDetails.differentialUrl) {
|
||||
console.log(`WARNING: Differential patch not available for ${patchVersion}, using full archive`);
|
||||
const archiveName = path.basename(versionDetails.fullUrl);
|
||||
const archivePath = path.join(cacheDir, `${branch}_${archiveName}`);
|
||||
|
||||
if (progressCallback) {
|
||||
progressCallback(`Downloading full archive for ${patchVersion} (${i + 1}/${patchesToApply.length})...`, 0, null, null, null);
|
||||
progressCallback(`Downloading patch ${i + 1}/${plan.steps.length}: ${stepName}...`, 0, null, null, null);
|
||||
}
|
||||
|
||||
await acquireGameArchive(versionDetails.fullUrl, archivePath, null, progressCallback);
|
||||
await deployGameArchive(archivePath, gameDir, toolsDir, progressCallback, false);
|
||||
} else {
|
||||
console.log(`Applying differential patch: ${versionDetails.sourceVersion} -> ${patchVersion}`);
|
||||
const archiveName = path.basename(versionDetails.differentialUrl);
|
||||
const archivePath = path.join(cacheDir, `${branch}_patch_${archiveName}`);
|
||||
|
||||
|
||||
await acquireGameArchive(step.url, archivePath, null, progressCallback);
|
||||
|
||||
if (progressCallback) {
|
||||
progressCallback(`Applying patch ${i + 1}/${patchesToApply.length}: ${patchVersion}...`, 0, null, null, null);
|
||||
progressCallback(`Applying patch ${i + 1}/${plan.steps.length}: ${stepName}...`, 50, null, null, null);
|
||||
}
|
||||
|
||||
await acquireGameArchive(versionDetails.differentialUrl, archivePath, versionDetails.checksum, progressCallback);
|
||||
await deployGameArchive(archivePath, gameDir, toolsDir, progressCallback, true);
|
||||
|
||||
|
||||
await deployGameArchive(archivePath, gameDir, toolsDir, progressCallback, isDifferential);
|
||||
|
||||
// Clean up patch file
|
||||
if (fs.existsSync(archivePath)) {
|
||||
try {
|
||||
fs.unlinkSync(archivePath);
|
||||
console.log(`Cleaned up patch file: ${archiveName}`);
|
||||
console.log(`Cleaned up: ${stepName}.pwr`);
|
||||
} catch (cleanupErr) {
|
||||
console.warn(`Failed to cleanup patch file: ${cleanupErr.message}`);
|
||||
console.warn(`Failed to cleanup: ${cleanupErr.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveVersionClient(patchVersion);
|
||||
console.log(`Patch ${patchVersion} applied successfully (${i + 1}/${patchesToApply.length})`);
|
||||
}
|
||||
|
||||
console.log(`Update completed successfully. Version ${targetVersion} is now installed.`);
|
||||
saveVersionClient(`v${step.to}`);
|
||||
console.log(`Patch ${stepName} applied (${i + 1}/${plan.steps.length})`);
|
||||
}
|
||||
|
||||
console.log(`Update completed. Version ${targetVersion} is now installed.`);
|
||||
} catch (planError) {
|
||||
console.error('Update plan failed:', planError.message);
|
||||
console.log('Falling back to full archive download');
|
||||
|
||||
// Fallback: full install
|
||||
const versionDetails = await extractVersionDetails(targetVersion, branch);
|
||||
const archivePath = path.join(cacheDir, `${branch}_0_to_${targetBuild}.pwr`);
|
||||
|
||||
if (progressCallback) {
|
||||
progressCallback(`Downloading full game archive (fallback)...`, 0, null, null, null);
|
||||
}
|
||||
|
||||
await acquireGameArchive(versionDetails.fullUrl, archivePath, null, progressCallback);
|
||||
await deployGameArchive(archivePath, gameDir, toolsDir, progressCallback, false);
|
||||
saveVersionClient(targetVersion);
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureGameInstalled(targetVersion, branch = 'release', progressCallback, gameDir = GAME_DIR, cacheDir = CACHE_DIR, toolsDir = TOOLS_DIR) {
|
||||
|
||||
Reference in New Issue
Block a user