@bkm016/calcite 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +416 -2
- package/THIRD_PARTY_NOTICES.md +44 -0
- package/dist/accounts.d.ts +46 -0
- package/dist/accounts.js +194 -0
- package/dist/accounts.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +332 -0
- package/dist/cli.js.map +1 -0
- package/dist/client-core.d.ts +66 -0
- package/dist/client-core.js +530 -0
- package/dist/client-core.js.map +1 -0
- package/dist/client.d.ts +127 -0
- package/dist/client.js +185 -0
- package/dist/client.js.map +1 -0
- package/dist/display.d.ts +23 -0
- package/dist/display.js +164 -0
- package/dist/display.js.map +1 -0
- package/dist/hmc.d.ts +53 -0
- package/dist/hmc.js +168 -0
- package/dist/hmc.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +48 -0
- package/dist/install.js +119 -0
- package/dist/install.js.map +1 -0
- package/dist/java.d.ts +37 -0
- package/dist/java.js +222 -0
- package/dist/java.js.map +1 -0
- package/dist/loaders.d.ts +41 -0
- package/dist/loaders.js +86 -0
- package/dist/loaders.js.map +1 -0
- package/dist/lock.d.ts +13 -0
- package/dist/lock.js +79 -0
- package/dist/lock.js.map +1 -0
- package/dist/log.d.ts +13 -0
- package/dist/log.js +20 -0
- package/dist/log.js.map +1 -0
- package/dist/manager.d.ts +15 -0
- package/dist/manager.js +65 -0
- package/dist/manager.js.map +1 -0
- package/dist/mcp.d.ts +13 -0
- package/dist/mcp.js +377 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mods.d.ts +21 -0
- package/dist/mods.js +135 -0
- package/dist/mods.js.map +1 -0
- package/dist/mojang.d.ts +54 -0
- package/dist/mojang.js +73 -0
- package/dist/mojang.js.map +1 -0
- package/dist/names.d.ts +53 -0
- package/dist/names.js +267 -0
- package/dist/names.js.map +1 -0
- package/dist/net.d.ts +26 -0
- package/dist/net.js +286 -0
- package/dist/net.js.map +1 -0
- package/dist/options.d.ts +14 -0
- package/dist/options.js +50 -0
- package/dist/options.js.map +1 -0
- package/dist/paths.d.ts +29 -0
- package/dist/paths.js +49 -0
- package/dist/paths.js.map +1 -0
- package/dist/probe-server.d.ts +28 -0
- package/dist/probe-server.js +132 -0
- package/dist/probe-server.js.map +1 -0
- package/dist/types.d.ts +235 -0
- package/dist/types.js +25 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/dist/zip.d.ts +5 -0
- package/dist/zip.js +65 -0
- package/dist/zip.js.map +1 -0
- package/package.json +57 -4
- package/vendor/calcite-probe.jar +0 -0
package/dist/hmc.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
2
|
+
import { mkdirSync } from 'node:fs';
|
|
3
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { createInterface } from 'node:readline';
|
|
6
|
+
import { pathToFileURL } from 'node:url';
|
|
7
|
+
import { download, javaProxyProps } from './net.js';
|
|
8
|
+
/** Pinned HeadlessMC release (MIT, https://github.com/headlesshq/headlessmc). */
|
|
9
|
+
export const HMC_VERSION = '3.0.0-RC3';
|
|
10
|
+
export const HMC_URL = `https://github.com/headlesshq/headlessmc/releases/download/${HMC_VERSION}/headlessmc.jar`;
|
|
11
|
+
export const HMC_SHA256 = 'dcc061a336ecf7aa638794bd47840fa9970b31d562e630e84c3d587c906153a8';
|
|
12
|
+
/** Path of the HeadlessMC jar (CALCITE_HMC_JAR overrides the pinned download). */
|
|
13
|
+
export async function ensureHmc(paths) {
|
|
14
|
+
if (process.env.CALCITE_HMC_JAR)
|
|
15
|
+
return process.env.CALCITE_HMC_JAR;
|
|
16
|
+
const file = join(paths.hmcJars, `headlessmc-${HMC_VERSION}.jar`);
|
|
17
|
+
await download(process.env.CALCITE_HMC_URL || HMC_URL, file, { sha256: HMC_SHA256 });
|
|
18
|
+
return file;
|
|
19
|
+
}
|
|
20
|
+
/** Escapes one entry of a comma separated HeadlessMC list setting. */
|
|
21
|
+
export function hmcListEntry(value) {
|
|
22
|
+
return value.replace(/\\/g, '\\\\').replace(/,/g, '\\,');
|
|
23
|
+
}
|
|
24
|
+
/** Quotes one argument for HeadlessMC's `--jvm`/`--game` options, which are split like a shell command line. */
|
|
25
|
+
export function hmcQuote(arg) {
|
|
26
|
+
return `"${arg.replace(/[\\"]/g, (c) => `\\${c}`)}"`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Writes the HeadlessMC profile used when launching `versionId` from `location`. HeadlessMC only runs a version
|
|
30
|
+
* with the exact Java major the version asks for; the profile pins the runtime Calcite chose instead. With a
|
|
31
|
+
* {@code loader} the profile selects that platform (fabric, forge, neoforge) and build for Minecraft {@code versionId}.
|
|
32
|
+
*/
|
|
33
|
+
export async function writeHmcProfile(location, versionId, gameDir, javaMajor, loader) {
|
|
34
|
+
const [platform, , build] = loader ? hmcVersionArgs(versionId, loader) : ['vanilla'];
|
|
35
|
+
const version = { side: null, platform, version: versionId, build: build ?? null };
|
|
36
|
+
const profile = {
|
|
37
|
+
name: 'calcite',
|
|
38
|
+
version,
|
|
39
|
+
currentVersion: version,
|
|
40
|
+
path: pathToFileURL(gameDir).href,
|
|
41
|
+
options: { resolution: null, quickPlayPath: null, join: null, demo: false },
|
|
42
|
+
patchers: [],
|
|
43
|
+
systemProperties: {},
|
|
44
|
+
vmArgs: [],
|
|
45
|
+
gameArgs: [],
|
|
46
|
+
javaVersion: javaMajor,
|
|
47
|
+
eulaStatus: 'UNKNOWN',
|
|
48
|
+
hasDefaultClientJvmArgs: true,
|
|
49
|
+
hmcVersion: 0,
|
|
50
|
+
};
|
|
51
|
+
await mkdir(join(location, 'profiles'), { recursive: true });
|
|
52
|
+
await writeFile(join(location, 'profiles', 'calcite.json'), JSON.stringify(profile, null, 2));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* HeadlessMC's version argument: "1.21.11", or "fabric 1.21.11 0.19.5" for a mod loader. HeadlessMC names NeoForge
|
|
56
|
+
* builds without the Minecraft part of their version (NeoForge 21.11.45 for 1.21.11 is build "45").
|
|
57
|
+
*/
|
|
58
|
+
export function hmcVersionArgs(versionId, loader) {
|
|
59
|
+
if (!loader)
|
|
60
|
+
return [versionId];
|
|
61
|
+
let build = loader.build;
|
|
62
|
+
if (build && loader.kind === 'neoforge') {
|
|
63
|
+
const prefix = `${versionId.startsWith('1.') ? versionId.slice(2) : versionId}.`;
|
|
64
|
+
if (build.startsWith(prefix))
|
|
65
|
+
build = build.slice(prefix.length);
|
|
66
|
+
}
|
|
67
|
+
return [loader.kind, versionId, ...(build ? [build] : [])];
|
|
68
|
+
}
|
|
69
|
+
const live = new Set();
|
|
70
|
+
let exitGuard = false;
|
|
71
|
+
/** Kills every still-running HeadlessMC tree when the host process exits, so nothing is orphaned. */
|
|
72
|
+
function guardExit() {
|
|
73
|
+
if (exitGuard)
|
|
74
|
+
return;
|
|
75
|
+
exitGuard = true;
|
|
76
|
+
const killAll = () => {
|
|
77
|
+
for (const child of live) {
|
|
78
|
+
if (child.pid === undefined || child.exitCode !== null)
|
|
79
|
+
continue;
|
|
80
|
+
try {
|
|
81
|
+
if (process.platform === 'win32')
|
|
82
|
+
spawnSync('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' });
|
|
83
|
+
else
|
|
84
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// already gone
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
live.clear();
|
|
91
|
+
};
|
|
92
|
+
process.once('exit', killAll);
|
|
93
|
+
// A signal without a handler terminates Node without an 'exit' event; when nobody else handles it, clean up and
|
|
94
|
+
// die with the conventional status. Hosts that handle the signal themselves end up in 'exit' above.
|
|
95
|
+
for (const [signal, code] of [['SIGTERM', 143], ['SIGHUP', 129], ['SIGINT', 130]]) {
|
|
96
|
+
process.on(signal, () => {
|
|
97
|
+
if (process.listenerCount(signal) > 1)
|
|
98
|
+
return;
|
|
99
|
+
killAll();
|
|
100
|
+
process.exit(code);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Runs one HeadlessMC command non-interactively; HeadlessMC exits once the command (and a launched game) finished.
|
|
106
|
+
* Settings are passed as -D system properties, which take precedence over any config file.
|
|
107
|
+
*/
|
|
108
|
+
export function runHmc(opts) {
|
|
109
|
+
const props = {
|
|
110
|
+
'hmc.files.location': opts.location,
|
|
111
|
+
'hmc.jline.enabled': 'false',
|
|
112
|
+
'hmc.log.console-level': 'INFO',
|
|
113
|
+
...javaProxyProps(),
|
|
114
|
+
...opts.props,
|
|
115
|
+
};
|
|
116
|
+
const args = [...Object.entries(props).map(([k, v]) => `-D${k}=${v}`), '-jar', opts.hmcJar, ...opts.command];
|
|
117
|
+
mkdirSync(opts.location, { recursive: true });
|
|
118
|
+
const child = spawn(opts.javaPath, args, {
|
|
119
|
+
cwd: opts.location,
|
|
120
|
+
env: opts.env ?? process.env,
|
|
121
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
122
|
+
// own process group on POSIX so the whole tree (launcher + game) can be stopped together
|
|
123
|
+
detached: process.platform !== 'win32',
|
|
124
|
+
windowsHide: true,
|
|
125
|
+
});
|
|
126
|
+
guardExit();
|
|
127
|
+
live.add(child);
|
|
128
|
+
child.once('exit', () => live.delete(child));
|
|
129
|
+
for (const stream of [child.stdout, child.stderr]) {
|
|
130
|
+
if (!stream)
|
|
131
|
+
continue;
|
|
132
|
+
const rl = createInterface({ input: stream });
|
|
133
|
+
rl.on('line', (line) => opts.onLine?.(line));
|
|
134
|
+
}
|
|
135
|
+
const exited = new Promise((resolve) => {
|
|
136
|
+
child.once('exit', (code) => resolve(code));
|
|
137
|
+
child.once('error', () => resolve(null));
|
|
138
|
+
});
|
|
139
|
+
return { child, exited };
|
|
140
|
+
}
|
|
141
|
+
/** Stops a process tree started by {@link runHmc}. */
|
|
142
|
+
export async function killTree(child, graceMs = 5000) {
|
|
143
|
+
if (child.exitCode !== null || child.signalCode !== null || child.pid === undefined)
|
|
144
|
+
return;
|
|
145
|
+
const exited = new Promise((resolve) => child.once('exit', () => resolve()));
|
|
146
|
+
if (process.platform === 'win32') {
|
|
147
|
+
spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' });
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
try {
|
|
151
|
+
process.kill(-child.pid, 'SIGTERM');
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
child.kill('SIGTERM');
|
|
155
|
+
}
|
|
156
|
+
const timer = setTimeout(() => {
|
|
157
|
+
try {
|
|
158
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
child.kill('SIGKILL');
|
|
162
|
+
}
|
|
163
|
+
}, graceMs);
|
|
164
|
+
exited.finally(() => clearTimeout(timer));
|
|
165
|
+
}
|
|
166
|
+
await Promise.race([exited, new Promise((r) => setTimeout(r, graceMs * 2))]);
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=hmc.js.map
|
package/dist/hmc.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hmc.js","sourceRoot":"","sources":["../src/hmc.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { Client, CalciteError, installVersion, parseServer } from './client.js';
|
|
2
|
+
export type { Account, BlockFace, BlockInfo, BlockPosition, ChatLine, ClickMode, ClientOptions, ClientStatus, ContainerInfo, DigResult, EntityInfo, EntityQuery, GameState, HitTarget, InstallOptions, InventoryInfo, ItemInfo, LogLine, MoveControls, Phase, PlayerState, RenderMode, ServerAddress, WalkResult, } from './client.js';
|
|
3
|
+
export { listAccounts, removeAccount, startLogin } from './accounts.js';
|
|
4
|
+
export type { AccountInfo, LoginHandle } from './accounts.js';
|
|
5
|
+
export { resolvePaths } from './paths.js';
|
|
6
|
+
export type { CalcitePaths } from './paths.js';
|
|
7
|
+
export { findJavaInstalls, ensureJava } from './java.js';
|
|
8
|
+
export type { JavaInstall } from './java.js';
|
|
9
|
+
export { resolveVersion } from './mojang.js';
|
|
10
|
+
export { setLogLevel } from './log.js';
|
|
11
|
+
export { ClientManager } from './manager.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { Client, CalciteError, installVersion, parseServer } from './client.js';
|
|
2
|
+
export { listAccounts, removeAccount, startLogin } from './accounts.js';
|
|
3
|
+
export { resolvePaths } from './paths.js';
|
|
4
|
+
export { findJavaInstalls, ensureJava } from './java.js';
|
|
5
|
+
export { resolveVersion } from './mojang.js';
|
|
6
|
+
export { setLogLevel } from './log.js';
|
|
7
|
+
export { ClientManager } from './manager.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA2BhF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type JavaInstall } from './java.js';
|
|
2
|
+
import { type InstalledLoader } from './loaders.js';
|
|
3
|
+
import { type VersionJson } from './mojang.js';
|
|
4
|
+
import { type NameMode } from './names.js';
|
|
5
|
+
import { type CalcitePaths } from './paths.js';
|
|
6
|
+
/** Copies the probe jar to the space-free probe directory (content-addressed). */
|
|
7
|
+
export declare function installProbe(paths: CalcitePaths): Promise<string>;
|
|
8
|
+
export interface PrepareOptions {
|
|
9
|
+
/** Minecraft version id, "release" or "snapshot". */
|
|
10
|
+
version: string;
|
|
11
|
+
/** Mod loader: "fabric", "forge", "neoforge", optionally "@<loader version>". */
|
|
12
|
+
loader?: string;
|
|
13
|
+
javaPath?: string;
|
|
14
|
+
allowJavaDownload?: boolean;
|
|
15
|
+
/** Whether the game will run without a renderer (picks Java 17 for the Java 8 versions). */
|
|
16
|
+
headless?: boolean;
|
|
17
|
+
/** HeadlessMC output while installing a loader. */
|
|
18
|
+
onLine?: (line: string) => void;
|
|
19
|
+
}
|
|
20
|
+
export interface PreparedGame {
|
|
21
|
+
json: VersionJson;
|
|
22
|
+
clientJar: string;
|
|
23
|
+
loader?: InstalledLoader;
|
|
24
|
+
names: NameMode;
|
|
25
|
+
java: JavaInstall;
|
|
26
|
+
launcherJava: JavaInstall;
|
|
27
|
+
hmcJar: string;
|
|
28
|
+
}
|
|
29
|
+
/** Downloads and verifies what launching needs: version files, Java, HeadlessMC, the mod loader and name tables. */
|
|
30
|
+
export declare function prepareGame(paths: CalcitePaths, opts: PrepareOptions): Promise<PreparedGame>;
|
|
31
|
+
export interface InstallOptions {
|
|
32
|
+
paths?: CalcitePaths;
|
|
33
|
+
/** Mod loader to install as well: "fabric", "forge", "neoforge", optionally "@<loader version>". */
|
|
34
|
+
loader?: string;
|
|
35
|
+
javaPath?: string;
|
|
36
|
+
allowJavaDownload?: boolean;
|
|
37
|
+
onLine?: (line: string) => void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Downloads everything a version needs (client, libraries, natives, assets, mappings, Java, HeadlessMC, the mod
|
|
41
|
+
* loader) without starting the game, so later launches start fast and work offline.
|
|
42
|
+
*/
|
|
43
|
+
export declare function installVersion(spec: string, opts?: InstallOptions): Promise<{
|
|
44
|
+
id: string;
|
|
45
|
+
loader?: string;
|
|
46
|
+
java: string;
|
|
47
|
+
probe: string;
|
|
48
|
+
}>;
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { copyFile, mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { ensureHmc, hmcListEntry, hmcQuote, hmcVersionArgs, runHmc, writeHmcProfile } from './hmc.js';
|
|
6
|
+
import { ensureJava, ensureLauncherJava } from './java.js';
|
|
7
|
+
import { findLoader, formatLoader, parseLoader } from './loaders.js';
|
|
8
|
+
import { withLock } from './lock.js';
|
|
9
|
+
import { ensureClientJar, getVersionJson, requiredJavaMajor, resolveVersion } from './mojang.js';
|
|
10
|
+
import { resolveNames } from './names.js';
|
|
11
|
+
import { resolvePaths } from './paths.js';
|
|
12
|
+
import { CalciteError } from './types.js';
|
|
13
|
+
/** HeadlessMC 3's LWJGL stubs need Java 9+; the Java 8 versions (up to 1.16.5) run headless on Java 17 instead. */
|
|
14
|
+
const HEADLESS_MIN_JAVA = 17;
|
|
15
|
+
function probeJarSource() {
|
|
16
|
+
return process.env.CALCITE_PROBE_JAR || fileURLToPath(new URL('../vendor/calcite-probe.jar', import.meta.url));
|
|
17
|
+
}
|
|
18
|
+
/** Copies the probe jar to the space-free probe directory (content-addressed). */
|
|
19
|
+
export async function installProbe(paths) {
|
|
20
|
+
const source = await readFile(probeJarSource());
|
|
21
|
+
const hash = createHash('sha256').update(source).digest('hex').slice(0, 12);
|
|
22
|
+
const target = join(paths.probe, `calcite-probe-${hash}.jar`);
|
|
23
|
+
try {
|
|
24
|
+
await stat(target);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
await mkdir(paths.probe, { recursive: true });
|
|
28
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
29
|
+
await writeFile(tmp, source);
|
|
30
|
+
await copyFile(tmp, target);
|
|
31
|
+
await rm(tmp, { force: true });
|
|
32
|
+
}
|
|
33
|
+
return target;
|
|
34
|
+
}
|
|
35
|
+
/** Downloads and verifies what launching needs: version files, Java, HeadlessMC, the mod loader and name tables. */
|
|
36
|
+
export async function prepareGame(paths, opts) {
|
|
37
|
+
const spec = parseLoader(opts.loader);
|
|
38
|
+
const json = await getVersionJson(paths, await resolveVersion(paths, opts.version));
|
|
39
|
+
const clientJar = await ensureClientJar(paths, json);
|
|
40
|
+
const required = requiredJavaMajor(json);
|
|
41
|
+
const modded = !!spec && spec.kind !== 'fabric';
|
|
42
|
+
if (modded && opts.headless && required < 9) {
|
|
43
|
+
throw new CalciteError('unsupported_loader', `${spec.kind} for Minecraft ${json.id} needs Java ${required}, which cannot run headless; use render "on-demand"`);
|
|
44
|
+
}
|
|
45
|
+
const java = await ensureJava(paths, opts.headless && required < 9 ? HEADLESS_MIN_JAVA : required, {
|
|
46
|
+
javaPath: opts.javaPath,
|
|
47
|
+
allowDownload: opts.allowJavaDownload,
|
|
48
|
+
exact: modded,
|
|
49
|
+
});
|
|
50
|
+
const launcherJava = await ensureLauncherJava(paths, { allowDownload: opts.allowJavaDownload });
|
|
51
|
+
const hmcJar = await ensureHmc(paths);
|
|
52
|
+
const loader = spec ? await ensureLoader(paths, json, spec, { java, launcherJava, hmcJar, onLine: opts.onLine }) : undefined;
|
|
53
|
+
const names = await resolveNames(paths, json, clientJar, loader);
|
|
54
|
+
return { json, clientJar, loader, names, java, launcherJava, hmcJar };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Lets HeadlessMC prepare a version without playing it: it downloads the game files (and installs a mod loader)
|
|
58
|
+
* when launching; `-version` makes the game JVM exit right away.
|
|
59
|
+
*/
|
|
60
|
+
async function hmcDryLaunch(paths, json, loader, rt) {
|
|
61
|
+
const location = join(paths.hmcHome, 'install', randomBytes(6).toString('hex'));
|
|
62
|
+
try {
|
|
63
|
+
await writeHmcProfile(location, json.id, join(location, 'game'), rt.java.major, loader);
|
|
64
|
+
const run = runHmc({
|
|
65
|
+
javaPath: rt.launcherJava.path,
|
|
66
|
+
hmcJar: rt.hmcJar,
|
|
67
|
+
location,
|
|
68
|
+
props: {
|
|
69
|
+
'hmc.files.mc': paths.minecraft,
|
|
70
|
+
'hmc.files.game': join(location, 'game'),
|
|
71
|
+
'hmc.java.versions': hmcListEntry(rt.java.path),
|
|
72
|
+
'hmc.java.download': 'false',
|
|
73
|
+
},
|
|
74
|
+
command: ['launch', ...hmcVersionArgs(json.id, loader), '--offline', '--headless', `--jvm=${hmcQuote('-version')}`],
|
|
75
|
+
onLine: rt.onLine,
|
|
76
|
+
});
|
|
77
|
+
const code = await run.exited;
|
|
78
|
+
const what = loader ? `${formatLoader(loader)} for ${json.id}` : json.id;
|
|
79
|
+
if (code !== 0)
|
|
80
|
+
throw new CalciteError('install_failed', `HeadlessMC exited with code ${code} while installing ${what}`);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
await rm(location, { recursive: true, force: true }).catch(() => undefined);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** The installed loader matching {@code spec}, installing it through HeadlessMC first when missing. */
|
|
87
|
+
async function ensureLoader(paths, json, spec, rt) {
|
|
88
|
+
const found = await findLoader(paths, json.id, spec);
|
|
89
|
+
if (found)
|
|
90
|
+
return found;
|
|
91
|
+
return withLock(join(paths.minecraft, 'versions', `.calcite-${spec.kind}-${json.id}.lock`), async () => {
|
|
92
|
+
const again = await findLoader(paths, json.id, spec);
|
|
93
|
+
if (again)
|
|
94
|
+
return again;
|
|
95
|
+
await hmcDryLaunch(paths, json, spec, rt);
|
|
96
|
+
const installed = await findLoader(paths, json.id, spec);
|
|
97
|
+
if (!installed) {
|
|
98
|
+
throw new CalciteError('install_failed', `HeadlessMC did not install ${formatLoader(spec)} for Minecraft ${json.id} (is there a build for this version?)`);
|
|
99
|
+
}
|
|
100
|
+
return installed;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Downloads everything a version needs (client, libraries, natives, assets, mappings, Java, HeadlessMC, the mod
|
|
105
|
+
* loader) without starting the game, so later launches start fast and work offline.
|
|
106
|
+
*/
|
|
107
|
+
export async function installVersion(spec, opts = {}) {
|
|
108
|
+
const paths = opts.paths ?? resolvePaths();
|
|
109
|
+
const game = await prepareGame(paths, { version: spec, loader: opts.loader, javaPath: opts.javaPath, allowJavaDownload: opts.allowJavaDownload, onLine: opts.onLine });
|
|
110
|
+
await installProbe(paths);
|
|
111
|
+
await hmcDryLaunch(paths, game.json, game.loader, game);
|
|
112
|
+
return {
|
|
113
|
+
id: game.json.id,
|
|
114
|
+
loader: game.loader ? `${game.loader.kind}@${game.loader.build}` : undefined,
|
|
115
|
+
java: game.java.version,
|
|
116
|
+
probe: game.names.kind === 'unsupported' ? game.names.reason : 'supported',
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
|
package/dist/java.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CalcitePaths } from './paths.js';
|
|
2
|
+
export interface JavaInstall {
|
|
3
|
+
/** Path of the java executable. */
|
|
4
|
+
path: string;
|
|
5
|
+
/** Major version (8, 17, 21, ...). */
|
|
6
|
+
major: number;
|
|
7
|
+
/** Full version string. */
|
|
8
|
+
version: string;
|
|
9
|
+
}
|
|
10
|
+
/** Parses "java.specification.version = 1.8" / "= 21" from -XshowSettings output. */
|
|
11
|
+
export declare function parseJavaSettings(output: string): {
|
|
12
|
+
major: number;
|
|
13
|
+
version: string;
|
|
14
|
+
} | null;
|
|
15
|
+
export declare function findJavaInstalls(paths: CalcitePaths): Promise<JavaInstall[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Picks a runtime for {@code major}: exact major first; Minecraft 1.17+ (Java 16/17/21/25) also runs on newer
|
|
18
|
+
* runtimes, legacy versions (Java 8) need exactly 8.
|
|
19
|
+
*/
|
|
20
|
+
export declare function pickJava(installs: JavaInstall[], major: number): JavaInstall | undefined;
|
|
21
|
+
/** Downloads an Eclipse Temurin runtime for {@code major} into CALCITE_HOME/java. */
|
|
22
|
+
export declare function downloadJava(paths: CalcitePaths, major: number): Promise<JavaInstall>;
|
|
23
|
+
export interface EnsureJavaOptions {
|
|
24
|
+
/** Explicit java executable; skips detection. */
|
|
25
|
+
javaPath?: string;
|
|
26
|
+
/** Download a runtime when none is installed (default true). */
|
|
27
|
+
allowDownload?: boolean;
|
|
28
|
+
/** Only accept exactly {@code major} (Forge and NeoForge refuse newer runtimes). */
|
|
29
|
+
exact?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare function ensureJava(paths: CalcitePaths, major: number, opts?: EnsureJavaOptions): Promise<JavaInstall>;
|
|
32
|
+
/** Java release HeadlessMC 3 itself runs on (the game may use another runtime). */
|
|
33
|
+
export declare const LAUNCHER_JAVA_MAJOR = 25;
|
|
34
|
+
/** A runtime for HeadlessMC itself (Java 25+); prefers an installed one, downloads Java 25 otherwise. */
|
|
35
|
+
export declare function ensureLauncherJava(paths: CalcitePaths, opts?: {
|
|
36
|
+
allowDownload?: boolean;
|
|
37
|
+
}): Promise<JavaInstall>;
|
package/dist/java.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { mkdir, readdir, rename, rm, stat } from 'node:fs/promises';
|
|
4
|
+
import { delimiter, join } from 'node:path';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
import { withLock } from './lock.js';
|
|
7
|
+
import { logger } from './log.js';
|
|
8
|
+
import { download, httpJson } from './net.js';
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
const log = logger('java');
|
|
11
|
+
const EXE = process.platform === 'win32' ? 'java.exe' : 'java';
|
|
12
|
+
/** Parses "java.specification.version = 1.8" / "= 21" from -XshowSettings output. */
|
|
13
|
+
export function parseJavaSettings(output) {
|
|
14
|
+
const spec = /java\.specification\.version = (\S+)/.exec(output);
|
|
15
|
+
const version = /java\.version = (\S+)/.exec(output);
|
|
16
|
+
if (!spec)
|
|
17
|
+
return null;
|
|
18
|
+
const raw = spec[1];
|
|
19
|
+
const major = raw.startsWith('1.') ? Number(raw.slice(2)) : Number(raw);
|
|
20
|
+
if (!Number.isFinite(major))
|
|
21
|
+
return null;
|
|
22
|
+
return { major, version: version ? version[1] : raw };
|
|
23
|
+
}
|
|
24
|
+
async function probeJava(path) {
|
|
25
|
+
try {
|
|
26
|
+
const env = { ...process.env };
|
|
27
|
+
delete env.JAVA_TOOL_OPTIONS;
|
|
28
|
+
delete env._JAVA_OPTIONS;
|
|
29
|
+
const { stdout, stderr } = await execFileAsync(path, ['-XshowSettings:properties', '-version'], { timeout: 20_000, env });
|
|
30
|
+
const parsed = parseJavaSettings(`${stdout}\n${stderr}`);
|
|
31
|
+
return parsed ? { path, ...parsed } : null;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function subdirs(dir) {
|
|
38
|
+
try {
|
|
39
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
40
|
+
return entries.filter((e) => e.isDirectory()).map((e) => join(dir, e.name));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Java homes to look at: env, PATH, well-known install locations and Calcite-managed runtimes. */
|
|
47
|
+
async function candidateExecutables(paths) {
|
|
48
|
+
const homes = [];
|
|
49
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
50
|
+
if (value && (key === 'JAVA_HOME' || /^JAVA_HOME_\d+/.test(key) || /^CALCITE_JAVA_\d+$/.test(key)))
|
|
51
|
+
homes.push(value);
|
|
52
|
+
}
|
|
53
|
+
const roots = [];
|
|
54
|
+
if (process.platform === 'win32') {
|
|
55
|
+
for (const base of [process.env.ProgramFiles, process.env['ProgramFiles(x86)'], process.env.ProgramW6432]) {
|
|
56
|
+
if (!base)
|
|
57
|
+
continue;
|
|
58
|
+
for (const vendor of ['Java', 'Eclipse Adoptium', 'Eclipse Foundation', 'Microsoft', 'Zulu', 'Amazon Corretto', 'BellSoft', 'Semeru']) {
|
|
59
|
+
roots.push(join(base, vendor));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (process.env.LOCALAPPDATA)
|
|
63
|
+
roots.push(join(process.env.LOCALAPPDATA, 'Programs', 'Eclipse Adoptium'));
|
|
64
|
+
}
|
|
65
|
+
else if (process.platform === 'darwin') {
|
|
66
|
+
roots.push('/Library/Java/JavaVirtualMachines', join(process.env.HOME || '', 'Library/Java/JavaVirtualMachines'));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
roots.push('/usr/lib/jvm', '/usr/java', '/opt/java', '/opt/jdk', join(process.env.HOME || '', '.sdkman/candidates/java'));
|
|
70
|
+
}
|
|
71
|
+
for (const root of roots)
|
|
72
|
+
homes.push(...(await subdirs(root)));
|
|
73
|
+
// runtimes downloaded by Calcite: java/temurin-<major>-<os>-<arch>/<archive root>/
|
|
74
|
+
for (const managed of await subdirs(paths.java))
|
|
75
|
+
homes.push(managed, ...(await subdirs(managed)));
|
|
76
|
+
const exes = new Set();
|
|
77
|
+
for (const home of homes) {
|
|
78
|
+
for (const bin of [join(home, 'bin', EXE), join(home, 'Contents', 'Home', 'bin', EXE)]) {
|
|
79
|
+
if (existsSync(bin))
|
|
80
|
+
exes.add(bin);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const dir of (process.env.PATH || '').split(delimiter)) {
|
|
84
|
+
const bin = join(dir, EXE);
|
|
85
|
+
if (dir && existsSync(bin))
|
|
86
|
+
exes.add(bin);
|
|
87
|
+
}
|
|
88
|
+
return [...exes];
|
|
89
|
+
}
|
|
90
|
+
export async function findJavaInstalls(paths) {
|
|
91
|
+
const results = await Promise.all((await candidateExecutables(paths)).map(probeJava));
|
|
92
|
+
const seen = new Set();
|
|
93
|
+
return results.filter((j) => {
|
|
94
|
+
if (!j)
|
|
95
|
+
return false;
|
|
96
|
+
const key = `${j.major}:${j.version}`;
|
|
97
|
+
if (seen.has(key))
|
|
98
|
+
return false;
|
|
99
|
+
seen.add(key);
|
|
100
|
+
return true;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Picks a runtime for {@code major}: exact major first; Minecraft 1.17+ (Java 16/17/21/25) also runs on newer
|
|
105
|
+
* runtimes, legacy versions (Java 8) need exactly 8.
|
|
106
|
+
*/
|
|
107
|
+
export function pickJava(installs, major) {
|
|
108
|
+
const exact = installs.filter((j) => j.major === major);
|
|
109
|
+
if (exact.length)
|
|
110
|
+
return exact[0];
|
|
111
|
+
if (major <= 8)
|
|
112
|
+
return undefined;
|
|
113
|
+
return installs.filter((j) => j.major > major).sort((a, b) => a.major - b.major)[0];
|
|
114
|
+
}
|
|
115
|
+
function adoptiumOs() {
|
|
116
|
+
switch (process.platform) {
|
|
117
|
+
case 'win32':
|
|
118
|
+
return 'windows';
|
|
119
|
+
case 'darwin':
|
|
120
|
+
return 'mac';
|
|
121
|
+
case 'linux':
|
|
122
|
+
return 'linux';
|
|
123
|
+
default:
|
|
124
|
+
throw new Error(`Automatic Java download is not supported on ${process.platform}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function adoptiumArch() {
|
|
128
|
+
switch (process.arch) {
|
|
129
|
+
case 'x64':
|
|
130
|
+
return 'x64';
|
|
131
|
+
case 'arm64':
|
|
132
|
+
return 'aarch64';
|
|
133
|
+
case 'ia32':
|
|
134
|
+
return 'x32';
|
|
135
|
+
default:
|
|
136
|
+
throw new Error(`Automatic Java download is not supported on ${process.arch}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Downloads an Eclipse Temurin runtime for {@code major} into CALCITE_HOME/java. */
|
|
140
|
+
export async function downloadJava(paths, major) {
|
|
141
|
+
const os = adoptiumOs();
|
|
142
|
+
const arch = adoptiumArch();
|
|
143
|
+
const target = join(paths.java, `temurin-${major}-${os}-${arch}`);
|
|
144
|
+
return withLock(`${target}.lock`, async () => {
|
|
145
|
+
const existing = await findInDir(target);
|
|
146
|
+
if (existing)
|
|
147
|
+
return existing;
|
|
148
|
+
let asset;
|
|
149
|
+
for (const image of ['jre', 'jdk']) {
|
|
150
|
+
const url = `https://api.adoptium.net/v3/assets/latest/${major}/hotspot?architecture=${arch}&image_type=${image}&os=${os}&vendor=eclipse`;
|
|
151
|
+
const assets = await httpJson(url).catch(() => []);
|
|
152
|
+
asset = assets[0];
|
|
153
|
+
if (asset)
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
if (!asset)
|
|
157
|
+
throw new Error(`No Temurin ${major} build for ${os}/${arch}`);
|
|
158
|
+
const pkg = asset.binary.package;
|
|
159
|
+
log.info(`downloading Temurin ${asset.version.semver} (${pkg.name})`);
|
|
160
|
+
const archive = join(paths.java, pkg.name);
|
|
161
|
+
await download(pkg.link, archive, { sha256: pkg.checksum });
|
|
162
|
+
const staging = `${target}.extract`;
|
|
163
|
+
await rm(staging, { recursive: true, force: true });
|
|
164
|
+
await mkdir(staging, { recursive: true });
|
|
165
|
+
// tar handles .tar.gz everywhere and .zip on Windows 10+ (bsdtar)
|
|
166
|
+
await execFileAsync('tar', ['-xf', archive, '-C', staging], { timeout: 10 * 60_000 });
|
|
167
|
+
await rm(target, { recursive: true, force: true });
|
|
168
|
+
await rename(staging, target);
|
|
169
|
+
await rm(archive, { force: true });
|
|
170
|
+
const installed = await findInDir(target);
|
|
171
|
+
if (!installed)
|
|
172
|
+
throw new Error(`Extracted Java ${major} but found no java executable in ${target}`);
|
|
173
|
+
return installed;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
async function findInDir(dir) {
|
|
177
|
+
try {
|
|
178
|
+
await stat(dir);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
for (const home of [dir, ...(await subdirs(dir))]) {
|
|
184
|
+
for (const bin of [join(home, 'bin', EXE), join(home, 'Contents', 'Home', 'bin', EXE)]) {
|
|
185
|
+
if (existsSync(bin)) {
|
|
186
|
+
const j = await probeJava(bin);
|
|
187
|
+
if (j)
|
|
188
|
+
return j;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
export async function ensureJava(paths, major, opts = {}) {
|
|
195
|
+
if (opts.javaPath) {
|
|
196
|
+
const j = await probeJava(opts.javaPath);
|
|
197
|
+
if (!j)
|
|
198
|
+
throw new Error(`Not a working java executable: ${opts.javaPath}`);
|
|
199
|
+
return j;
|
|
200
|
+
}
|
|
201
|
+
const installs = await findJavaInstalls(paths);
|
|
202
|
+
const picked = opts.exact ? installs.find((j) => j.major === major) : pickJava(installs, major);
|
|
203
|
+
if (picked)
|
|
204
|
+
return picked;
|
|
205
|
+
if (opts.allowDownload === false) {
|
|
206
|
+
throw new Error(`Java ${major}${opts.exact ? '' : ' or newer'} is required but not installed (automatic download disabled)`);
|
|
207
|
+
}
|
|
208
|
+
return downloadJava(paths, major);
|
|
209
|
+
}
|
|
210
|
+
/** Java release HeadlessMC 3 itself runs on (the game may use another runtime). */
|
|
211
|
+
export const LAUNCHER_JAVA_MAJOR = 25;
|
|
212
|
+
/** A runtime for HeadlessMC itself (Java 25+); prefers an installed one, downloads Java 25 otherwise. */
|
|
213
|
+
export async function ensureLauncherJava(paths, opts = {}) {
|
|
214
|
+
const picked = pickJava(await findJavaInstalls(paths), LAUNCHER_JAVA_MAJOR);
|
|
215
|
+
if (picked)
|
|
216
|
+
return picked;
|
|
217
|
+
if (opts.allowDownload === false) {
|
|
218
|
+
throw new Error(`HeadlessMC needs Java ${LAUNCHER_JAVA_MAJOR} or newer, which is not installed (automatic download disabled)`);
|
|
219
|
+
}
|
|
220
|
+
return downloadJava(paths, LAUNCHER_JAVA_MAJOR);
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=java.js.map
|