mirror of
https://git.sanhost.net/sanasol/hytale-f2p
synced 2026-02-26 08:01:46 -03:00
Consistent order across all files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
406 lines
13 KiB
JavaScript
406 lines
13 KiB
JavaScript
/**
|
|
* Localhost/Local Development Code Backup
|
|
*
|
|
* This file contains code removed from clientPatcher.js that was used for local development.
|
|
* To re-enable local dev mode, merge this code back into clientPatcher.js.
|
|
*
|
|
* Backed up: 2026-01-28
|
|
*/
|
|
|
|
// =============================================================================
|
|
// LOCAL PATCHER PATHS (was in ensurePatcherDownloaded method)
|
|
// =============================================================================
|
|
|
|
// Check for local patcher (for local development)
|
|
const localPatcherPaths = [
|
|
path.join(__dirname, '..', '..', '..', 'hytale-auth-server', 'patcher', 'DualAuthPatcher.java'),
|
|
path.join(__dirname, '..', '..', '..', '..', 'hytale-auth-server', 'patcher', 'DualAuthPatcher.java'),
|
|
'/Users/sanasol/code/pterodactyl-hytale/hytale-auth-server/patcher/DualAuthPatcher.java'
|
|
];
|
|
|
|
// Check if we should use local patcher (localhost domain = local dev)
|
|
const domain = getTargetDomain();
|
|
const isLocalDev = domain.startsWith('localhost') || domain.startsWith('127.0.0.1');
|
|
|
|
if (isLocalDev) {
|
|
for (const localPath of localPatcherPaths) {
|
|
if (fs.existsSync(localPath)) {
|
|
console.log(`Using local DualAuthPatcher: ${localPath}`);
|
|
// Always copy fresh for local dev to pick up changes
|
|
fs.copyFileSync(localPath, patcherJava);
|
|
// Delete compiled class to force recompile
|
|
const patcherClass = path.join(patcherDir, 'DualAuthPatcher.class');
|
|
if (fs.existsSync(patcherClass)) {
|
|
fs.unlinkSync(patcherClass);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
console.log('Local patcher not found, falling back to download...');
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// LEGACY SERVER PATCHER (was patchServerLegacy method)
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Legacy server patcher (simple domain replacement, no dual auth)
|
|
* Use patchServer() for full dual auth support
|
|
*/
|
|
async patchServerLegacy(serverPath, progressCallback) {
|
|
const newDomain = this.getNewDomain();
|
|
const strategy = this.getDomainStrategy(newDomain);
|
|
|
|
console.log('=== Legacy Server Patcher ===');
|
|
console.log(`Target: ${serverPath}`);
|
|
console.log(`Domain: ${newDomain} (${newDomain.length} chars)`);
|
|
|
|
if (!fs.existsSync(serverPath)) {
|
|
return { success: false, error: `Server JAR not found: ${serverPath}` };
|
|
}
|
|
|
|
if (progressCallback) progressCallback('Patching server...', 20);
|
|
|
|
console.log('Opening server JAR...');
|
|
const zip = new AdmZip(serverPath);
|
|
const entries = zip.getEntries();
|
|
|
|
let totalCount = 0;
|
|
const oldUtf8 = this.stringToUtf8(ORIGINAL_DOMAIN);
|
|
|
|
for (const entry of entries) {
|
|
const name = entry.entryName;
|
|
if (name.endsWith('.class') || name.endsWith('.properties') ||
|
|
name.endsWith('.json') || name.endsWith('.xml') || name.endsWith('.yml')) {
|
|
const data = entry.getData();
|
|
if (data.includes(oldUtf8)) {
|
|
const { buffer: patchedData, count } = this.findAndReplaceDomainUtf8(data, ORIGINAL_DOMAIN, strategy.mainDomain);
|
|
if (count > 0) {
|
|
zip.updateFile(entry.entryName, patchedData);
|
|
totalCount += count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (totalCount > 0) {
|
|
zip.writeZip(serverPath);
|
|
}
|
|
|
|
if (progressCallback) progressCallback('Complete', 100);
|
|
return { success: true, patchCount: totalCount };
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// DUALAUTHPATCHER FLOW FOR NON-STANDARD DOMAINS (was in patchServer method)
|
|
// =============================================================================
|
|
|
|
// For non-standard domains, use DualAuthPatcher for proper bytecode patching
|
|
const isStandardDomain = newDomain === 'auth.sanasol.ws' || newDomain === 'sanasol.ws';
|
|
|
|
if (!isStandardDomain) {
|
|
console.log(`Non-standard domain "${newDomain}" - using DualAuthPatcher`);
|
|
|
|
// Find Java
|
|
const java = this.findJava();
|
|
if (!java) {
|
|
console.error('Java not found - cannot run DualAuthPatcher');
|
|
console.error('Please install Java or use the bundled JRE');
|
|
return { success: false, error: 'Java not found for DualAuthPatcher' };
|
|
}
|
|
console.log(` Using Java: ${java}`);
|
|
|
|
// Setup patcher directory
|
|
const patcherDir = path.join(path.dirname(serverPath), '.patcher');
|
|
const libDir = path.join(patcherDir, 'lib');
|
|
|
|
try {
|
|
// Download patcher and libraries
|
|
if (progressCallback) progressCallback('Downloading DualAuthPatcher...', 20);
|
|
await this.ensurePatcherDownloaded(patcherDir);
|
|
|
|
if (progressCallback) progressCallback('Downloading ASM libraries...', 30);
|
|
await this.ensureAsmLibraries(libDir);
|
|
|
|
// Compile patcher
|
|
if (progressCallback) progressCallback('Compiling patcher...', 40);
|
|
const compileResult = await this.compileDualAuthPatcher(java, patcherDir, libDir);
|
|
if (!compileResult.success) {
|
|
return { success: false, error: compileResult.error };
|
|
}
|
|
|
|
// Build classpath
|
|
const classpath = [
|
|
patcherDir,
|
|
path.join(libDir, 'asm-9.6.jar'),
|
|
path.join(libDir, 'asm-tree-9.6.jar'),
|
|
path.join(libDir, 'asm-util-9.6.jar')
|
|
].join(process.platform === 'win32' ? ';' : ':');
|
|
|
|
// Run DualAuthPatcher with custom domain
|
|
if (progressCallback) progressCallback('Running DualAuthPatcher...', 60);
|
|
console.log(` Running DualAuthPatcher with domain: ${newDomain}`);
|
|
|
|
const patchResult = await this.runDualAuthPatcher(java, classpath, serverPath, newDomain);
|
|
|
|
if (patchResult.success) {
|
|
// Mark as patched
|
|
fs.writeFileSync(patchFlagFile, JSON.stringify({
|
|
domain: newDomain,
|
|
patchedAt: new Date().toISOString(),
|
|
patcher: 'DualAuthPatcher',
|
|
output: patchResult.stdout
|
|
}, null, 2));
|
|
|
|
if (progressCallback) progressCallback('Server patching complete', 100);
|
|
console.log('=== Server Patching Complete (DualAuthPatcher) ===');
|
|
return { success: true, patchCount: 1 };
|
|
} else {
|
|
console.error('DualAuthPatcher failed:', patchResult.error);
|
|
return { success: false, error: patchResult.error };
|
|
}
|
|
|
|
} catch (err) {
|
|
console.error('Failed to run DualAuthPatcher:', err.message);
|
|
return { success: false, error: err.message };
|
|
}
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// HELPER METHODS FOR DUALAUTHPATCHER (keep if re-enabling non-standard domains)
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Find Java executable - uses bundled JRE first (same as game uses)
|
|
* Falls back to system Java if bundled not available
|
|
*/
|
|
findJava() {
|
|
// 1. Try bundled JRE first (comes with the game)
|
|
try {
|
|
const bundled = getBundledJavaPath(JRE_DIR);
|
|
if (bundled && fs.existsSync(bundled)) {
|
|
console.log(`Using bundled Java: ${bundled}`);
|
|
return bundled;
|
|
}
|
|
} catch (e) {
|
|
// Bundled not available
|
|
}
|
|
|
|
// 2. Try javaManager's getJavaExec (handles all fallbacks)
|
|
try {
|
|
const javaExec = getJavaExec(JRE_DIR);
|
|
if (javaExec && fs.existsSync(javaExec)) {
|
|
console.log(`Using Java from javaManager: ${javaExec}`);
|
|
return javaExec;
|
|
}
|
|
} catch (e) {
|
|
// Not available
|
|
}
|
|
|
|
// 3. Check JAVA_HOME
|
|
if (process.env.JAVA_HOME) {
|
|
const javaHome = process.env.JAVA_HOME;
|
|
const javaBin = path.join(javaHome, 'bin', process.platform === 'win32' ? 'java.exe' : 'java');
|
|
if (fs.existsSync(javaBin)) {
|
|
console.log(`Using Java from JAVA_HOME: ${javaBin}`);
|
|
return javaBin;
|
|
}
|
|
}
|
|
|
|
// 4. Try 'java' from PATH
|
|
try {
|
|
execSync('java -version 2>&1', { encoding: 'utf8' });
|
|
console.log('Using Java from PATH');
|
|
return 'java';
|
|
} catch (e) {
|
|
// Not in PATH
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get DualAuthPatcher - downloads from GitHub
|
|
*/
|
|
async ensurePatcherDownloaded(patcherDir) {
|
|
const patcherJava = path.join(patcherDir, 'DualAuthPatcher.java');
|
|
const patcherUrl = 'https://raw.githubusercontent.com/sanasol/hytale-auth-server/master/patcher/DualAuthPatcher.java';
|
|
|
|
if (!fs.existsSync(patcherDir)) {
|
|
fs.mkdirSync(patcherDir, { recursive: true });
|
|
}
|
|
|
|
if (!fs.existsSync(patcherJava)) {
|
|
console.log('Downloading DualAuthPatcher from hytale-auth-server...');
|
|
try {
|
|
const https = require('https');
|
|
await new Promise((resolve, reject) => {
|
|
const file = fs.createWriteStream(patcherJava);
|
|
https.get(patcherUrl, (response) => {
|
|
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
https.get(response.headers.location, (redirectResponse) => {
|
|
redirectResponse.pipe(file);
|
|
file.on('finish', () => {
|
|
file.close();
|
|
resolve();
|
|
});
|
|
}).on('error', reject);
|
|
} else {
|
|
response.pipe(file);
|
|
file.on('finish', () => {
|
|
file.close();
|
|
resolve();
|
|
});
|
|
}
|
|
}).on('error', (err) => {
|
|
fs.unlink(patcherJava, () => {});
|
|
reject(err);
|
|
});
|
|
});
|
|
console.log(' Downloaded DualAuthPatcher.java');
|
|
} catch (e) {
|
|
console.error(` Failed to download DualAuthPatcher: ${e.message}`);
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Download ASM libraries if not present
|
|
*/
|
|
async ensureAsmLibraries(libDir) {
|
|
if (!fs.existsSync(libDir)) {
|
|
fs.mkdirSync(libDir, { recursive: true });
|
|
}
|
|
|
|
const libs = [
|
|
{ name: 'asm-9.6.jar', url: 'https://repo1.maven.org/maven2/org/ow2/asm/asm/9.6/asm-9.6.jar' },
|
|
{ name: 'asm-tree-9.6.jar', url: 'https://repo1.maven.org/maven2/org/ow2/asm/asm-tree/9.6/asm-tree-9.6.jar' },
|
|
{ name: 'asm-util-9.6.jar', url: 'https://repo1.maven.org/maven2/org/ow2/asm/asm-util/9.6/asm-util-9.6.jar' }
|
|
];
|
|
|
|
for (const lib of libs) {
|
|
const libPath = path.join(libDir, lib.name);
|
|
if (!fs.existsSync(libPath)) {
|
|
console.log(`Downloading ${lib.name}...`);
|
|
try {
|
|
const https = require('https');
|
|
await new Promise((resolve, reject) => {
|
|
const file = fs.createWriteStream(libPath);
|
|
https.get(lib.url, (response) => {
|
|
response.pipe(file);
|
|
file.on('finish', () => {
|
|
file.close();
|
|
resolve();
|
|
});
|
|
}).on('error', (err) => {
|
|
fs.unlink(libPath, () => {});
|
|
reject(err);
|
|
});
|
|
});
|
|
console.log(` Downloaded ${lib.name}`);
|
|
} catch (e) {
|
|
console.error(` Failed to download ${lib.name}: ${e.message}`);
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compile DualAuthPatcher if needed
|
|
*/
|
|
async compileDualAuthPatcher(java, patcherDir, libDir) {
|
|
const patcherClass = path.join(patcherDir, 'DualAuthPatcher.class');
|
|
const patcherJava = path.join(patcherDir, 'DualAuthPatcher.java');
|
|
|
|
if (fs.existsSync(patcherClass)) {
|
|
const classTime = fs.statSync(patcherClass).mtime;
|
|
const javaTime = fs.statSync(patcherJava).mtime;
|
|
if (classTime > javaTime) {
|
|
console.log('DualAuthPatcher already compiled');
|
|
return { success: true };
|
|
}
|
|
}
|
|
|
|
console.log('Compiling DualAuthPatcher...');
|
|
|
|
const javac = java.replace(/java(\.exe)?$/, 'javac$1');
|
|
const classpath = [
|
|
path.join(libDir, 'asm-9.6.jar'),
|
|
path.join(libDir, 'asm-tree-9.6.jar'),
|
|
path.join(libDir, 'asm-util-9.6.jar')
|
|
].join(process.platform === 'win32' ? ';' : ':');
|
|
|
|
try {
|
|
const execOptions = {
|
|
stdio: 'pipe',
|
|
cwd: patcherDir,
|
|
env: { ...process.env }
|
|
};
|
|
|
|
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');
|
|
return { success: true };
|
|
} catch (e) {
|
|
const error = `Failed to compile DualAuthPatcher: ${e.message}`;
|
|
console.error(error);
|
|
if (e.stderr) console.error(e.stderr.toString());
|
|
return { success: false, error };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run DualAuthPatcher on the server JAR
|
|
*/
|
|
async runDualAuthPatcher(java, classpath, serverPath, domain) {
|
|
return new Promise((resolve) => {
|
|
const args = ['-cp', classpath, 'DualAuthPatcher', serverPath];
|
|
const env = { ...process.env, HYTALE_AUTH_DOMAIN: domain };
|
|
|
|
console.log(`Running: java ${args.join(' ')}`);
|
|
console.log(` HYTALE_AUTH_DOMAIN=${domain}`);
|
|
|
|
const proc = spawn(java, args, { env, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
|
|
let stdout = '';
|
|
let stderr = '';
|
|
|
|
proc.stdout.on('data', (data) => {
|
|
const str = data.toString();
|
|
stdout += str;
|
|
console.log(str.trim());
|
|
});
|
|
|
|
proc.stderr.on('data', (data) => {
|
|
const str = data.toString();
|
|
stderr += str;
|
|
console.error(str.trim());
|
|
});
|
|
|
|
proc.on('close', (code) => {
|
|
if (code === 0) {
|
|
resolve({ success: true, stdout });
|
|
} else {
|
|
resolve({ success: false, error: `Patcher exited with code ${code}: ${stderr}` });
|
|
}
|
|
});
|
|
|
|
proc.on('error', (err) => {
|
|
resolve({ success: false, error: `Failed to run patcher: ${err.message}` });
|
|
});
|
|
});
|
|
}
|