MacOs build crash on game launch and server not botting

This commit is contained in:
sanasol
2026-01-17 14:32:43 +01:00
parent f49c5c02dd
commit 335db04cee

View File

@@ -284,7 +284,7 @@ async function installButler(toolsDir = TOOLS_DIR) {
if (!fs.existsSync(toolsDir)) { if (!fs.existsSync(toolsDir)) {
fs.mkdirSync(toolsDir, { recursive: true }); fs.mkdirSync(toolsDir, { recursive: true });
} }
const butlerName = process.platform === 'win32' ? 'butler.exe' : 'butler'; const butlerName = process.platform === 'win32' ? 'butler.exe' : 'butler';
const butlerPath = path.join(toolsDir, butlerName); const butlerPath = path.join(toolsDir, butlerName);
const zipPath = path.join(toolsDir, 'butler.zip'); const zipPath = path.join(toolsDir, 'butler.zip');
@@ -349,7 +349,7 @@ async function downloadPWR(version = 'release', fileName = '1.pwr', progressCall
const osName = getOS(); const osName = getOS();
const arch = getArch(); const arch = getArch();
const url = `https://game-patches.hytale.com/patches/${osName}/${arch}/${version}/0/${fileName}`; const url = `https://game-patches.hytale.com/patches/${osName}/${arch}/${version}/0/${fileName}`;
const dest = path.join(cacheDir, fileName); const dest = path.join(cacheDir, fileName);
if (fs.existsSync(dest)) { if (fs.existsSync(dest)) {
@@ -360,7 +360,7 @@ async function downloadPWR(version = 'release', fileName = '1.pwr', progressCall
console.log('Fetching PWR patch file:', url); console.log('Fetching PWR patch file:', url);
await downloadFile(url, dest, progressCallback); await downloadFile(url, dest, progressCallback);
console.log('PWR saved to:', dest); console.log('PWR saved to:', dest);
return dest; return dest;
} }
@@ -368,9 +368,9 @@ async function applyPWR(pwrFile, progressCallback, gameDir = GAME_DIR, toolsDir
const butlerPath = await installButler(toolsDir); const butlerPath = await installButler(toolsDir);
const gameLatest = gameDir; const gameLatest = gameDir;
const stagingDir = path.join(gameLatest, 'staging-temp'); const stagingDir = path.join(gameLatest, 'staging-temp');
const clientPath = findClientPath(gameLatest); const clientPath = findClientPath(gameLatest);
if (clientPath) { if (clientPath) {
console.log('Game files detected, skipping patch installation.'); console.log('Game files detected, skipping patch installation.');
return; return;
@@ -388,11 +388,11 @@ async function applyPWR(pwrFile, progressCallback, gameDir = GAME_DIR, toolsDir
} }
console.log('Installing game patch...'); console.log('Installing game patch...');
if (!fs.existsSync(butlerPath)) { if (!fs.existsSync(butlerPath)) {
throw new Error(`Butler tool not found at: ${butlerPath}`); throw new Error(`Butler tool not found at: ${butlerPath}`);
} }
if (!fs.existsSync(pwrFile)) { if (!fs.existsSync(pwrFile)) {
throw new Error(`PWR file not found at: ${pwrFile}`); throw new Error(`PWR file not found at: ${pwrFile}`);
} }
@@ -404,7 +404,7 @@ async function applyPWR(pwrFile, progressCallback, gameDir = GAME_DIR, toolsDir
pwrFile, pwrFile,
gameLatest gameLatest
]; ];
try { try {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const child = execFile(butlerPath, args, { const child = execFile(butlerPath, args, {
@@ -438,7 +438,7 @@ async function downloadJRE(progressCallback, cacheDir = CACHE_DIR, jreDir = JRE_
if (!fs.existsSync(cacheDir)) { if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir, { recursive: true }); fs.mkdirSync(cacheDir, { recursive: true });
} }
const osName = getOS(); const osName = getOS();
const arch = getArch(); const arch = getArch();
@@ -488,7 +488,7 @@ async function downloadJRE(progressCallback, cacheDir = CACHE_DIR, jreDir = JRE_
const hashSum = crypto.createHash('sha256'); const hashSum = crypto.createHash('sha256');
hashSum.update(fileBuffer); hashSum.update(fileBuffer);
const hex = hashSum.digest('hex'); const hex = hashSum.digest('hex');
if (hex !== platform.sha256) { if (hex !== platform.sha256) {
fs.unlinkSync(cacheFile); fs.unlinkSync(cacheFile);
throw new Error(`File validation failed: expected ${platform.sha256} but got ${hex}`); throw new Error(`File validation failed: expected ${platform.sha256} but got ${hex}`);
@@ -544,7 +544,7 @@ function extractZip(zipPath, dest) {
for (const entry of entries) { for (const entry of entries) {
const entryPath = path.join(dest, entry.entryName); const entryPath = path.join(dest, entry.entryName);
const resolvedPath = path.resolve(entryPath); const resolvedPath = path.resolve(entryPath);
const resolvedDest = path.resolve(dest); const resolvedDest = path.resolve(dest);
if (!resolvedPath.startsWith(resolvedDest)) { if (!resolvedPath.startsWith(resolvedDest)) {
@@ -575,7 +575,7 @@ function extractTarGz(tarGzPath, dest) {
function flattenJREDir(jreLatest) { function flattenJREDir(jreLatest) {
try { try {
const entries = fs.readdirSync(jreLatest, { withFileTypes: true }); const entries = fs.readdirSync(jreLatest, { withFileTypes: true });
if (entries.length !== 1 || !entries[0].isDirectory()) { if (entries.length !== 1 || !entries[0].isDirectory()) {
return; return;
} }
@@ -655,7 +655,7 @@ function isGameInstalled() {
async function uninstallGame() { async function uninstallGame() {
const appDir = getResolvedAppDir(); const appDir = getResolvedAppDir();
if (!fs.existsSync(appDir)) { if (!fs.existsSync(appDir)) {
throw new Error('Game is not installed'); throw new Error('Game is not installed');
} }
@@ -663,7 +663,7 @@ async function uninstallGame() {
try { try {
fs.rmSync(appDir, { recursive: true, force: true }); fs.rmSync(appDir, { recursive: true, force: true });
console.log('Game uninstalled successfully - removed entire HytaleF2P folder'); console.log('Game uninstalled successfully - removed entire HytaleF2P folder');
if (fs.existsSync(CONFIG_FILE)) { if (fs.existsSync(CONFIG_FILE)) {
const config = loadConfig(); const config = loadConfig();
delete config.installPath; delete config.installPath;
@@ -680,21 +680,21 @@ async function launchGame(playerName = 'Player', progressCallback, javaPathOverr
const customToolsDir = path.join(customAppDir, 'butler'); const customToolsDir = path.join(customAppDir, 'butler');
const customGameDir = path.join(customAppDir, 'release', 'package', 'game', 'latest'); const customGameDir = path.join(customAppDir, 'release', 'package', 'game', 'latest');
const customJreDir = path.join(customAppDir, 'release', 'package', 'jre', 'latest'); const customJreDir = path.join(customAppDir, 'release', 'package', 'jre', 'latest');
[customAppDir, customCacheDir, customToolsDir].forEach(dir => { [customAppDir, customCacheDir, customToolsDir].forEach(dir => {
if (!fs.existsSync(dir)) { if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true }); fs.mkdirSync(dir, { recursive: true });
} }
}); });
saveUsername(playerName); saveUsername(playerName);
if (installPathOverride) { if (installPathOverride) {
saveInstallPath(installPathOverride); saveInstallPath(installPathOverride);
} }
const configuredJava = (javaPathOverride !== undefined && javaPathOverride !== null const configuredJava = (javaPathOverride !== undefined && javaPathOverride !== null
? javaPathOverride ? javaPathOverride
: loadJavaPath() || '').trim(); : loadJavaPath() || '').trim();
let javaBin = null; let javaBin = null;
if (configuredJava) { if (configuredJava) {
@@ -737,6 +737,73 @@ async function launchGame(playerName = 'Player', progressCallback, javaPathOverr
throw new Error(`Game client missing. Tried: ${attempted}`); throw new Error(`Game client missing. Tried: ${attempted}`);
} }
// macOS: Remove quarantine and ad-hoc sign binaries to prevent SIGABRT crashes
if (process.platform === 'darwin') {
try {
const appBundle = path.join(gameLatest, 'Client', 'Hytale.app');
const serverDir = path.join(gameLatest, 'Server');
// Helper to remove quarantine and sign a path
const signPath = async (targetPath, deep = false) => {
await execAsync(`xattr -cr "${targetPath}"`).catch(() => {});
const deepFlag = deep ? '--deep ' : '';
await execAsync(`codesign --force ${deepFlag}--sign - "${targetPath}"`).catch(() => {});
};
// Sign app bundle or client binary
if (fs.existsSync(appBundle)) {
await signPath(appBundle, true);
console.log('Signed macOS app bundle');
} else {
await signPath(path.dirname(clientPath), true);
console.log('Signed macOS client binary');
}
// Sign Java runtime
if (javaBin && fs.existsSync(javaBin)) {
// Navigate from bin/java up to the JRE bundle root (contains Contents/)
let jreRoot = path.dirname(path.dirname(javaBin));
if (jreRoot.endsWith('Home')) {
jreRoot = path.dirname(path.dirname(jreRoot));
}
await signPath(jreRoot, true);
await signPath(javaBin, false);
console.log('Signed Java runtime');
}
// Sign server directory native libraries
if (fs.existsSync(serverDir)) {
await execAsync(`xattr -cr "${serverDir}"`).catch(() => {});
await execAsync(`find "${serverDir}" -type f -perm +111 -exec codesign --force --sign - {} \\;`).catch(() => {});
console.log('Signed server binaries');
}
// Create java wrapper script that adds --disable-sentry flag for server launches
if (javaBin && fs.existsSync(javaBin)) {
const javaWrapperPath = path.join(path.dirname(javaBin), 'java-wrapper');
const wrapperScript = `#!/bin/bash
# Java wrapper for macOS - adds --disable-sentry to fix Sentry hang issue
REAL_JAVA="${javaBin}"
ARGS=("$@")
for i in "\${!ARGS[@]}"; do
if [[ "\${ARGS[$i]}" == *"HytaleServer.jar"* ]]; then
ARGS=("\${ARGS[@]:0:$((i+1))}" "--disable-sentry" "\${ARGS[@]:$((i+1))}")
break
fi
done
exec "$REAL_JAVA" "\${ARGS[@]}"
`;
fs.writeFileSync(javaWrapperPath, wrapperScript, { mode: 0o755 });
await signPath(javaWrapperPath, false);
console.log('Created java wrapper with --disable-sentry fix');
javaBin = javaWrapperPath;
}
} catch (signError) {
console.log('Notice: macOS signing step failed:', signError.message);
console.log('The game may still launch if Gatekeeper allows it');
}
}
const uuid = getUuidForUser(playerName); const uuid = getUuidForUser(playerName);
const args = [ const args = [
'--app-dir', gameLatest, '--app-dir', gameLatest,
@@ -751,7 +818,7 @@ async function launchGame(playerName = 'Player', progressCallback, javaPathOverr
} }
console.log('Starting game...'); console.log('Starting game...');
console.log(`Command: "${clientPath}" ${args.join(' ')}`); console.log(`Command: "${clientPath}" ${args.join(' ')}`);
const child = exec(`"${clientPath}" ${args.map(a => `"${a}"`).join(' ')}`, { const child = exec(`"${clientPath}" ${args.map(a => `"${a}"`).join(' ')}`, {
stdio: 'inherit', stdio: 'inherit',
detached: true detached: true