Upload via GUI (21 Dateien)

This commit is contained in:
Git Manager GUI
2026-08-01 19:32:20 +02:00
parent afe02f81be
commit 7a6b31fb11
13 changed files with 858 additions and 20 deletions
+180
View File
@@ -0,0 +1,180 @@
'use strict';
/*
* bedrock.js – Unterstützung für Minecraft Bedrock (Windows-Store-App)
* --------------------------------------------------------------------
* Bedrock ist keine Java-Anwendung, sondern eine UWP-App. Der Launcher kann
* sie daher nicht herunterladen oder in Instanzen aufteilen – wohl aber
* starten und die Spieldaten (Welten, Add-ons, Pakete, Bildschirmfotos)
* verwalten. Die Installation selbst bleibt Sache des Microsoft Store.
*/
const fs = require('fs');
const path = require('path');
const { execFile, spawn } = require('child_process');
const PKG_FAMILY = 'Microsoft.MinecraftUWP_8wekyb3d8bbwe';
const APP_ID = PKG_FAMILY + '!App';
function comMojangDir() {
return path.join(process.env.LOCALAPPDATA || '', 'Packages', PKG_FAMILY,
'LocalState', 'games', 'com.mojang');
}
// ---------------------------------------------------------------------------
// Erkennung
// ---------------------------------------------------------------------------
function runPwsh(cmd) {
return new Promise((resolve) => {
execFile('powershell', ['-NoProfile', '-NonInteractive', '-Command', cmd],
{ windowsHide: true, timeout: 15000 },
(err, stdout) => resolve(err ? '' : String(stdout || '').trim()));
});
}
async function detect() {
const dir = comMojangDir();
const version = await runPwsh(
"(Get-AppxPackage -Name 'Microsoft.MinecraftUWP' -ErrorAction SilentlyContinue).Version");
const installed = !!version;
return {
installed,
version: version || null,
dataDir: fs.existsSync(dir) ? dir : null,
};
}
// ---------------------------------------------------------------------------
// Start
// ---------------------------------------------------------------------------
/*
* Startet Bedrock direkt.
* Wichtig: Der AppsFolder-Pfad darf NICHT in Anführungszeichen stehen – sonst
* öffnet der Explorer nur ein Ordnerfenster. Daher windowsVerbatimArguments.
*/
function launch() {
const target = 'shell:appsFolder\\' + APP_ID;
const versuche = [
// 1) genau der Aufruf, der sich unter Windows als zuverlässig erwiesen hat
['powershell', ['-NoProfile', '-WindowStyle', 'Hidden', '-Command',
`Start-Process explorer.exe -ArgumentList '${target}'`], {}],
// 2) explorer direkt, ohne zusätzliche Anführungszeichen
['explorer.exe', [target], { windowsVerbatimArguments: true }],
// 3) über die Eingabeaufforderung
['cmd', ['/c', 'start', '', target], { windowsVerbatimArguments: true }],
];
for (const [cmd, args, extra] of versuche) {
try {
const p = spawn(cmd, args, Object.assign({ detached: true, stdio: 'ignore', windowsHide: true }, extra));
p.unref();
return { ok: true, method: cmd };
} catch { /* nächsten Weg versuchen */ }
}
return { ok: false, message: 'Bedrock konnte nicht gestartet werden.' };
}
// ---------------------------------------------------------------------------
// Welten / Pakete / Bildschirmfotos
// ---------------------------------------------------------------------------
function readLevelName(worldDir) {
// levelname.txt enthält den im Spiel angezeigten Namen
try {
const p = path.join(worldDir, 'levelname.txt');
if (fs.existsSync(p)) return fs.readFileSync(p, 'utf8').trim();
} catch { /* Ordnername verwenden */ }
return path.basename(worldDir);
}
function dirSize(dir, depth = 3) {
let total = 0;
if (depth < 0) return 0;
let entries;
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return 0; }
for (const e of entries) {
const p = path.join(dir, e.name);
try {
if (e.isDirectory()) total += dirSize(p, depth - 1);
else total += fs.statSync(p).size;
} catch { /* überspringen */ }
}
return total;
}
function listWorlds() {
const base = path.join(comMojangDir(), 'minecraftWorlds');
if (!fs.existsSync(base)) return [];
return fs.readdirSync(base, { withFileTypes: true })
.filter((e) => e.isDirectory())
.map((e) => {
const dir = path.join(base, e.name);
let modified = null;
try { modified = fs.statSync(dir).mtime.toISOString(); } catch { /* egal */ }
return { id: e.name, name: readLevelName(dir), dir, modified, bytes: dirSize(dir) };
})
.sort((a, b) => String(b.modified).localeCompare(String(a.modified)));
}
// Ordnerarten, die verwaltet werden können
const FOLDERS = {
worlds: 'minecraftWorlds',
resource_packs: 'resource_packs',
behavior_packs: 'behavior_packs',
skin_packs: 'skin_packs',
screenshots: 'Screenshots',
};
function listFolder(kind) {
const sub = FOLDERS[kind];
if (!sub) return [];
const base = path.join(comMojangDir(), sub);
if (!fs.existsSync(base)) return [];
return fs.readdirSync(base, { withFileTypes: true })
.filter((e) => !e.name.startsWith('.'))
.map((e) => {
const p = path.join(base, e.name);
let bytes = 0, modified = null;
try {
const st = fs.statSync(p);
modified = st.mtime.toISOString();
bytes = e.isDirectory() ? dirSize(p, 2) : st.size;
} catch { /* egal */ }
return { name: e.name, isDir: e.isDirectory(), path: p, bytes, modified };
});
}
function folderPath(kind) {
const sub = FOLDERS[kind];
return sub ? path.join(comMojangDir(), sub) : null;
}
function removeEntry(kind, name) {
const base = folderPath(kind);
if (!base) return false;
const target = path.join(base, name);
if (!target.startsWith(base + path.sep)) return false; // Pfad-Ausbruch verhindern
if (fs.existsSync(target)) fs.rmSync(target, { recursive: true, force: true });
return true;
}
/*
* Add-on installieren: .mcpack/.mcaddon/.mcworld sind ZIP-Dateien.
* Der Doppelklick-Weg über die Shell ist der zuverlässigste, weil Minecraft
* die Pakete selbst einsortiert (inkl. Abhängigkeiten).
*/
function installAddon(filePath) {
if (!fs.existsSync(filePath)) return { ok: false, message: 'Datei nicht gefunden.' };
if (!/\.(mcpack|mcaddon|mcworld|mctemplate)$/i.test(filePath)) {
return { ok: false, message: 'Nur .mcpack, .mcaddon, .mcworld oder .mctemplate möglich.' };
}
const child = spawn('cmd', ['/c', 'start', '', filePath], { detached: true, stdio: 'ignore' });
child.unref();
return { ok: true };
}
module.exports = {
detect, launch, listWorlds, listFolder, folderPath, removeEntry, installAddon,
comMojangDir, FOLDERS,
};