minecraft-server-dl-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # minecraft-server-dl-cli
2
+
3
+ [![npm version](https://img.shields.io/npm/v/minecraft-server-dl-cli.svg)](https://www.npmjs.com/package/minecraft-server-dl-cli)
4
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
+
7
+ A simple CLI tool for downloading the official Minecraft Java `server.jar` by version.
8
+
9
+ This tool reads Mojang's official version manifest, resolves the requested Minecraft Java version, downloads the matching `server.jar`, and verifies the downloaded file using SHA1 when available.
10
+
11
+ ## Languages
12
+
13
+ * [English](README.md)
14
+ * [繁體中文](README.zh-TW.md)
15
+
16
+ ## Features
17
+
18
+ * Download official Minecraft Java `server.jar` by version
19
+ * Uses Mojang's official version manifest
20
+ * Automatically names files as `server_<version>.jar`
21
+ * Supports custom output paths
22
+ * Supports SHA1 and file size verification
23
+ * Can print the resolved download URL without downloading
24
+ * No runtime dependencies
25
+
26
+ ## Official Data Sources
27
+
28
+ This tool uses Mojang/Minecraft official endpoints:
29
+
30
+ * [Minecraft Java version manifest](https://piston-meta.mojang.com/mc/game/version_manifest_v2.json)
31
+ * [Official Minecraft Java server download page](https://www.minecraft.net/en-us/download/server)
32
+
33
+ The resolved `server.jar` URL usually points to:
34
+
35
+ ```text
36
+ https://piston-data.mojang.com/v1/objects/<sha1>/server.jar
37
+ ```
38
+
39
+ ## Requirements
40
+
41
+ * [Node.js](https://nodejs.org/) 18 or later
42
+ * npm, pnpm, or yarn
43
+
44
+ ## Installation
45
+
46
+ Install globally from npm:
47
+
48
+ ```bash
49
+ npm install -g minecraft-server-dl-cli
50
+ ```
51
+
52
+ Then run:
53
+
54
+ ```bash
55
+ mc-server-dl 1.21.8
56
+ ```
57
+
58
+ Or use it directly with `npx`:
59
+
60
+ ```bash
61
+ npx minecraft-server-dl-cli 1.21.8
62
+ ```
63
+
64
+ ## Usage
65
+
66
+ ```bash
67
+ mc-server-dl <version> [options]
68
+ ```
69
+
70
+ Example:
71
+
72
+ ```bash
73
+ mc-server-dl 1.21.8
74
+ ```
75
+
76
+ This downloads:
77
+
78
+ ```text
79
+ server_1.21.8.jar
80
+ ```
81
+
82
+ ## Options
83
+
84
+ | Option | Description |
85
+ | --------------------- | ------------------------------------------------------- |
86
+ | `-o, --output <path>` | Custom output path |
87
+ | `--no-verify` | Skip SHA1 and file size verification |
88
+ | `--url-only` | Print the resolved `server.jar` URL without downloading |
89
+ | `-h, --help` | Show help message |
90
+
91
+ ## Examples
92
+
93
+ Download a specific Minecraft Java server version:
94
+
95
+ ```bash
96
+ mc-server-dl 1.21.8
97
+ ```
98
+
99
+ Download and save to a custom path:
100
+
101
+ ```bash
102
+ mc-server-dl 1.21.8 -o ./servers/1.21.8/server.jar
103
+ ```
104
+
105
+ Print the official download URL only:
106
+
107
+ ```bash
108
+ mc-server-dl 1.21.8 --url-only
109
+ ```
110
+
111
+ Skip verification:
112
+
113
+ ```bash
114
+ mc-server-dl 1.21.8 --no-verify
115
+ ```
116
+
117
+ ## Exit Codes
118
+
119
+ | Code | Meaning |
120
+ | ---: | --------------------------------------------------------- |
121
+ | `0` | Success |
122
+ | `1` | Unexpected error |
123
+ | `2` | Invalid CLI arguments |
124
+ | `3` | Failed to fetch or parse metadata |
125
+ | `4` | Minecraft version not found |
126
+ | `5` | Version exists, but no official `server.jar` is available |
127
+ | `6` | Download failed |
128
+ | `7` | File size or SHA1 verification failed |
129
+
130
+ ## How It Works
131
+
132
+ 1. Fetches Mojang's version manifest.
133
+ 2. Finds the requested Minecraft Java version.
134
+ 3. Fetches that version's metadata JSON.
135
+ 4. Reads `downloads.server.url`.
136
+ 5. Downloads the official `server.jar`.
137
+ 6. Verifies file size and SHA1 when metadata is available.
138
+
139
+ ## Disclaimer
140
+
141
+ This project is not affiliated with Mojang Studios or Microsoft.
142
+
143
+ Minecraft is a trademark of Mojang Synergies AB. This tool only resolves and downloads files from official Mojang/Minecraft endpoints.
144
+
145
+ ## License
146
+
147
+ [MIT](LICENSE)
@@ -0,0 +1,147 @@
1
+ # minecraft-server-dl-cli
2
+
3
+ [![npm version](https://img.shields.io/npm/v/minecraft-server-dl-cli.svg)](https://www.npmjs.com/package/minecraft-server-dl-cli)
4
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
+
7
+ 一個簡易的 CLI 工具,用來依照指定版本下載官方 Minecraft Java `server.jar`。
8
+
9
+ 這個工具會讀取 Mojang 官方版本清單,解析指定的 Minecraft Java 版本,下載對應的 `server.jar`,並在 metadata 有提供時使用 SHA1 驗證下載結果。
10
+
11
+ ## 語言
12
+
13
+ * [English](README.md)
14
+ * [繁體中文](README.zh-TW.md)
15
+
16
+ ## 功能
17
+
18
+ * 依照版本下載官方 Minecraft Java `server.jar`
19
+ * 使用 Mojang 官方版本 manifest
20
+ * 預設輸出檔名為 `server_<version>.jar`
21
+ * 支援自訂輸出路徑
22
+ * 支援 SHA1 與檔案大小驗證
23
+ * 可只印出解析後的下載 URL,不下載檔案
24
+ * 不需要額外 runtime dependencies
25
+
26
+ ## 官方資料來源
27
+
28
+ 本工具使用 Mojang/Minecraft 官方端點:
29
+
30
+ * [Minecraft Java version manifest](https://piston-meta.mojang.com/mc/game/version_manifest_v2.json)
31
+ * [Minecraft Java 官方伺服器下載頁](https://www.minecraft.net/en-us/download/server)
32
+
33
+ 解析後的 `server.jar` 下載 URL 通常會長這樣:
34
+
35
+ ```text
36
+ https://piston-data.mojang.com/v1/objects/<sha1>/server.jar
37
+ ```
38
+
39
+ ## 系統需求
40
+
41
+ * [Node.js](https://nodejs.org/) 18 或更新版本
42
+ * npm、pnpm 或 yarn
43
+
44
+ ## 安裝
45
+
46
+ 從 npm 全域安裝:
47
+
48
+ ```bash
49
+ npm install -g minecraft-server-dl-cli
50
+ ```
51
+
52
+ 執行:
53
+
54
+ ```bash
55
+ mc-server-dl 1.21.8
56
+ ```
57
+
58
+ 或直接使用 `npx`:
59
+
60
+ ```bash
61
+ npx minecraft-server-dl-cli 1.21.8
62
+ ```
63
+
64
+ ## 使用方式
65
+
66
+ ```bash
67
+ mc-server-dl <version> [options]
68
+ ```
69
+
70
+ 範例:
71
+
72
+ ```bash
73
+ mc-server-dl 1.21.8
74
+ ```
75
+
76
+ 會下載成:
77
+
78
+ ```text
79
+ server_1.21.8.jar
80
+ ```
81
+
82
+ ## 參數
83
+
84
+ | 參數 | 說明 |
85
+ | --------------------- | ------------------------------ |
86
+ | `-o, --output <path>` | 自訂輸出路徑 |
87
+ | `--no-verify` | 跳過 SHA1 與檔案大小驗證 |
88
+ | `--url-only` | 只印出解析後的 `server.jar` URL,不下載檔案 |
89
+ | `-h, --help` | 顯示說明 |
90
+
91
+ ## 範例
92
+
93
+ 下載指定 Minecraft Java server 版本:
94
+
95
+ ```bash
96
+ mc-server-dl 1.21.8
97
+ ```
98
+
99
+ 下載到指定路徑:
100
+
101
+ ```bash
102
+ mc-server-dl 1.21.8 -o ./servers/1.21.8/server.jar
103
+ ```
104
+
105
+ 只印出官方下載 URL:
106
+
107
+ ```bash
108
+ mc-server-dl 1.21.8 --url-only
109
+ ```
110
+
111
+ 跳過驗證:
112
+
113
+ ```bash
114
+ mc-server-dl 1.21.8 --no-verify
115
+ ```
116
+
117
+ ## Exit Codes
118
+
119
+ | Code | 意義 |
120
+ | ---: | --------------------------- |
121
+ | `0` | 成功 |
122
+ | `1` | 未預期錯誤 |
123
+ | `2` | CLI 參數錯誤 |
124
+ | `3` | metadata 取得或解析失敗 |
125
+ | `4` | 找不到指定 Minecraft 版本 |
126
+ | `5` | 版本存在,但沒有官方 `server.jar` 可下載 |
127
+ | `6` | 下載失敗 |
128
+ | `7` | 檔案大小或 SHA1 驗證失敗 |
129
+
130
+ ## 運作方式
131
+
132
+ 1. 取得 Mojang version manifest。
133
+ 2. 找到使用者指定的 Minecraft Java 版本。
134
+ 3. 取得該版本的 metadata JSON。
135
+ 4. 讀取 `downloads.server.url`。
136
+ 5. 下載官方 `server.jar`。
137
+ 6. 若 metadata 有提供,驗證檔案大小與 SHA1。
138
+
139
+ ## 免責聲明
140
+
141
+ 本專案與 Mojang Studios 或 Microsoft 沒有關聯。
142
+
143
+ Minecraft 是 Mojang Synergies AB 的商標。本工具只會解析並下載 Mojang/Minecraft 官方端點提供的檔案。
144
+
145
+ ## 授權
146
+
147
+ [MIT](LICENSE)
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createHash } from 'node:crypto';
4
+ import { createWriteStream } from 'node:fs';
5
+ import { mkdir, rename, rm, stat } from 'node:fs/promises';
6
+ import { dirname, resolve } from 'node:path';
7
+ import { Readable } from 'node:stream';
8
+ import { pipeline } from 'node:stream/promises';
9
+
10
+ const MANIFEST_URL = 'https://piston-meta.mojang.com/mc/game/version_manifest_v2.json';
11
+ const USER_AGENT = 'mc-server-dl-cli/0.1.0';
12
+
13
+ class CliError extends Error {
14
+ constructor(message, exitCode = 1) {
15
+ super(message);
16
+ this.name = 'CliError';
17
+ this.exitCode = exitCode;
18
+ }
19
+ }
20
+
21
+ function printHelp() {
22
+ console.log(`
23
+ Usage:
24
+ mc-server-dl <version> [options]
25
+
26
+ Options:
27
+ -o, --output <path> Output jar path. Default: server_<version>.jar
28
+ --no-verify Skip SHA1 verification
29
+ --url-only Print server.jar URL only, do not download
30
+ -h, --help Show help
31
+
32
+ Examples:
33
+ mc-server-dl 1.21.8
34
+ mc-server-dl 1.21.8 -o ./servers/1.21.8/server.jar
35
+ mc-server-dl 1.21.8 --url-only
36
+ `.trim());
37
+ }
38
+
39
+ function parseArgs(argv) {
40
+ const args = {
41
+ version: null,
42
+ output: null,
43
+ verify: true,
44
+ urlOnly: false
45
+ };
46
+
47
+ for (let i = 0; i < argv.length; i += 1) {
48
+ const token = argv[i];
49
+
50
+ if (token === '-h' || token === '--help') {
51
+ printHelp();
52
+ process.exit(0);
53
+ }
54
+
55
+ if (token === '-o' || token === '--output') {
56
+ const value = argv[i + 1];
57
+
58
+ if (!value || value.startsWith('-')) {
59
+ throw new CliError('Missing output path: -o <path>', 2);
60
+ }
61
+
62
+ args.output = value;
63
+ i += 1;
64
+ continue;
65
+ }
66
+
67
+ if (token === '--no-verify') {
68
+ args.verify = false;
69
+ continue;
70
+ }
71
+
72
+ if (token === '--url-only') {
73
+ args.urlOnly = true;
74
+ continue;
75
+ }
76
+
77
+ if (token.startsWith('-')) {
78
+ throw new CliError(`Unknown option: ${token}`, 2);
79
+ }
80
+
81
+ if (args.version) {
82
+ throw new CliError(`Only one Minecraft version can be specified. Extra argument: ${token}`, 2);
83
+ }
84
+
85
+ args.version = token;
86
+ }
87
+
88
+ if (!args.version) {
89
+ throw new CliError('Please specify a Minecraft version, for example: mc-server-dl 1.21.8', 2);
90
+ }
91
+
92
+ return args;
93
+ }
94
+
95
+ async function getJson(url) {
96
+ const response = await fetch(url, {
97
+ headers: {
98
+ // Make requests identifiable in server logs for easier troubleshooting.
99
+ 'User-Agent': USER_AGENT,
100
+ Accept: 'application/json'
101
+ }
102
+ });
103
+
104
+ if (!response.ok) {
105
+ throw new CliError(`HTTP ${response.status}: ${url}`, 3);
106
+ }
107
+
108
+ try {
109
+ return await response.json();
110
+ } catch (error) {
111
+ throw new CliError(`Failed to parse JSON response: ${url}`, 3);
112
+ }
113
+ }
114
+
115
+ function findVersion(manifest, version) {
116
+ const item = manifest.versions?.find((entry) => entry.id === version);
117
+
118
+ if (!item) {
119
+ const latestRelease = manifest.latest?.release ?? 'unknown';
120
+ const latestSnapshot = manifest.latest?.snapshot ?? 'unknown';
121
+
122
+ throw new CliError(
123
+ [
124
+ `Minecraft Java version not found: ${version}`,
125
+ `latest release: ${latestRelease}`,
126
+ `latest snapshot: ${latestSnapshot}`
127
+ ].join('\n'),
128
+ 4
129
+ );
130
+ }
131
+
132
+ return item;
133
+ }
134
+
135
+ async function resolveServerDownload(version) {
136
+ const manifest = await getJson(MANIFEST_URL);
137
+ const versionMeta = findVersion(manifest, version);
138
+ const versionJson = await getJson(versionMeta.url);
139
+
140
+ const server = versionJson.downloads?.server;
141
+
142
+ if (!server?.url) {
143
+ throw new CliError(`Version ${version} exists, but no official server.jar download is available.`, 5);
144
+ }
145
+
146
+ return {
147
+ version,
148
+ url: server.url,
149
+ sha1: server.sha1 ?? '',
150
+ size: Number(server.size ?? 0)
151
+ };
152
+ }
153
+
154
+ async function downloadFile(url, outputPath) {
155
+ const absoluteOutputPath = resolve(outputPath);
156
+ const tempPath = `${absoluteOutputPath}.tmp-${process.pid}`;
157
+
158
+ await mkdir(dirname(absoluteOutputPath), { recursive: true });
159
+
160
+ try {
161
+ const response = await fetch(url, {
162
+ headers: {
163
+ 'User-Agent': USER_AGENT
164
+ }
165
+ });
166
+
167
+ if (!response.ok) {
168
+ throw new CliError(`Download failed, HTTP ${response.status}: ${url}`, 6);
169
+ }
170
+
171
+ if (!response.body) {
172
+ throw new CliError('Download failed: response body is empty', 6);
173
+ }
174
+
175
+ // Write to a temporary file first to avoid replacing an existing valid server.jar on failure.
176
+ await pipeline(
177
+ Readable.fromWeb(response.body),
178
+ createWriteStream(tempPath)
179
+ );
180
+
181
+ await rename(tempPath, absoluteOutputPath);
182
+
183
+ return absoluteOutputPath;
184
+ } catch (error) {
185
+ await rm(tempPath, { force: true });
186
+
187
+ if (error instanceof CliError) {
188
+ throw error;
189
+ }
190
+
191
+ throw new CliError(`Download failed: ${error.message}`, 6);
192
+ }
193
+ }
194
+
195
+ async function sha1OfFile(path) {
196
+ const hash = createHash('sha1');
197
+ const fileStream = Readable.toWeb(
198
+ await import('node:fs').then(({ createReadStream }) => createReadStream(path))
199
+ );
200
+
201
+ const reader = fileStream.getReader();
202
+
203
+ try {
204
+ while (true) {
205
+ const { done, value } = await reader.read();
206
+
207
+ if (done) {
208
+ break;
209
+ }
210
+
211
+ hash.update(value);
212
+ }
213
+ } finally {
214
+ reader.releaseLock();
215
+ }
216
+
217
+ return hash.digest('hex');
218
+ }
219
+
220
+ async function verifyFile(path, expectedSha1, expectedSize) {
221
+ if (expectedSize > 0) {
222
+ const fileStat = await stat(path);
223
+
224
+ if (fileStat.size !== expectedSize) {
225
+ throw new CliError(
226
+ `File size mismatch: expected=${expectedSize}, actual=${fileStat.size}`,
227
+ 7
228
+ );
229
+ }
230
+ }
231
+
232
+ if (!expectedSha1) {
233
+ return;
234
+ }
235
+
236
+ const actualSha1 = await sha1OfFile(path);
237
+
238
+ if (actualSha1.toLowerCase() !== expectedSha1.toLowerCase()) {
239
+ throw new CliError(
240
+ [
241
+ 'SHA1 verification failed:',
242
+ `expected: ${expectedSha1}`,
243
+ `actual: ${actualSha1}`
244
+ ].join('\n'),
245
+ 7
246
+ );
247
+ }
248
+ }
249
+
250
+ async function main() {
251
+ const args = parseArgs(process.argv.slice(2));
252
+ const server = await resolveServerDownload(args.version);
253
+
254
+ if (args.urlOnly) {
255
+ console.log(server.url);
256
+ return;
257
+ }
258
+
259
+ console.log(`Version: ${server.version}`);
260
+ console.log(`URL: ${server.url}`);
261
+
262
+ const output = args.output ?? `server_${server.version}.jar`;
263
+ const outputPath = await downloadFile(server.url, output);
264
+
265
+ if (args.verify) {
266
+ await verifyFile(outputPath, server.sha1, server.size);
267
+ }
268
+
269
+ console.log(`Downloaded: ${outputPath}`);
270
+ }
271
+
272
+ main().catch((error) => {
273
+ if (error instanceof CliError) {
274
+ console.error(`Error: ${error.message}`);
275
+ process.exit(error.exitCode);
276
+ }
277
+
278
+ console.error(error);
279
+ process.exit(1);
280
+ });
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "minecraft-server-dl-cli",
3
+ "version": "0.1.0",
4
+ "description": "Download official Minecraft Java server.jar by version.",
5
+ "type": "module",
6
+ "bin": {
7
+ "mc-server-dl": "./bin/mc-server-dl.mjs"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "scripts": {
18
+ "dev": "node ./bin/mc-server-dl.mjs",
19
+ "pack:check": "npm pack --dry-run"
20
+ },
21
+ "keywords": [
22
+ "minecraft",
23
+ "server",
24
+ "cli",
25
+ "downloader",
26
+ "mojang"
27
+ ],
28
+ "author": "DoSer ZBlock",
29
+ "license": "MIT"
30
+ }