mirror of
https://gitea.shironeko-all.duckdns.org/shironeko/Hytale-F2P-2.git
synced 2026-02-26 02:31:46 -03:00
temp jar patcher
This commit is contained in:
@@ -331,6 +331,7 @@ exec "$REAL_JAVA" "\${ARGS[@]}"
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Monitor game process status in background
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!hasExited) {
|
if (!hasExited) {
|
||||||
console.log('Game appears to be running successfully');
|
console.log('Game appears to be running successfully');
|
||||||
@@ -343,6 +344,7 @@ exec "$REAL_JAVA" "\${ARGS[@]}"
|
|||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
|
// Return immediately, don't wait for setTimeout
|
||||||
return { success: true, installed: true, launched: true, pid: child.pid };
|
return { success: true, installed: true, launched: true, pid: child.pid };
|
||||||
} catch (spawnError) {
|
} catch (spawnError) {
|
||||||
console.error(`Error spawning game process: ${spawnError.message}`);
|
console.error(`Error spawning game process: ${spawnError.message}`);
|
||||||
@@ -404,13 +406,22 @@ async function launchGameWithVersionCheck(playerName = 'Player', progressCallbac
|
|||||||
progressCallback('Launching game...', 80, null, null, null);
|
progressCallback('Launching game...', 80, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await launchGame(playerName, progressCallback, javaPathOverride, installPathOverride, gpuPreference, branch);
|
const launchResult = await launchGame(playerName, progressCallback, javaPathOverride, installPathOverride, gpuPreference, branch);
|
||||||
|
|
||||||
|
// Ensure we always return a result
|
||||||
|
if (!launchResult) {
|
||||||
|
console.error('launchGame returned null/undefined, creating fallback response');
|
||||||
|
return { success: false, error: 'Game launch failed - no response from launcher' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return launchResult;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in version check and launch:', error);
|
console.error('Error in version check and launch:', error);
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback(`Error: ${error.message}`, -1, null, null, null);
|
progressCallback(`Error: ${error.message}`, -1, null, null, null);
|
||||||
}
|
}
|
||||||
throw error;
|
// Always return an error response instead of throwing
|
||||||
|
return { success: false, error: error.message || 'Unknown launch error' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -525,17 +525,16 @@ class ClientPatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Patch the server JAR using DualAuthPatcher for full dual auth support
|
* Patch the server JAR by downloading pre-patched version
|
||||||
* This uses the same patcher as the Docker server for consistency
|
|
||||||
* @param {string} serverPath - Path to the HytaleServer.jar
|
* @param {string} serverPath - Path to the HytaleServer.jar
|
||||||
* @param {function} progressCallback - Optional callback for progress updates
|
* @param {function} progressCallback - Optional callback for progress updates
|
||||||
* @param {string} javaPath - Path to Java executable
|
* @param {string} javaPath - Path to Java executable (unused, kept for compatibility)
|
||||||
* @returns {object} Result object with success status and details
|
* @returns {object} Result object with success status and details
|
||||||
*/
|
*/
|
||||||
async patchServer(serverPath, progressCallback, javaPath = null) {
|
async patchServer(serverPath, progressCallback, javaPath = null) {
|
||||||
const newDomain = this.getNewDomain();
|
const newDomain = this.getNewDomain();
|
||||||
|
|
||||||
console.log('=== Server Patcher v3.0 (DualAuth) ===');
|
console.log('=== Server Patcher TEMP SYSTEM NEED TO BE FIXED ===');
|
||||||
console.log(`Target: ${serverPath}`);
|
console.log(`Target: ${serverPath}`);
|
||||||
console.log(`Domain: ${newDomain}`);
|
console.log(`Domain: ${newDomain}`);
|
||||||
|
|
||||||
@@ -545,13 +544,13 @@ class ClientPatcher {
|
|||||||
return { success: false, error };
|
return { success: false, error };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if already patched with DualAuth
|
// Check if already patched
|
||||||
const patchFlagFile = serverPath + '.dualauth_patched';
|
const patchFlagFile = serverPath + '.dualauth_patched';
|
||||||
if (fs.existsSync(patchFlagFile)) {
|
if (fs.existsSync(patchFlagFile)) {
|
||||||
try {
|
try {
|
||||||
const flagData = JSON.parse(fs.readFileSync(patchFlagFile, 'utf8'));
|
const flagData = JSON.parse(fs.readFileSync(patchFlagFile, 'utf8'));
|
||||||
if (flagData.domain === newDomain) {
|
if (flagData.domain === newDomain) {
|
||||||
console.log(`Server already patched with DualAuth for ${newDomain}, skipping`);
|
console.log(`Server already patched for ${newDomain}, skipping`);
|
||||||
if (progressCallback) progressCallback('Server already patched', 100);
|
if (progressCallback) progressCallback('Server already patched', 100);
|
||||||
return { success: true, alreadyPatched: true };
|
return { success: true, alreadyPatched: true };
|
||||||
}
|
}
|
||||||
@@ -560,80 +559,99 @@ class ClientPatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressCallback) progressCallback('Preparing DualAuth patcher...', 10);
|
|
||||||
|
|
||||||
// Find Java executable - use bundled JRE first (same as game uses)
|
|
||||||
const java = javaPath || this.findJava();
|
|
||||||
if (!java) {
|
|
||||||
const error = 'Java not found. Please install the game first (it includes Java) or install Java 25 from: https://adoptium.net/';
|
|
||||||
console.error(error);
|
|
||||||
return { success: false, error };
|
|
||||||
}
|
|
||||||
console.log(`Using Java: ${java}`);
|
|
||||||
|
|
||||||
// Setup patcher directory
|
|
||||||
const patcherDir = path.join(__dirname, '..', 'patcher');
|
|
||||||
const patcherJava = path.join(patcherDir, 'DualAuthPatcher.java');
|
|
||||||
const libDir = path.join(patcherDir, 'lib');
|
|
||||||
|
|
||||||
// Download patcher from hytale-auth-server if not present
|
|
||||||
if (progressCallback) progressCallback('Checking patcher...', 15);
|
|
||||||
try {
|
|
||||||
await this.ensurePatcherDownloaded(patcherDir);
|
|
||||||
} catch (e) {
|
|
||||||
const error = `Failed to download DualAuthPatcher: ${e.message}`;
|
|
||||||
console.error(error);
|
|
||||||
return { success: false, error };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs.existsSync(patcherJava)) {
|
|
||||||
const error = `DualAuthPatcher.java not found at ${patcherJava}`;
|
|
||||||
console.error(error);
|
|
||||||
return { success: false, error };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download ASM libraries if not present
|
|
||||||
if (progressCallback) progressCallback('Checking ASM libraries...', 20);
|
|
||||||
await this.ensureAsmLibraries(libDir);
|
|
||||||
|
|
||||||
// Compile patcher if needed
|
|
||||||
if (progressCallback) progressCallback('Compiling patcher...', 30);
|
|
||||||
const compileResult = await this.compileDualAuthPatcher(java, patcherDir, libDir);
|
|
||||||
if (!compileResult.success) {
|
|
||||||
return { success: false, error: compileResult.error };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create backup
|
// Create backup
|
||||||
if (progressCallback) progressCallback('Creating backup...', 40);
|
if (progressCallback) progressCallback('Creating backup...', 10);
|
||||||
console.log('Creating backup...');
|
console.log('Creating backup...');
|
||||||
this.backupClient(serverPath);
|
this.backupClient(serverPath);
|
||||||
|
|
||||||
// Run the patcher
|
// Download pre-patched JAR
|
||||||
if (progressCallback) progressCallback('Patching server JAR...', 50);
|
if (progressCallback) progressCallback('Downloading patched server JAR...', 30);
|
||||||
console.log('Running DualAuthPatcher...');
|
console.log('Downloading pre-patched HytaleServer.jar from https://files.hytalef2p.com/jar');
|
||||||
|
|
||||||
const classpath = [
|
try {
|
||||||
patcherDir,
|
const https = require('https');
|
||||||
path.join(libDir, 'asm-9.6.jar'),
|
const url = 'https://files.hytalef2p.com/jar';
|
||||||
path.join(libDir, 'asm-tree-9.6.jar'),
|
|
||||||
path.join(libDir, 'asm-util-9.6.jar')
|
|
||||||
].join(process.platform === 'win32' ? ';' : ':');
|
|
||||||
|
|
||||||
const patchResult = await this.runDualAuthPatcher(java, classpath, serverPath, newDomain);
|
await new Promise((resolve, reject) => {
|
||||||
|
https.get(url, (response) => {
|
||||||
|
if (response.statusCode === 302 || response.statusCode === 301) {
|
||||||
|
// Follow redirect
|
||||||
|
https.get(response.headers.location, (redirectResponse) => {
|
||||||
|
if (redirectResponse.statusCode !== 200) {
|
||||||
|
reject(new Error(`Failed to download: HTTP ${redirectResponse.statusCode}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = fs.createWriteStream(serverPath);
|
||||||
|
const totalSize = parseInt(redirectResponse.headers['content-length'], 10);
|
||||||
|
let downloaded = 0;
|
||||||
|
|
||||||
|
redirectResponse.on('data', (chunk) => {
|
||||||
|
downloaded += chunk.length;
|
||||||
|
if (progressCallback && totalSize) {
|
||||||
|
const percent = 30 + Math.floor((downloaded / totalSize) * 60);
|
||||||
|
progressCallback(`Downloading... ${(downloaded / 1024 / 1024).toFixed(2)} MB`, percent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
redirectResponse.pipe(file);
|
||||||
|
file.on('finish', () => {
|
||||||
|
file.close();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}).on('error', reject);
|
||||||
|
} else if (response.statusCode === 200) {
|
||||||
|
const file = fs.createWriteStream(serverPath);
|
||||||
|
const totalSize = parseInt(response.headers['content-length'], 10);
|
||||||
|
let downloaded = 0;
|
||||||
|
|
||||||
|
response.on('data', (chunk) => {
|
||||||
|
downloaded += chunk.length;
|
||||||
|
if (progressCallback && totalSize) {
|
||||||
|
const percent = 30 + Math.floor((downloaded / totalSize) * 60);
|
||||||
|
progressCallback(`Downloading... ${(downloaded / 1024 / 1024).toFixed(2)} MB`, percent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
response.pipe(file);
|
||||||
|
file.on('finish', () => {
|
||||||
|
file.close();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Failed to download: HTTP ${response.statusCode}`));
|
||||||
|
}
|
||||||
|
}).on('error', (err) => {
|
||||||
|
fs.unlink(serverPath, () => {});
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(' Download successful');
|
||||||
|
|
||||||
if (patchResult.success) {
|
|
||||||
// Mark as patched
|
// Mark as patched
|
||||||
fs.writeFileSync(patchFlagFile, JSON.stringify({
|
fs.writeFileSync(patchFlagFile, JSON.stringify({
|
||||||
domain: newDomain,
|
domain: newDomain,
|
||||||
patchedAt: new Date().toISOString(),
|
patchedAt: new Date().toISOString(),
|
||||||
patcher: 'DualAuthPatcher'
|
patcher: 'PrePatchedDownload',
|
||||||
|
source: 'https://download.sanasol.ws/download/HytaleServer.jar'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (progressCallback) progressCallback('Server patching complete', 100);
|
if (progressCallback) progressCallback('Server patching complete', 100);
|
||||||
console.log('=== Server Patching Complete ===');
|
console.log('=== Server Patching Complete ===');
|
||||||
return { success: true, patchCount: patchResult.patchCount || 1 };
|
return { success: true, patchCount: 1 };
|
||||||
} else {
|
|
||||||
return { success: false, error: patchResult.error };
|
} catch (downloadError) {
|
||||||
|
console.error(`Failed to download patched JAR: ${downloadError.message}`);
|
||||||
|
|
||||||
|
// Restore backup on failure
|
||||||
|
const backupPath = serverPath + '.original';
|
||||||
|
if (fs.existsSync(backupPath)) {
|
||||||
|
fs.copyFileSync(backupPath, serverPath);
|
||||||
|
console.log('Restored backup after download failure');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: `Failed to download patched server: ${downloadError.message}` };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -802,10 +820,24 @@ class ClientPatcher {
|
|||||||
].join(process.platform === 'win32' ? ';' : ':');
|
].join(process.platform === 'win32' ? ';' : ':');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
execSync(`"${javac}" -cp "${classpath}" -d "${patcherDir}" "${patcherJava}"`, {
|
// Fix PATH for packaged Electron apps on Windows
|
||||||
|
const execOptions = {
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
cwd: patcherDir
|
cwd: patcherDir,
|
||||||
});
|
env: { ...process.env }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add system32 to PATH for Windows to find cmd.exe
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
const systemRoot = process.env.SystemRoot || 'C:\\WINDOWS';
|
||||||
|
const systemPath = `${systemRoot}\\system32;${systemRoot};${systemRoot}\\System32\\Wbem`;
|
||||||
|
execOptions.env.PATH = execOptions.env.PATH
|
||||||
|
? `${systemPath};${execOptions.env.PATH}`
|
||||||
|
: systemPath;
|
||||||
|
execOptions.shell = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
execSync(`"${javac}" -cp "${classpath}" -d "${patcherDir}" "${patcherJava}"`, execOptions);
|
||||||
console.log(' Compilation successful');
|
console.log(' Compilation successful');
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ async function downloadFile(url, dest, progressCallback, maxRetries = 5) {
|
|||||||
console.log(`Download attempt ${attempt + 1}/${maxRetries} for ${url}`);
|
console.log(`Download attempt ${attempt + 1}/${maxRetries} for ${url}`);
|
||||||
|
|
||||||
if (attempt > 0 && progressCallback) {
|
if (attempt > 0 && progressCallback) {
|
||||||
// Exponential backoff with jitter
|
// Exponential backoff with jitter - longer delays for unstable connections
|
||||||
const baseDelay = 2000;
|
const baseDelay = 3000;
|
||||||
const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
|
const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
|
||||||
const jitter = Math.random() * 1000;
|
const jitter = Math.random() * 2000;
|
||||||
const delay = Math.min(exponentialDelay + jitter, 30000);
|
const delay = Math.min(exponentialDelay + jitter, 60000);
|
||||||
|
|
||||||
progressCallback(`Retry ${attempt}/${maxRetries - 1}...`, null, null, null, null, retryState);
|
progressCallback(`Retry ${attempt}/${maxRetries - 1}...`, null, null, null, null, retryState);
|
||||||
await new Promise(resolve => setTimeout(resolve, delay));
|
await new Promise(resolve => setTimeout(resolve, delay));
|
||||||
@@ -78,9 +78,9 @@ async function downloadFile(url, dest, progressCallback, maxRetries = 5) {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const timeSinceLastProgress = now - lastProgressTime;
|
const timeSinceLastProgress = now - lastProgressTime;
|
||||||
|
|
||||||
// Only timeout if no data received for 5 minutes (300 seconds)
|
// Only timeout if no data received for 10 minutes (600 seconds) - for very slow connections
|
||||||
if (timeSinceLastProgress > 300000 && hasReceivedData) {
|
if (timeSinceLastProgress > 600000 && hasReceivedData) {
|
||||||
console.log('Download stalled for 5 minutes, aborting...');
|
console.log('Download stalled for 10 minutes, aborting...');
|
||||||
console.log(`Download had progress before stall: ${(downloaded / 1024 / 1024).toFixed(2)} MB`);
|
console.log(`Download had progress before stall: ${(downloaded / 1024 / 1024).toFixed(2)} MB`);
|
||||||
controller.abort();
|
controller.abort();
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ async function downloadFile(url, dest, progressCallback, maxRetries = 5) {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: url,
|
url: url,
|
||||||
responseType: 'stream',
|
responseType: 'stream',
|
||||||
timeout: 60000,
|
timeout: 120000, // 120 seconds for slow connections
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
validateStatus: function (status) {
|
validateStatus: function (status) {
|
||||||
@@ -403,8 +403,9 @@ async function downloadFile(url, dest, progressCallback, maxRetries = 5) {
|
|||||||
const retryableErrors = [
|
const retryableErrors = [
|
||||||
'ECONNRESET', 'ENOTFOUND', 'ECONNREFUSED', 'ETIMEDOUT',
|
'ECONNRESET', 'ENOTFOUND', 'ECONNREFUSED', 'ETIMEDOUT',
|
||||||
'ESOCKETTIMEDOUT', 'EPROTO', 'ENETDOWN', 'EHOSTUNREACH',
|
'ESOCKETTIMEDOUT', 'EPROTO', 'ENETDOWN', 'EHOSTUNREACH',
|
||||||
|
'ECONNABORTED', 'EPIPE', 'ENETRESET', 'EADDRNOTAVAIL',
|
||||||
'ERR_NETWORK', 'ERR_INTERNET_DISCONNECTED', 'ERR_CONNECTION_RESET',
|
'ERR_NETWORK', 'ERR_INTERNET_DISCONNECTED', 'ERR_CONNECTION_RESET',
|
||||||
'ERR_CONNECTION_TIMED_OUT', 'ERR_NAME_NOT_RESOLVED'
|
'ERR_CONNECTION_TIMED_OUT', 'ERR_NAME_NOT_RESOLVED', 'ERR_CONNECTION_CLOSED'
|
||||||
];
|
];
|
||||||
|
|
||||||
const isRetryable = retryableErrors.includes(error.code) ||
|
const isRetryable = retryableErrors.includes(error.code) ||
|
||||||
|
|||||||
Reference in New Issue
Block a user