From 36760f17160bdc21e127e489253da7855aa910b0 Mon Sep 17 00:00:00 2001 From: Full_droper Date: Fri, 2 Oct 2026 20:37:02 +0300 Subject: [PATCH] Add .mrpack exporter (tools/export-mrpack.mjs) and install docs Co-Authored-By: Claude Opus 5.5 --- .gitignore | 1 + README.md | 13 +++++--- tools/export-mrpack.mjs | 72 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 .gitignore create mode 100644 tools/export-mrpack.mjs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/README.md b/README.md index 13626c5..2708782 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,19 @@ ## Встановлення -**Modrinth App / Prism Launcher (packwiz-installer):** створити профіль NeoForge 1.21.1 / 21.1.228 і в -pre-launch команді вказати +**Modrinth App (найпростіше):** завантажити `CreateRP-<версія>.mrpack` з [релізів](../client/releases) → +Modrinth App → «+» → «Import from file». Збірка сама підтягне NeoForge 21.1.228 і всі моди. + +**Prism Launcher / packwiz-installer (автооновлення):** профіль NeoForge 1.21.1 / 21.1.228, у pre-launch команді ``` -"$INST_JAVA" -jar packwiz-installer-bootstrap.jar +"$INST_JAVA" -jar packwiz-installer-bootstrap.jar http://git.no.ra/Pinecraft-CreateRP/client/raw/branch/main/pack.toml ``` -Збірка сама докачає моди з Modrinth і оновиться при наступному запуску. +## Для мейнтейнера + +- Після зміни `mods/` чи `config/`: `node tools/reindex.mjs`, коміт. +- Новий `.mrpack`: підняти `version` у `pack.toml`, `node tools/export-mrpack.mjs` → `dist/`, завантажити в реліз. ## Склад diff --git a/tools/export-mrpack.mjs b/tools/export-mrpack.mjs new file mode 100644 index 0000000..81ed7aa --- /dev/null +++ b/tools/export-mrpack.mjs @@ -0,0 +1,72 @@ +// Exports this packwiz pack as a Modrinth modpack (.mrpack) for Modrinth App / Prism "Import". +// node tools/export-mrpack.mjs → dist/CreateRP-.mrpack +// Mods are referenced by their Modrinth CDN URL (sha1/sha512/size from the Modrinth API); +// config/ goes into overrides/. Zipping uses Windows' bsdtar (tar -a) or `zip` on Linux. +import fs from 'fs'; +import path from 'path'; +import { execFileSync } from 'child_process'; +import { fileURLToPath } from 'url'; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const UA = { 'User-Agent': 'Pinecraft-CreateRP/mrpack-export' }; +const field = (toml, key) => (toml.match(new RegExp(`^${key}\\s*=\\s*"([^"]*)"`, 'm')) || [])[1]; + +const pack = fs.readFileSync(path.join(root, 'pack.toml'), 'utf8'); +const packName = field(pack, 'name'); +const packVersion = field(pack, 'version'); +const mc = field(pack, 'minecraft'); +const neoforge = field(pack, 'neoforge'); + +const metas = fs.readdirSync(path.join(root, 'mods')).filter(f => f.endsWith('.pw.toml')).map(f => { + const t = fs.readFileSync(path.join(root, 'mods', f), 'utf8'); + return { file: f, filename: field(t, 'filename'), side: field(t, 'side') || 'both', url: field(t, 'url'), versionId: field(t, 'version') }; +}); + +// sha1/sha512/size per version from Modrinth (batched) +const versionInfo = {}; +const ids = metas.map(m => m.versionId).filter(Boolean); +for (let i = 0; i < ids.length; i += 100) { + const chunk = ids.slice(i, i + 100); + const res = await fetch('https://api.modrinth.com/v2/versions?ids=' + encodeURIComponent(JSON.stringify(chunk)), { headers: UA }); + for (const v of await res.json()) versionInfo[v.id] = v; +} + +const files = metas.map(m => { + const v = versionInfo[m.versionId]; + const f = v?.files.find(x => x.url === m.url) || v?.files.find(x => x.filename === m.filename); + if (!f) throw new Error(`no Modrinth file for ${m.file}`); + return { + path: `mods/${f.filename}`, + hashes: { sha1: f.hashes.sha1, sha512: f.hashes.sha512 }, + env: { client: m.side === 'server' ? 'unsupported' : 'required', server: m.side === 'client' ? 'unsupported' : 'required' }, + downloads: [f.url], + fileSize: f.size + }; +}).sort((a, b) => a.path.localeCompare(b.path)); + +const index = { + formatVersion: 1, + game: 'minecraft', + versionId: packVersion, + name: packName, + summary: 'Клієнтська збірка сервера Create RP', + files, + dependencies: { minecraft: mc, neoforge } +}; + +// staging dir → zip +const stage = path.join(root, 'dist', '.stage'); +fs.rmSync(stage, { recursive: true, force: true }); +fs.mkdirSync(path.join(stage, 'overrides'), { recursive: true }); +fs.writeFileSync(path.join(stage, 'modrinth.index.json'), JSON.stringify(index, null, 2)); +fs.cpSync(path.join(root, 'config'), path.join(stage, 'overrides', 'config'), { recursive: true }); + +const out = path.join(root, 'dist', `CreateRP-${packVersion}.mrpack`); +fs.rmSync(out, { force: true }); +if (process.platform === 'win32') { + execFileSync('C:\\Windows\\System32\\tar.exe', ['--format', 'zip', '-c', '-f', out, '-C', stage, 'modrinth.index.json', 'overrides']); +} else { + execFileSync('zip', ['-qr', out, 'modrinth.index.json', 'overrides'], { cwd: stage }); +} +fs.rmSync(stage, { recursive: true, force: true }); +console.log(`${out}: ${files.length} mods, ${(fs.statSync(out).size / 1024).toFixed(0)} KiB`);