Add .mrpack exporter (tools/export-mrpack.mjs) and install docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Full_droper
2026-10-02 20:37:02 +03:00
co-authored by Claude Opus 5.5
parent 5b807d0600
commit 36760f1716
3 changed files with 82 additions and 4 deletions
+1
View File
@@ -0,0 +1 @@
dist/
+9 -4
View File
@@ -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 <url-до-pack.toml>
"$INST_JAVA" -jar packwiz-installer-bootstrap.jar https://git.fulldroper.ks.ua/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/`, завантажити в реліз.
## Склад
+72
View File
@@ -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-<version>.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`);