v2.4.1: Replace raw wrapper script editor with structured config UI

Replace the raw textarea script editor with a structured form for Java
wrapper configuration. Users now manage two lists (JVM flags to strip,
args to inject with server/always condition) instead of editing bash/batch
scripts directly. Scripts are generated at launch time from the structured
config. Includes collapsible script preview for power users.
This commit is contained in:
sanasol
2026-02-24 16:53:19 +01:00
parent 19c8991a44
commit 0d15659dc0
10 changed files with 770 additions and 20 deletions

View File

@@ -858,6 +858,212 @@ function checkLaunchReady() {
};
}
// =============================================================================
// JAVA WRAPPER CONFIGURATION (Structured)
// =============================================================================
const DEFAULT_WRAPPER_CONFIG = {
stripFlags: ['-XX:+UseCompactObjectHeaders'],
injectArgs: [
{ arg: '--disable-sentry', condition: 'server' }
]
};
function getDefaultWrapperConfig() {
return JSON.parse(JSON.stringify(DEFAULT_WRAPPER_CONFIG));
}
function loadWrapperConfig() {
const config = loadConfig();
if (config.javaWrapperConfig && typeof config.javaWrapperConfig === 'object') {
const wc = config.javaWrapperConfig;
if (Array.isArray(wc.stripFlags) && Array.isArray(wc.injectArgs)) {
const loaded = JSON.parse(JSON.stringify(wc));
// Normalize entries: ensure every injectArg has a valid condition
for (const entry of loaded.injectArgs) {
if (!['server', 'always'].includes(entry.condition)) {
entry.condition = 'always';
}
}
return loaded;
}
}
return getDefaultWrapperConfig();
}
function saveWrapperConfig(wrapperConfig) {
if (!wrapperConfig || typeof wrapperConfig !== 'object') {
throw new Error('Invalid wrapper config');
}
if (!Array.isArray(wrapperConfig.stripFlags) || !Array.isArray(wrapperConfig.injectArgs)) {
throw new Error('Invalid wrapper config structure');
}
// Validate injectArgs entries
for (const entry of wrapperConfig.injectArgs) {
if (!entry.arg || typeof entry.arg !== 'string') {
throw new Error('Each inject arg must have a string "arg" property');
}
if (!['server', 'always'].includes(entry.condition)) {
throw new Error('Inject arg condition must be "server" or "always"');
}
}
saveConfig({ javaWrapperConfig: wrapperConfig });
console.log('[Config] Wrapper config saved');
}
function resetWrapperConfig() {
const config = loadConfig();
delete config.javaWrapperConfig;
delete config.javaWrapperScripts; // Clean up legacy key if present
// Write the cleaned config using the same atomic pattern as saveConfig.
// We cannot use saveConfig() here because it merges (spread) which cannot remove keys.
const data = JSON.stringify(config, null, 2);
fs.writeFileSync(CONFIG_TEMP, data, 'utf8');
if (fs.existsSync(CONFIG_FILE)) {
fs.copyFileSync(CONFIG_FILE, CONFIG_BACKUP);
}
fs.renameSync(CONFIG_TEMP, CONFIG_FILE);
console.log('[Config] Wrapper config reset to default');
return getDefaultWrapperConfig();
}
/**
* Generate a platform-specific wrapper script from structured config
* @param {Object} config - { stripFlags: string[], injectArgs: {arg, condition}[] }
* @param {string} platform - 'darwin', 'win32', or 'linux'
* @param {string|null} javaBin - Path to real java binary (required for darwin/linux)
* @returns {string} Generated script content
*/
function generateWrapperScript(config, platform, javaBin) {
const { stripFlags, injectArgs } = config;
const alwaysArgs = injectArgs.filter(a => a.condition === 'always');
const serverArgs = injectArgs.filter(a => a.condition === 'server');
if (platform === 'win32') {
return _generateWindowsWrapper(stripFlags, alwaysArgs, serverArgs);
} else {
return _generateUnixWrapper(stripFlags, alwaysArgs, serverArgs, javaBin);
}
}
function _generateUnixWrapper(stripFlags, alwaysArgs, serverArgs, javaBin) {
const lines = [
'#!/bin/bash',
'# Java wrapper - generated by HytaleF2P launcher',
`REAL_JAVA="${javaBin || '${JAVA_BIN}'}"`,
'ARGS=("$@")',
''
];
// Strip flags
if (stripFlags.length > 0) {
lines.push('# Strip JVM flags');
lines.push('FILTERED_ARGS=()');
lines.push('for arg in "${ARGS[@]}"; do');
lines.push(' case "$arg" in');
for (const flag of stripFlags) {
lines.push(` "${flag}") echo "[Wrapper] Stripped: $arg" ;;`);
}
lines.push(' *) FILTERED_ARGS+=("$arg") ;;');
lines.push(' esac');
lines.push('done');
} else {
lines.push('FILTERED_ARGS=("${ARGS[@]}")');
}
lines.push('');
// Always-inject args
if (alwaysArgs.length > 0) {
lines.push('# Inject args (always)');
for (const a of alwaysArgs) {
lines.push(`FILTERED_ARGS+=("${a.arg}")`);
lines.push(`echo "[Wrapper] Injected ${a.arg}"`);
}
lines.push('');
}
// Server-conditional args (appended after HytaleServer.jar if present)
if (serverArgs.length > 0) {
lines.push('# Inject args (server only)');
lines.push('IS_SERVER=false');
lines.push('for arg in "${FILTERED_ARGS[@]}"; do');
lines.push(' if [[ "$arg" == *"HytaleServer.jar"* ]]; then');
lines.push(' IS_SERVER=true');
lines.push(' break');
lines.push(' fi');
lines.push('done');
lines.push('if [ "$IS_SERVER" = true ]; then');
for (const a of serverArgs) {
lines.push(` FILTERED_ARGS+=("${a.arg}")`);
lines.push(` echo "[Wrapper] Injected ${a.arg}"`);
}
lines.push('fi');
lines.push('');
}
lines.push('echo "[Wrapper] Executing: $REAL_JAVA ${FILTERED_ARGS[*]}"');
lines.push('exec "$REAL_JAVA" "${FILTERED_ARGS[@]}"');
lines.push('');
return lines.join('\n');
}
function _generateWindowsWrapper(stripFlags, alwaysArgs, serverArgs) {
const lines = [
'@echo off',
'setlocal EnableDelayedExpansion',
'',
'REM Java wrapper - generated by HytaleF2P launcher',
'set "REAL_JAVA=%~dp0java-original.exe"',
'set "ARGS=%*"',
''
];
// Strip flags using string replacement
if (stripFlags.length > 0) {
lines.push('REM Strip JVM flags');
for (const flag of stripFlags) {
lines.push(`set "ARGS=!ARGS:${flag}=!"`);
}
lines.push('');
}
// Always-inject args
const alwaysExtra = alwaysArgs.map(a => a.arg).join(' ');
// Server-conditional args
if (serverArgs.length > 0) {
const serverExtra = serverArgs.map(a => a.arg).join(' ');
lines.push('REM Check if running HytaleServer.jar and inject server args');
lines.push('echo !ARGS! | findstr /i "HytaleServer.jar" >nul 2>&1');
lines.push('if "!ERRORLEVEL!"=="0" (');
if (alwaysExtra) {
lines.push(` echo [Wrapper] Injected ${alwaysExtra} ${serverExtra}`);
lines.push(` "%REAL_JAVA%" !ARGS! ${alwaysExtra} ${serverExtra}`);
} else {
lines.push(` echo [Wrapper] Injected ${serverExtra}`);
lines.push(` "%REAL_JAVA%" !ARGS! ${serverExtra}`);
}
lines.push(') else (');
if (alwaysExtra) {
lines.push(` "%REAL_JAVA%" !ARGS! ${alwaysExtra}`);
} else {
lines.push(' "%REAL_JAVA%" !ARGS!');
}
lines.push(')');
} else if (alwaysExtra) {
lines.push(`"%REAL_JAVA%" !ARGS! ${alwaysExtra}`);
} else {
lines.push('"%REAL_JAVA%" !ARGS!');
}
lines.push('exit /b !ERRORLEVEL!');
lines.push('');
return lines.join('\r\n');
}
// =============================================================================
// EXPORTS
// =============================================================================
@@ -924,6 +1130,13 @@ module.exports = {
saveVersionBranch,
loadVersionBranch,
// Java Wrapper Config
getDefaultWrapperConfig,
loadWrapperConfig,
saveWrapperConfig,
resetWrapperConfig,
generateWrapperScript,
// Constants
CONFIG_FILE,
UUID_STORE_FILE