diff --git a/backend/services/versionManager.js b/backend/services/versionManager.js index 6957821..6bc902d 100644 --- a/backend/services/versionManager.js +++ b/backend/services/versionManager.js @@ -7,16 +7,17 @@ const { getOS, getArch } = require('../utils/platformUtils'); // Patches base URL fetched dynamically via multi-source fallback chain const AUTH_DOMAIN = process.env.HYTALE_AUTH_DOMAIN || 'auth.sanasol.ws'; const PATCHES_CONFIG_SOURCES = [ - { type: 'http', url: `https://${AUTH_DOMAIN}/api/patches-config`, name: 'auth-server' }, - { type: 'http', url: 'https://htdwnldsan.top/patches-config', name: 'backup-http' }, + { type: 'http', url: `https://${AUTH_DOMAIN}/api/patches-config`, name: 'primary' }, + { type: 'http', url: 'https://htdwnldsan.top/patches-config', name: 'backup-1' }, + { type: 'http', url: 'https://dl1.htdwnldsan.top/patches-config', name: 'backup-2' }, { type: 'doh', name: '_patches.htdwnldsan.top', name_label: 'dns-txt' }, ]; const HARDCODED_FALLBACK = 'https://dl.vboro.de/patches'; -// Non-Cloudflare mirrors for users where Cloudflare IPs are blocked (Russia, Ukraine, etc.) -// These redirect to MEGA S3 which is not behind Cloudflare +// Alternative mirrors (non-Cloudflare) for regions where CF is blocked const NON_CF_MIRRORS = [ - 'https://htdwnldsan.top/patches', // Direct IP VPS → MEGA redirect + 'https://dl1.htdwnldsan.top', + 'https://htdwnldsan.top/patches', ]; // Fallback: latest known build number if manifest is unreachable diff --git a/backend/utils/clientPatcher.js b/backend/utils/clientPatcher.js index 4325a84..4656f3d 100644 --- a/backend/utils/clientPatcher.js +++ b/backend/utils/clientPatcher.js @@ -9,7 +9,9 @@ const MAX_DOMAIN_LENGTH = 16; // DualAuth ByteBuddy Agent (runtime class transformation, no JAR modification) const DUALAUTH_AGENT_URL = 'https://github.com/sanasol/hytale-auth-server/releases/latest/download/dualauth-agent.jar'; +const DUALAUTH_AGENT_VERSION_API = 'https://api.github.com/repos/sanasol/hytale-auth-server/releases/latest'; const DUALAUTH_AGENT_FILENAME = 'dualauth-agent.jar'; +const DUALAUTH_AGENT_VERSION_FILE = 'dualauth-agent.version'; function getTargetDomain() { if (process.env.HYTALE_AUTH_DOMAIN) { @@ -511,30 +513,70 @@ class ClientPatcher { */ async ensureAgentAvailable(serverDir, progressCallback) { const agentPath = this.getAgentPath(serverDir); + const versionPath = path.join(serverDir, DUALAUTH_AGENT_VERSION_FILE); console.log('=== DualAuth Agent (ByteBuddy) ==='); console.log(`Target: ${agentPath}`); - // Check if agent already exists and is valid + // Check local version and whether file exists + let localVersion = null; + let agentExists = false; if (fs.existsSync(agentPath)) { try { const stats = fs.statSync(agentPath); if (stats.size > 1024) { - console.log(`DualAuth Agent present (${(stats.size / 1024).toFixed(0)} KB)`); - if (progressCallback) progressCallback('DualAuth Agent ready', 100); - return { success: true, agentPath, alreadyExists: true }; + agentExists = true; + if (fs.existsSync(versionPath)) { + localVersion = fs.readFileSync(versionPath, 'utf8').trim(); + } + } else { + console.log('Agent file appears corrupt, re-downloading...'); + fs.unlinkSync(agentPath); } - // File exists but too small - corrupt, re-download - console.log('Agent file appears corrupt, re-downloading...'); - fs.unlinkSync(agentPath); } catch (e) { console.warn('Could not check agent file:', e.message); } } + // Check for updates from GitHub + let remoteVersion = null; + let needsDownload = !agentExists; + if (agentExists) { + try { + if (progressCallback) progressCallback('Checking for agent updates...', 5); + const axios = require('axios'); + const resp = await axios.get(DUALAUTH_AGENT_VERSION_API, { + timeout: 5000, + headers: { 'Accept': 'application/vnd.github.v3+json' } + }); + remoteVersion = resp.data.tag_name; // e.g. "v1.1.10" + if (localVersion && localVersion === remoteVersion) { + console.log(`DualAuth Agent up to date (${localVersion})`); + if (progressCallback) progressCallback('DualAuth Agent ready', 100); + return { success: true, agentPath, alreadyExists: true, version: localVersion }; + } + console.log(`Agent update available: ${localVersion || 'unknown'} → ${remoteVersion}`); + needsDownload = true; + } catch (e) { + // GitHub API failed - use existing agent if available + console.warn(`Could not check for updates: ${e.message}`); + if (agentExists) { + console.log(`Using existing agent (${localVersion || 'unknown version'})`); + if (progressCallback) progressCallback('DualAuth Agent ready', 100); + return { success: true, agentPath, alreadyExists: true, version: localVersion }; + } + } + } + + if (!needsDownload) { + if (progressCallback) progressCallback('DualAuth Agent ready', 100); + return { success: true, agentPath, alreadyExists: true, version: localVersion }; + } + // Download agent from GitHub releases - if (progressCallback) progressCallback('Downloading DualAuth Agent...', 20); - console.log(`Downloading from: ${DUALAUTH_AGENT_URL}`); + const action = agentExists ? 'Updating' : 'Downloading'; + if (progressCallback) progressCallback(`${action} DualAuth Agent...`, 20); + console.log(`${action} from: ${DUALAUTH_AGENT_URL}`); try { // Ensure server directory exists @@ -548,7 +590,7 @@ class ClientPatcher { const stream = await smartDownloadStream(DUALAUTH_AGENT_URL, (chunk, downloadedBytes, total) => { if (progressCallback && total) { const percent = 20 + Math.floor((downloadedBytes / total) * 70); - progressCallback(`Downloading agent... ${(downloadedBytes / 1024).toFixed(0)} KB`, percent); + progressCallback(`${action} agent... ${(downloadedBytes / 1024).toFixed(0)} KB`, percent); } }); @@ -575,9 +617,13 @@ class ClientPatcher { } fs.renameSync(tmpPath, agentPath); - console.log(`DualAuth Agent downloaded (${(stats.size / 1024).toFixed(0)} KB)`); + // Save version + const version = remoteVersion || 'unknown'; + fs.writeFileSync(versionPath, version, 'utf8'); + + console.log(`DualAuth Agent ${agentExists ? 'updated' : 'downloaded'} (${(stats.size / 1024).toFixed(0)} KB, ${version})`); if (progressCallback) progressCallback('DualAuth Agent ready', 100); - return { success: true, agentPath }; + return { success: true, agentPath, updated: agentExists, version }; } catch (downloadError) { console.error(`Failed to download DualAuth Agent: ${downloadError.message}`); @@ -586,6 +632,11 @@ class ClientPatcher { if (fs.existsSync(tmpPath)) { try { fs.unlinkSync(tmpPath); } catch (e) { /* ignore */ } } + // If we had an existing agent, still use it + if (agentExists) { + console.log('Using existing agent despite update failure'); + return { success: true, agentPath, alreadyExists: true, version: localVersion }; + } return { success: false, error: downloadError.message }; } }