UNPKG

minecraft-core-master

Version:

Núcleo avanzado para launchers de Minecraft. Descarga, instala y ejecuta versiones de Minecraft, assets, librerías, Java y loaders de forma automática y eficiente.

123 lines (122 loc) 4.45 kB
import { mkdir, writeFile } from "node:fs/promises"; import { join, dirname } from "node:path"; import { EventEmitter } from "node:events"; import https from "node:https"; export class ClientDownloader extends EventEmitter { version; root; paused = false; stopped = false; maxRetries; decodeJson; jarURL = ""; jsonURL = ""; jsonData = null; constructor(opts) { super(); this.version = opts.version; this.root = opts.root; this.maxRetries = opts.maxRetries ?? 5; this.decodeJson = opts.decodeJson ?? false; } fetch(url) { return new Promise((resolve, reject) => { https .get(url, (res) => { if (res.statusCode !== 200) return reject(new Error("HTTP " + res.statusCode)); const data = []; res.on("data", (c) => data.push(c)); res.on("end", () => resolve(Buffer.concat(data))); }) .on("error", reject); }); } async loadManifest() { const buf = await this.fetch("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json"); const manifest = JSON.parse(buf.toString()); const entry = manifest.versions.find((v) => v.id === this.version); if (!entry) throw new Error("Version no encontrada"); const versionJSON = JSON.parse((await this.fetch(entry.url)).toString()); this.jarURL = versionJSON.downloads.client.url; this.jsonURL = entry.url; this.jsonData = versionJSON; } async getTotalBytes() { await this.loadManifest(); return (this.jsonData.downloads.client.size + JSON.stringify(this.jsonData, null, 0).length); } async downloadTo(url, filePath) { await mkdir(dirname(filePath), { recursive: true }); return new Promise((resolve, reject) => { const attempt = (retry) => { if (this.stopped) return resolve(); const req = https.get(url, (res) => { if (res.statusCode !== 200) { if (retry < this.maxRetries) return attempt(retry + 1); return reject(new Error("HTTP " + res.statusCode)); } const chunks = []; res.on("data", async (chunk) => { while (this.paused && !this.stopped) { await new Promise((r) => setTimeout(r, 50)); } if (this.stopped) return; chunks.push(chunk); this.emit("Bytes", chunk.length); }); res.on("end", async () => { if (this.stopped) return resolve(); await writeFile(filePath, Buffer.concat(chunks)); resolve(); }); }); req.on("error", (e) => { if (retry < this.maxRetries) return attempt(retry + 1); reject(e); }); }; attempt(0); }); } pause() { this.paused = true; this.emit("Paused"); } resume() { this.paused = false; this.emit("Resumed"); } stop() { this.stopped = true; this.emit("Stopped"); } async start() { this.emit("Start"); this.paused = false; this.stopped = false; if (!this.jsonData) await this.loadManifest(); const jsonPath = join(this.root, "versions", this.version, `${this.version}.json`); const jarPath = join(this.root, "versions", this.version, `${this.version}.jar`); if (this.decodeJson) { await mkdir(dirname(jsonPath), { recursive: true }); const pretty = JSON.stringify(this.jsonData, null, 2); this.emit("Bytes", Buffer.byteLength(pretty, "utf-8")); await writeFile(jsonPath, pretty, "utf-8"); } else { await this.downloadTo(this.jsonURL, jsonPath); } await this.downloadTo(this.jarURL, jarPath); if (!this.stopped) this.emit("Done"); } }