Upload via GUI (39 Dateien)
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "aeromc",
|
"name": "aeromc",
|
||||||
"version": "1.0.4-beta",
|
"version": "1.0.5",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "aeromc",
|
"name": "aeromc",
|
||||||
"version": "1.0.4-beta",
|
"version": "1.0.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "aeromc",
|
"name": "aeromc",
|
||||||
"productName": "AeroMC",
|
"productName": "AeroMC",
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"description": "AeroMC – Instanz-basierter Minecraft-Launcher (à la MultiMC/Prism)",
|
"description": "AeroMC – Instanz-basierter Minecraft-Launcher (à la MultiMC/Prism)",
|
||||||
"homepage": "https://m-viper.de",
|
"homepage": "https://m-viper.de",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
|
|||||||
@@ -47,11 +47,18 @@ function neuesterBericht(gameDir, seit) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Zieht die aussagekräftigen Zeilen aus einem Bericht.
|
* Zieht die aussagekräftigen Zeilen aus einem Bericht.
|
||||||
* -> { beschreibung, fehler, verdaechtigeMods, verlauf: [] }
|
* -> { beschreibung, fehler, verdaechtigeMods, diagnose, empfehlung, verlauf: [] }
|
||||||
*/
|
*/
|
||||||
function auswerten(text) {
|
function auswerten(text) {
|
||||||
const zeilen = String(text || '').split(/\r?\n/);
|
const zeilen = String(text || '').split(/\r?\n/);
|
||||||
const befund = { beschreibung: '', fehler: '', verdaechtigeMods: '', verlauf: [] };
|
const befund = {
|
||||||
|
beschreibung: '',
|
||||||
|
fehler: '',
|
||||||
|
verdaechtigeMods: '',
|
||||||
|
diagnose: '',
|
||||||
|
empfehlung: '',
|
||||||
|
verlauf: [],
|
||||||
|
};
|
||||||
|
|
||||||
for (let i = 0; i < zeilen.length; i++) {
|
for (let i = 0; i < zeilen.length; i++) {
|
||||||
const z = zeilen[i];
|
const z = zeilen[i];
|
||||||
@@ -86,6 +93,22 @@ function auswerten(text) {
|
|||||||
const treffer = zeilen.find((z) => /^[\w.$]+(Exception|Error)(:|\s|$)/.test(z.trim()));
|
const treffer = zeilen.find((z) => /^[\w.$]+(Exception|Error)(:|\s|$)/.test(z.trim()));
|
||||||
if (treffer) befund.fehler = treffer.trim();
|
if (treffer) befund.fehler = treffer.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const volltext = zeilen.join('\n');
|
||||||
|
const shutdownWatchdog = /Description:\s*Client shutdown from post-main/i.test(volltext)
|
||||||
|
&& /ClientShutdownWatchdog/i.test(volltext);
|
||||||
|
const webMapJetty = /journeymap_webmap:\s*JourneyMap WebMap\b/i.test(volltext)
|
||||||
|
&& /JettyServerThreadPool/i.test(volltext)
|
||||||
|
&& /ServerConnector.*\{0\.0\.0\.0:\d+\}/i.test(volltext);
|
||||||
|
if (shutdownWatchdog && webMapJetty) {
|
||||||
|
const version = volltext.match(/journeymap_webmap:\s*JourneyMap WebMap\s+([^\s\r\n]+)/i);
|
||||||
|
const name = `JourneyMap WebMap${version ? ` ${version[1]}` : ''}`;
|
||||||
|
befund.verdaechtigeMods = name;
|
||||||
|
befund.diagnose = `${name} hat seinen eingebetteten Jetty-Webserver beim Beenden nicht gestoppt. `
|
||||||
|
+ 'Dessen nicht beendete Server-Threads haben den Minecraft-Shutdown-Watchdog ausgelöst.';
|
||||||
|
befund.empfehlung = 'AeroMC beendet einen nach dem regulären Client-Shutdown verbliebenen Java-Prozess nun '
|
||||||
|
+ 'nach einer kurzen Schonfrist automatisch. JourneyMap WebMap kann installiert und aktiviert bleiben.';
|
||||||
|
}
|
||||||
return befund;
|
return befund;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const { spawn, spawnSync } = require('child_process');
|
|||||||
const services = require('./services');
|
const services = require('./services');
|
||||||
const loaders = require('./loaders');
|
const loaders = require('./loaders');
|
||||||
const forge = require('./forge');
|
const forge = require('./forge');
|
||||||
|
const processwatchdog = require('./processwatchdog');
|
||||||
|
|
||||||
const UA = 'AeroMC-Launcher/0.2';
|
const UA = 'AeroMC-Launcher/0.2';
|
||||||
const RESOURCES = 'https://resources.download.minecraft.net';
|
const RESOURCES = 'https://resources.download.minecraft.net';
|
||||||
@@ -253,20 +254,28 @@ async function prepareAssets(vj, sharedDir, onProgress) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function pickJava(vj, preferredPath) {
|
function pickJava(vj, preferredPath) {
|
||||||
|
const required = (vj.javaVersion && vj.javaVersion.majorVersion) || 8;
|
||||||
|
let preferred = null;
|
||||||
if (preferredPath && preferredPath.trim()) {
|
if (preferredPath && preferredPath.trim()) {
|
||||||
const info = services.probeJava(preferredPath);
|
const info = services.probeJava(preferredPath);
|
||||||
if (info) return { path: preferredPath, major: info.major, forced: true };
|
if (info) {
|
||||||
|
preferred = { path: preferredPath, major: info.major, forced: true };
|
||||||
|
if (info.major === required) return preferred;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const required = (vj.javaVersion && vj.javaVersion.majorVersion) || 8;
|
|
||||||
const jvms = services.scanJava();
|
const jvms = services.scanJava();
|
||||||
// exakt passende oder nächsthöhere 64-bit-Version
|
// Mod-Loader und Mods unterstützen neuere Java-Versionen nicht automatisch.
|
||||||
|
// Deshalb ausschließlich die von Mojang angegebene Major-Version verwenden.
|
||||||
const x64 = jvms.filter((j) => j.arch === 'x64');
|
const x64 = jvms.filter((j) => j.arch === 'x64');
|
||||||
const exact = x64.find((j) => j.major === required);
|
const exact = x64.find((j) => j.major === required);
|
||||||
if (exact) return { path: exact.path, major: exact.major };
|
if (exact) return { path: exact.path, major: exact.major };
|
||||||
const higher = x64.filter((j) => j.major >= required).sort((a, b) => a.major - b.major)[0];
|
const fallback = preferred || x64[0] || jvms[0];
|
||||||
if (higher) return { path: higher.path, major: higher.major };
|
return fallback ? {
|
||||||
const any = jvms[0];
|
path: fallback.path,
|
||||||
return any ? { path: any.path, major: any.major, mismatch: required } : null;
|
major: fallback.major,
|
||||||
|
forced: !!fallback.forced,
|
||||||
|
mismatch: required,
|
||||||
|
} : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -387,6 +396,14 @@ async function prepareAndLaunch(opts, onProgress) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!java) return { ok: false, message: 'Keine Java-Installation gefunden. Bitte in den Einstellungen Java festlegen.' };
|
if (!java) return { ok: false, message: 'Keine Java-Installation gefunden. Bitte in den Einstellungen Java festlegen.' };
|
||||||
|
if (java.mismatch) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
requiredJava: java.mismatch,
|
||||||
|
message: `Minecraft ${vj.id} benötigt Java ${java.mismatch}, gefunden wurde nur Java ${java.major || 'unbekannt'}. `
|
||||||
|
+ 'Bitte Auto-Java aktivieren oder eine passende Java-Installation auswählen.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Mod-Loader
|
// Mod-Loader
|
||||||
let effectiveVj = vj;
|
let effectiveVj = vj;
|
||||||
@@ -503,14 +520,33 @@ async function prepareAndLaunch(opts, onProgress) {
|
|||||||
spawnArgs = [...w.slice(1), java.path, ...args];
|
spawnArgs = [...w.slice(1), java.path, ...args];
|
||||||
}
|
}
|
||||||
const child = spawn(cmd, spawnArgs, { cwd: opts.gameDir, detached: false });
|
const child = spawn(cmd, spawnArgs, { cwd: opts.gameDir, detached: false });
|
||||||
child.stdout.on('data', (d) => emit('log', { line: d.toString() }));
|
const shutdownWatchdog = opts.wrapperCommand && opts.wrapperCommand.trim()
|
||||||
child.stderr.on('data', (d) => emit('log', { line: d.toString() }));
|
? null
|
||||||
|
: processwatchdog.createProcessWatchdog({
|
||||||
|
isRunning: () => child.exitCode === null && child.signalCode === null,
|
||||||
|
kill: () => child.kill(),
|
||||||
|
onForce: (graceMs) => emit('log', {
|
||||||
|
line: `[Launcher] Regulärer Client-Shutdown war nach ${graceMs / 1000} Sekunden noch nicht beendet; `
|
||||||
|
+ 'verbliebenen Java-Prozess beendet.\n',
|
||||||
|
}),
|
||||||
|
onError: () => emit('log', {
|
||||||
|
line: '[Launcher] Der nach dem regulären Client-Shutdown verbliebene Java-Prozess konnte nicht beendet werden.\n',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
child.stdout.on('data', (d) => {
|
||||||
|
emit('log', { line: d.toString() });
|
||||||
|
if (shutdownWatchdog) shutdownWatchdog.feed(d, 'stdout');
|
||||||
|
});
|
||||||
|
child.stderr.on('data', (d) => {
|
||||||
|
emit('log', { line: d.toString() });
|
||||||
|
if (shutdownWatchdog) shutdownWatchdog.feed(d, 'stderr');
|
||||||
|
});
|
||||||
// Ende genau einmal melden – 'exit' und 'error' können beide kommen
|
// Ende genau einmal melden – 'exit' und 'error' können beide kommen
|
||||||
let beendetGemeldet = false;
|
let beendetGemeldet = false;
|
||||||
const meldeEnde = (code) => {
|
const meldeEnde = (code, cleanShutdown) => {
|
||||||
if (beendetGemeldet) return;
|
if (beendetGemeldet) return;
|
||||||
beendetGemeldet = true;
|
beendetGemeldet = true;
|
||||||
emit('exit', { code });
|
emit('exit', { code, cleanShutdown: !!cleanShutdown });
|
||||||
// Post-Exit-Befehl
|
// Post-Exit-Befehl
|
||||||
if (opts.postExitCommand && opts.postExitCommand.trim()) {
|
if (opts.postExitCommand && opts.postExitCommand.trim()) {
|
||||||
emit('log', { line: '[Post-Exit] ' + opts.postExitCommand + '\n' });
|
emit('log', { line: '[Post-Exit] ' + opts.postExitCommand + '\n' });
|
||||||
@@ -521,12 +557,19 @@ async function prepareAndLaunch(opts, onProgress) {
|
|||||||
} catch { /* egal */ }
|
} catch { /* egal */ }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
child.on('exit', (code) => meldeEnde(code));
|
child.on('exit', (code) => {
|
||||||
|
// vor end() lesen: sawCleanShutdown wird zwar nicht von end()/clear()
|
||||||
|
// zurückgesetzt, aber so bleibt die Reihenfolge unabhängig von internen Details
|
||||||
|
const cleanShutdown = !!(shutdownWatchdog && shutdownWatchdog.sawCleanShutdown);
|
||||||
|
if (shutdownWatchdog) shutdownWatchdog.end();
|
||||||
|
meldeEnde(shutdownWatchdog && shutdownWatchdog.forced ? 0 : code, cleanShutdown);
|
||||||
|
});
|
||||||
// ohne diesen Listener würde ein Fehlstart (z. B. falscher Java-Pfad) als
|
// ohne diesen Listener würde ein Fehlstart (z. B. falscher Java-Pfad) als
|
||||||
// unbehandeltes 'error'-Ereignis den Hauptprozess beenden
|
// unbehandeltes 'error'-Ereignis den Hauptprozess beenden
|
||||||
child.on('error', (err) => {
|
child.on('error', (err) => {
|
||||||
|
if (shutdownWatchdog) shutdownWatchdog.end();
|
||||||
emit('log', { line: '[Fehler] Minecraft konnte nicht gestartet werden: ' + err.message + '\n' });
|
emit('log', { line: '[Fehler] Minecraft konnte nicht gestartet werden: ' + err.message + '\n' });
|
||||||
meldeEnde(-1);
|
meldeEnde(-1, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
return { ok: true, pid: child.pid, java };
|
return { ok: true, pid: child.pid, java };
|
||||||
|
|||||||
25
src/main.js
25
src/main.js
@@ -762,7 +762,11 @@ async function importMrpack(buf, zip) {
|
|||||||
const herkunft = await modpackHerkunft(buf, index);
|
const herkunft = await modpackHerkunft(buf, index);
|
||||||
store.updateInstance(created.id, { modpack: Object.assign(herkunft, { dateien: eingespielt.dateien }) });
|
store.updateInstance(created.id, { modpack: Object.assign(herkunft, { dateien: eingespielt.dateien }) });
|
||||||
|
|
||||||
return { ok: true, instance: store.getInstance(created.id), fileCount: eingespielt.dateien.length };
|
return {
|
||||||
|
ok: true,
|
||||||
|
instance: store.getInstance(created.id),
|
||||||
|
fileCount: eingespielt.dateien.length,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -921,7 +925,9 @@ ipcMain.handle('modpack:update', async (_e, id, versionId) => {
|
|||||||
if (SPIELERDATEN.some((r) => r.test(alt))) continue;
|
if (SPIELERDATEN.some((r) => r.test(alt))) continue;
|
||||||
const p = path.join(gameDir, String(alt).replace(/\//g, path.sep));
|
const p = path.join(gameDir, String(alt).replace(/\//g, path.sep));
|
||||||
if (!p.startsWith(gameDir + path.sep)) continue;
|
if (!p.startsWith(gameDir + path.sep)) continue;
|
||||||
try { if (fs.existsSync(p)) { fs.rmSync(p, { force: true }); entfernt.push(alt); } } catch { /* egal */ }
|
try {
|
||||||
|
if (fs.existsSync(p)) { fs.rmSync(p, { force: true }); entfernt.push(alt); }
|
||||||
|
} catch { /* egal */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minecraft-Version und Loader können sich mit der neuen Fassung ändern
|
// Minecraft-Version und Loader können sich mit der neuen Fassung ändern
|
||||||
@@ -1514,7 +1520,10 @@ ipcMain.handle('mmc:import', (_e, entries, copyData) => {
|
|||||||
const done = [];
|
const done = [];
|
||||||
const failed = [];
|
const failed = [];
|
||||||
for (const entry of entries || []) {
|
for (const entry of entries || []) {
|
||||||
try { done.push(mmcimport.importOne(store, entry, copyData !== false)); }
|
try {
|
||||||
|
const imported = mmcimport.importOne(store, entry, copyData !== false);
|
||||||
|
done.push(imported);
|
||||||
|
}
|
||||||
catch (err) { failed.push({ name: entry.name, message: err.message }); }
|
catch (err) { failed.push({ name: entry.name, message: err.message }); }
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send('mmc:progress', { current: done.length + failed.length, total: entries.length });
|
mainWindow.webContents.send('mmc:progress', { current: done.length + failed.length, total: entries.length });
|
||||||
@@ -1540,7 +1549,10 @@ ipcMain.handle('cf:import', (_e, entries, copyData) => {
|
|||||||
const done = [];
|
const done = [];
|
||||||
const failed = [];
|
const failed = [];
|
||||||
for (const entry of entries || []) {
|
for (const entry of entries || []) {
|
||||||
try { done.push(cfimport.importOne(store, entry, copyData !== false)); }
|
try {
|
||||||
|
const imported = cfimport.importOne(store, entry, copyData !== false);
|
||||||
|
done.push(imported);
|
||||||
|
}
|
||||||
catch (err) { failed.push({ name: entry.name, message: err.message }); }
|
catch (err) { failed.push({ name: entry.name, message: err.message }); }
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send('cf:progress', { current: done.length + failed.length, total: entries.length });
|
mainWindow.webContents.send('cf:progress', { current: done.length + failed.length, total: entries.length });
|
||||||
@@ -1566,7 +1578,10 @@ ipcMain.handle('mc:import', (_e, entries, copyData) => {
|
|||||||
const done = [];
|
const done = [];
|
||||||
const failed = [];
|
const failed = [];
|
||||||
for (const entry of entries || []) {
|
for (const entry of entries || []) {
|
||||||
try { done.push(mcimport.importOne(store, entry, copyData !== false)); }
|
try {
|
||||||
|
const imported = mcimport.importOne(store, entry, copyData !== false);
|
||||||
|
done.push(imported);
|
||||||
|
}
|
||||||
catch (err) { failed.push({ name: entry.name, message: err.message }); }
|
catch (err) { failed.push({ name: entry.name, message: err.message }); }
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
mainWindow.webContents.send('mc:progress', { current: done.length + failed.length, total: entries.length });
|
mainWindow.webContents.send('mc:progress', { current: done.length + failed.length, total: entries.length });
|
||||||
|
|||||||
101
src/processwatchdog.js
Normal file
101
src/processwatchdog.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const DEFAULT_GRACE_MS = 15000;
|
||||||
|
|
||||||
|
const CLEAN_SHUTDOWN = /\[(?:Render thread|Client thread)\/INFO\](?:\s+\([^)]*\))?:\s*Stopping!\s*$/;
|
||||||
|
const FATAL_OUTPUT = [
|
||||||
|
/---- Minecraft Crash Report ----/i,
|
||||||
|
/\b(?:Encountered an unexpected exception|Unreported exception thrown|Reported exception thrown)\b/i,
|
||||||
|
/\b(?:This crash report has been saved to|Saving crash report to)\b/i,
|
||||||
|
/\bA fatal error has been detected by the Java Runtime Environment\b/i,
|
||||||
|
/Exception in thread "(?:Render thread|Client thread|main)"/i,
|
||||||
|
/\[(?:Render thread|Client thread|main)\/FATAL\]/i,
|
||||||
|
];
|
||||||
|
|
||||||
|
function createProcessWatchdog(options) {
|
||||||
|
const graceMs = options.graceMs || DEFAULT_GRACE_MS;
|
||||||
|
const schedule = options.setTimer || setTimeout;
|
||||||
|
const cancel = options.clearTimer || clearTimeout;
|
||||||
|
const buffers = { stdout: '', stderr: '' };
|
||||||
|
let timer = null;
|
||||||
|
let armed = false;
|
||||||
|
let crashed = false;
|
||||||
|
let ended = false;
|
||||||
|
let forced = false;
|
||||||
|
// bleibt true, sobald einmal ein regulärer Shutdown ("Stopping!") gesehen
|
||||||
|
// wurde – im Gegensatz zu `armed` wird das NICHT durch markCrash()
|
||||||
|
// zurückgesetzt, damit ein später vom internen Watchdog erzwungener Halt
|
||||||
|
// weiterhin als "war ein regulärer Shutdown" erkennbar bleibt
|
||||||
|
let sawCleanShutdown = false;
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
if (timer !== null) cancel(timer);
|
||||||
|
timer = null;
|
||||||
|
armed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function markCrash() {
|
||||||
|
crashed = true;
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleKill() {
|
||||||
|
if (timer !== null) cancel(timer);
|
||||||
|
timer = schedule(() => {
|
||||||
|
timer = null;
|
||||||
|
if (ended || crashed || !armed || !options.isRunning()) return;
|
||||||
|
try {
|
||||||
|
forced = options.kill() === true;
|
||||||
|
} catch {
|
||||||
|
forced = false;
|
||||||
|
}
|
||||||
|
if (forced && options.onForce) options.onForce(graceMs);
|
||||||
|
else if (!forced && options.isRunning() && options.onError) options.onError();
|
||||||
|
}, graceMs);
|
||||||
|
if (timer && typeof timer.unref === 'function') timer.unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
function inspectLine(line) {
|
||||||
|
const isCleanShutdown = CLEAN_SHUTDOWN.test(line);
|
||||||
|
if (isCleanShutdown) sawCleanShutdown = true;
|
||||||
|
if (FATAL_OUTPUT.some((pattern) => pattern.test(line))) {
|
||||||
|
markCrash();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ended || crashed || armed || !isCleanShutdown) return;
|
||||||
|
|
||||||
|
armed = true;
|
||||||
|
scheduleKill();
|
||||||
|
}
|
||||||
|
|
||||||
|
function feed(chunk, stream) {
|
||||||
|
if (ended) return;
|
||||||
|
const key = stream === 'stderr' ? 'stderr' : 'stdout';
|
||||||
|
const text = buffers[key] + String(chunk || '');
|
||||||
|
const lines = text.split(/\r?\n/);
|
||||||
|
buffers[key] = lines.pop() || '';
|
||||||
|
for (const line of lines) inspectLine(line);
|
||||||
|
if (armed && !crashed && !ended && lines.length) scheduleKill();
|
||||||
|
}
|
||||||
|
|
||||||
|
function end() {
|
||||||
|
ended = true;
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
feed,
|
||||||
|
end,
|
||||||
|
get armed() { return armed; },
|
||||||
|
get crashed() { return crashed; },
|
||||||
|
get forced() { return forced; },
|
||||||
|
get sawCleanShutdown() { return sawCleanShutdown; },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
DEFAULT_GRACE_MS,
|
||||||
|
CLEAN_SHUTDOWN,
|
||||||
|
FATAL_OUTPUT,
|
||||||
|
createProcessWatchdog,
|
||||||
|
};
|
||||||
@@ -2227,7 +2227,8 @@ async function zeigeAbsturzbericht(instanzId) {
|
|||||||
const zeile = (titel, wert) => wert
|
const zeile = (titel, wert) => wert
|
||||||
? `<div class="crash-zeile"><div class="crash-titel">${titel}</div><div class="crash-wert">${escapeHtml(wert)}</div></div>`
|
? `<div class="crash-zeile"><div class="crash-titel">${titel}</div><div class="crash-wert">${escapeHtml(wert)}</div></div>`
|
||||||
: '';
|
: '';
|
||||||
let html = zeile('Beschreibung', res.beschreibung) + zeile('Fehler', res.fehler)
|
let html = zeile('Diagnose', res.diagnose) + zeile('Empfohlene Lösung', res.empfehlung)
|
||||||
|
+ zeile('Beschreibung', res.beschreibung) + zeile('Fehler', res.fehler)
|
||||||
+ zeile('Verdächtige Mods', res.verdaechtigeMods);
|
+ zeile('Verdächtige Mods', res.verdaechtigeMods);
|
||||||
if (res.verlauf && res.verlauf.length) {
|
if (res.verlauf && res.verlauf.length) {
|
||||||
html += `<div class="crash-zeile"><div class="crash-titel">Aufrufverlauf</div>`
|
html += `<div class="crash-zeile"><div class="crash-titel">Aufrufverlauf</div>`
|
||||||
@@ -2246,7 +2247,17 @@ function onLaunchEvt(evt) {
|
|||||||
status('Minecraft beendet (Code ' + evt.code + ')');
|
status('Minecraft beendet (Code ' + evt.code + ')');
|
||||||
el('status-right').textContent = '';
|
el('status-right').textContent = '';
|
||||||
appendConsole('\n[Prozess beendet, Code ' + evt.code + ']\n');
|
appendConsole('\n[Prozess beendet, Code ' + evt.code + ']\n');
|
||||||
const crashed = evt.code !== 0 && evt.code !== null;
|
// War vorher "Stopping!" im Log (regulärer Client-Shutdown), zählt ein
|
||||||
|
// nachträglicher harter Exit-Code nicht als Absturz während des Spiels –
|
||||||
|
// z. B. wenn Minecrafts interner Shutdown-Watchdog wegen hängender
|
||||||
|
// Mod-Threads (z. B. JourneyMap WebMap) die JVM nach Ablauf einer
|
||||||
|
// Wartezeit zwangsweise beendet. Der Bericht bleibt trotzdem gespeichert.
|
||||||
|
const crashed = evt.code !== 0 && evt.code !== null && !evt.cleanShutdown;
|
||||||
|
if (evt.cleanShutdown && evt.code !== 0 && evt.code !== null) {
|
||||||
|
appendConsole('[Hinweis] Der Client wurde regulär beendet (Stopping!), aber danach vom '
|
||||||
|
+ 'internen Shutdown-Watchdog hart terminiert (z. B. durch einen Mod, dessen Threads '
|
||||||
|
+ 'nicht rechtzeitig beenden – etwa JourneyMap WebMap). Kein Absturz während des Spiels.\n');
|
||||||
|
}
|
||||||
// Absturzbericht suchen, bevor über die Konsole entschieden wird –
|
// Absturzbericht suchen, bevor über die Konsole entschieden wird –
|
||||||
// ein Bericht sagt mehr als der Java-Auswurf
|
// ein Bericht sagt mehr als der Java-Auswurf
|
||||||
if (crashed) zeigeAbsturzbericht(evt.id);
|
if (crashed) zeigeAbsturzbericht(evt.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user