@kzheart_/mc-pilot 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/bin/mct +2 -0
- package/data/variants.json +139 -0
- package/dist/client/ClientManager.d.ts +82 -0
- package/dist/client/ClientManager.js +213 -0
- package/dist/client/WebSocketClient.d.ts +15 -0
- package/dist/client/WebSocketClient.js +76 -0
- package/dist/commands/block.d.ts +2 -0
- package/dist/commands/block.js +52 -0
- package/dist/commands/book.d.ts +2 -0
- package/dist/commands/book.js +21 -0
- package/dist/commands/channel.d.ts +2 -0
- package/dist/commands/channel.js +24 -0
- package/dist/commands/chat.d.ts +2 -0
- package/dist/commands/chat.js +31 -0
- package/dist/commands/client.d.ts +2 -0
- package/dist/commands/client.js +87 -0
- package/dist/commands/combat.d.ts +2 -0
- package/dist/commands/combat.js +46 -0
- package/dist/commands/craft.d.ts +5 -0
- package/dist/commands/craft.js +45 -0
- package/dist/commands/effects.d.ts +2 -0
- package/dist/commands/effects.js +16 -0
- package/dist/commands/entity.d.ts +2 -0
- package/dist/commands/entity.js +53 -0
- package/dist/commands/gui.d.ts +2 -0
- package/dist/commands/gui.js +49 -0
- package/dist/commands/hud.d.ts +2 -0
- package/dist/commands/hud.js +16 -0
- package/dist/commands/input.d.ts +2 -0
- package/dist/commands/input.js +124 -0
- package/dist/commands/inventory.d.ts +2 -0
- package/dist/commands/inventory.js +28 -0
- package/dist/commands/look.d.ts +2 -0
- package/dist/commands/look.js +37 -0
- package/dist/commands/move.d.ts +2 -0
- package/dist/commands/move.js +50 -0
- package/dist/commands/position.d.ts +2 -0
- package/dist/commands/position.js +7 -0
- package/dist/commands/request-helpers.d.ts +26 -0
- package/dist/commands/request-helpers.js +58 -0
- package/dist/commands/resourcepack.d.ts +2 -0
- package/dist/commands/resourcepack.js +9 -0
- package/dist/commands/rotation.d.ts +2 -0
- package/dist/commands/rotation.js +7 -0
- package/dist/commands/screen.d.ts +2 -0
- package/dist/commands/screen.js +7 -0
- package/dist/commands/screenshot.d.ts +2 -0
- package/dist/commands/screenshot.js +14 -0
- package/dist/commands/server.d.ts +2 -0
- package/dist/commands/server.js +66 -0
- package/dist/commands/sign.d.ts +2 -0
- package/dist/commands/sign.js +30 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +17 -0
- package/dist/commands/wait.d.ts +2 -0
- package/dist/commands/wait.js +23 -0
- package/dist/download/CacheManager.d.ts +20 -0
- package/dist/download/CacheManager.js +50 -0
- package/dist/download/DownloadUtils.d.ts +2 -0
- package/dist/download/DownloadUtils.js +23 -0
- package/dist/download/JavaDetector.d.ts +7 -0
- package/dist/download/JavaDetector.js +31 -0
- package/dist/download/ModVariantCatalog.d.ts +10 -0
- package/dist/download/ModVariantCatalog.js +52 -0
- package/dist/download/SearchCommand.d.ts +29 -0
- package/dist/download/SearchCommand.js +37 -0
- package/dist/download/VersionMatrix.d.ts +72 -0
- package/dist/download/VersionMatrix.js +227 -0
- package/dist/download/client/Arm64LwjglPatcher.d.ts +5 -0
- package/dist/download/client/Arm64LwjglPatcher.js +153 -0
- package/dist/download/client/ClientDownloader.d.ts +42 -0
- package/dist/download/client/ClientDownloader.js +233 -0
- package/dist/download/client/FabricRuntimeDownloader.d.ts +10 -0
- package/dist/download/client/FabricRuntimeDownloader.js +91 -0
- package/dist/download/server/ServerDownloader.d.ts +51 -0
- package/dist/download/server/ServerDownloader.js +196 -0
- package/dist/download/types.d.ts +37 -0
- package/dist/download/types.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +89 -0
- package/dist/server/ServerManager.d.ts +63 -0
- package/dist/server/ServerManager.js +114 -0
- package/dist/util/command.d.ts +10 -0
- package/dist/util/command.js +35 -0
- package/dist/util/config.d.ts +30 -0
- package/dist/util/config.js +59 -0
- package/dist/util/context.d.ts +17 -0
- package/dist/util/context.js +16 -0
- package/dist/util/errors.d.ts +20 -0
- package/dist/util/errors.js +37 -0
- package/dist/util/net.d.ts +5 -0
- package/dist/util/net.js +35 -0
- package/dist/util/output.d.ts +4 -0
- package/dist/util/output.js +23 -0
- package/dist/util/process.d.ts +3 -0
- package/dist/util/process.js +32 -0
- package/dist/util/state.d.ts +10 -0
- package/dist/util/state.js +40 -0
- package/package.json +54 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import type { CommandContext, GlobalOptions } from "../util/context.js";
|
|
3
|
+
export interface RequestPayload<TOptions> {
|
|
4
|
+
args: string[];
|
|
5
|
+
options: TOptions;
|
|
6
|
+
globalOptions: GlobalOptions;
|
|
7
|
+
}
|
|
8
|
+
export declare function sendClientRequest(context: CommandContext, clientName: string | undefined, action: string, params: Record<string, unknown>, timeoutSeconds?: number): Promise<unknown>;
|
|
9
|
+
export declare function createRequestAction<TOptions = Record<string, any>>(action: string, buildParams: (payload: RequestPayload<TOptions>) => Record<string, unknown>, timeoutSelector?: (payload: RequestPayload<TOptions>, context: CommandContext) => number | undefined): (this: Command, ...input: unknown[]) => Promise<void>;
|
|
10
|
+
export declare function parseJson(text: string, fieldName: string): Record<string, unknown>;
|
|
11
|
+
export declare function parseNumberList(text: string): number[];
|
|
12
|
+
export declare function withTransportTimeoutBuffer(requestedTimeout: number | undefined, fallbackTimeout: number): number;
|
|
13
|
+
export declare function buildEntityFilter(options: {
|
|
14
|
+
type?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
nearest?: boolean;
|
|
17
|
+
id?: number;
|
|
18
|
+
maxDistance?: number;
|
|
19
|
+
}): {
|
|
20
|
+
type: string | undefined;
|
|
21
|
+
name: string | undefined;
|
|
22
|
+
nearest: boolean | undefined;
|
|
23
|
+
id: number | undefined;
|
|
24
|
+
maxDistance: number | undefined;
|
|
25
|
+
};
|
|
26
|
+
export declare function command(name: string, description: string): Command;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { ClientManager } from "../client/ClientManager.js";
|
|
3
|
+
import { WebSocketClient } from "../client/WebSocketClient.js";
|
|
4
|
+
import { MctError } from "../util/errors.js";
|
|
5
|
+
import { wrapCommand } from "../util/command.js";
|
|
6
|
+
export async function sendClientRequest(context, clientName, action, params, timeoutSeconds) {
|
|
7
|
+
const manager = new ClientManager(context);
|
|
8
|
+
const client = await manager.getClient(clientName);
|
|
9
|
+
const ws = new WebSocketClient(manager.getWsUrl(client.wsPort));
|
|
10
|
+
return ws.send(action, params, timeoutSeconds ?? context.config.timeout.default);
|
|
11
|
+
}
|
|
12
|
+
export function createRequestAction(action, buildParams, timeoutSelector) {
|
|
13
|
+
return wrapCommand(async (context, payload) => {
|
|
14
|
+
const timeout = timeoutSelector?.(payload, context);
|
|
15
|
+
return sendClientRequest(context, payload.globalOptions.client, action, buildParams(payload), timeout);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export function parseJson(text, fieldName) {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(text);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
throw new MctError({
|
|
24
|
+
code: "INVALID_PARAMS",
|
|
25
|
+
message: `${fieldName} must be valid JSON`
|
|
26
|
+
}, 4);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function parseNumberList(text) {
|
|
30
|
+
return text
|
|
31
|
+
.split(",")
|
|
32
|
+
.map((value) => value.trim())
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.map((value) => Number(value));
|
|
35
|
+
}
|
|
36
|
+
export function withTransportTimeoutBuffer(requestedTimeout, fallbackTimeout) {
|
|
37
|
+
const effectiveTimeout = requestedTimeout ?? fallbackTimeout;
|
|
38
|
+
return Math.max(effectiveTimeout + 10, fallbackTimeout);
|
|
39
|
+
}
|
|
40
|
+
export function buildEntityFilter(options) {
|
|
41
|
+
const filter = {
|
|
42
|
+
type: options.type,
|
|
43
|
+
name: options.name,
|
|
44
|
+
nearest: options.nearest,
|
|
45
|
+
id: options.id,
|
|
46
|
+
maxDistance: options.maxDistance
|
|
47
|
+
};
|
|
48
|
+
if (!filter.type && !filter.name && !filter.nearest && !filter.id && !filter.maxDistance) {
|
|
49
|
+
throw new MctError({
|
|
50
|
+
code: "INVALID_PARAMS",
|
|
51
|
+
message: "At least one entity filter option is required"
|
|
52
|
+
}, 4);
|
|
53
|
+
}
|
|
54
|
+
return filter;
|
|
55
|
+
}
|
|
56
|
+
export function command(name, description) {
|
|
57
|
+
return new Command(name).description(description);
|
|
58
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { createRequestAction } from "./request-helpers.js";
|
|
3
|
+
export function createResourcepackCommand() {
|
|
4
|
+
const command = new Command("resourcepack").description("Resource pack operations");
|
|
5
|
+
command.command("status").description("Get resource pack status").action(createRequestAction("resourcepack.status", () => ({})));
|
|
6
|
+
command.command("accept").description("Accept the pending resource pack").action(createRequestAction("resourcepack.accept", () => ({})));
|
|
7
|
+
command.command("reject").description("Reject the pending resource pack").action(createRequestAction("resourcepack.reject", () => ({})));
|
|
8
|
+
return command;
|
|
9
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { createRequestAction } from "./request-helpers.js";
|
|
3
|
+
export function createRotationCommand() {
|
|
4
|
+
const command = new Command("rotation").description("View direction query");
|
|
5
|
+
command.command("get").description("Get current view direction (yaw, pitch)").action(createRequestAction("rotation.get", () => ({})));
|
|
6
|
+
return command;
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { createRequestAction } from "./request-helpers.js";
|
|
3
|
+
export function createScreenCommand() {
|
|
4
|
+
const command = new Command("screen").description("Screen info");
|
|
5
|
+
command.command("size").description("Get screen dimensions (width, height)").action(createRequestAction("screen.size", () => ({})));
|
|
6
|
+
return command;
|
|
7
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { createRequestAction } from "./request-helpers.js";
|
|
3
|
+
export function createScreenshotCommand() {
|
|
4
|
+
return new Command("screenshot")
|
|
5
|
+
.description("Take a screenshot")
|
|
6
|
+
.requiredOption("--output <path>", "Output file path (e.g. ./screenshots/test.png)")
|
|
7
|
+
.option("--region <region>", "Capture a sub-region, format: x,y,w,h")
|
|
8
|
+
.option("--gui", "Capture the current GUI screen")
|
|
9
|
+
.action(createRequestAction("capture.screenshot", ({ options }) => ({
|
|
10
|
+
output: options.output,
|
|
11
|
+
region: options.region,
|
|
12
|
+
gui: Boolean(options.gui)
|
|
13
|
+
})));
|
|
14
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { buildServerSearchResults } from "../download/SearchCommand.js";
|
|
3
|
+
import { downloadServerJar } from "../download/server/ServerDownloader.js";
|
|
4
|
+
import { wrapCommand } from "../util/command.js";
|
|
5
|
+
import { ServerManager } from "../server/ServerManager.js";
|
|
6
|
+
export function createServerCommand() {
|
|
7
|
+
const command = new Command("server").description("Manage Minecraft server");
|
|
8
|
+
command
|
|
9
|
+
.command("search")
|
|
10
|
+
.description("Search available server versions")
|
|
11
|
+
.option("--type <type>", "Server type: vanilla|paper|purpur|spigot")
|
|
12
|
+
.option("--version <version>", "Minecraft version")
|
|
13
|
+
.action(wrapCommand(async (_context, { options }) => {
|
|
14
|
+
return {
|
|
15
|
+
results: buildServerSearchResults({
|
|
16
|
+
type: options.type,
|
|
17
|
+
version: options.version
|
|
18
|
+
})
|
|
19
|
+
};
|
|
20
|
+
}));
|
|
21
|
+
command
|
|
22
|
+
.command("download")
|
|
23
|
+
.description("Download server jar and update config")
|
|
24
|
+
.option("--type <type>", "Server type: vanilla|paper|purpur|spigot")
|
|
25
|
+
.option("--version <version>", "Minecraft version")
|
|
26
|
+
.option("--build <build>", "Specific build number")
|
|
27
|
+
.option("--dir <path>", "Download target directory")
|
|
28
|
+
.option("--fixtures <path>", "Fixture plugin jar, auto-copied to plugins/")
|
|
29
|
+
.action(wrapCommand(async (context, { options }) => {
|
|
30
|
+
return downloadServerJar(context, options);
|
|
31
|
+
}));
|
|
32
|
+
command
|
|
33
|
+
.command("start")
|
|
34
|
+
.description("Start the server")
|
|
35
|
+
.option("--jar <path>", "Server jar path (default: from config server.jar)")
|
|
36
|
+
.option("--dir <path>", "Server directory (default: from config server.dir)")
|
|
37
|
+
.option("--port <number>", "Server port (default: 25565)", Number)
|
|
38
|
+
.option("--eula", "Auto-accept EULA")
|
|
39
|
+
.action(wrapCommand(async (context, { options }) => {
|
|
40
|
+
const manager = new ServerManager(context);
|
|
41
|
+
return manager.start(options);
|
|
42
|
+
}));
|
|
43
|
+
command
|
|
44
|
+
.command("stop")
|
|
45
|
+
.description("Stop the server")
|
|
46
|
+
.action(wrapCommand(async (context) => {
|
|
47
|
+
const manager = new ServerManager(context);
|
|
48
|
+
return manager.stop();
|
|
49
|
+
}));
|
|
50
|
+
command
|
|
51
|
+
.command("status")
|
|
52
|
+
.description("Show server status")
|
|
53
|
+
.action(wrapCommand(async (context) => {
|
|
54
|
+
const manager = new ServerManager(context);
|
|
55
|
+
return manager.status();
|
|
56
|
+
}));
|
|
57
|
+
command
|
|
58
|
+
.command("wait-ready")
|
|
59
|
+
.description("Wait until server port is connectable")
|
|
60
|
+
.option("--timeout <seconds>", "Timeout in seconds", Number)
|
|
61
|
+
.action(wrapCommand(async (context, { options }) => {
|
|
62
|
+
const manager = new ServerManager(context);
|
|
63
|
+
return manager.waitReady(options.timeout ?? context.config.timeout.serverReady);
|
|
64
|
+
}));
|
|
65
|
+
return command;
|
|
66
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { createRequestAction } from "./request-helpers.js";
|
|
3
|
+
export function createSignCommand() {
|
|
4
|
+
const command = new Command("sign").description("Sign block operations (reads/writes directly, no GUI interaction needed)");
|
|
5
|
+
command
|
|
6
|
+
.command("read")
|
|
7
|
+
.description("Read sign text")
|
|
8
|
+
.argument("<x>", "X coordinate")
|
|
9
|
+
.argument("<y>", "Y coordinate")
|
|
10
|
+
.argument("<z>", "Z coordinate")
|
|
11
|
+
.action(createRequestAction("sign.read", ({ args }) => ({
|
|
12
|
+
x: Number(args[0]),
|
|
13
|
+
y: Number(args[1]),
|
|
14
|
+
z: Number(args[2])
|
|
15
|
+
})));
|
|
16
|
+
command
|
|
17
|
+
.command("edit")
|
|
18
|
+
.description("Edit sign text")
|
|
19
|
+
.argument("<x>", "X coordinate")
|
|
20
|
+
.argument("<y>", "Y coordinate")
|
|
21
|
+
.argument("<z>", "Z coordinate")
|
|
22
|
+
.requiredOption("--lines <lines...>", "Four lines of text, e.g. --lines \"Line 1\" \"Line 2\" \"Line 3\" \"Line 4\"")
|
|
23
|
+
.action(createRequestAction("sign.edit", ({ args, options }) => ({
|
|
24
|
+
x: Number(args[0]),
|
|
25
|
+
y: Number(args[1]),
|
|
26
|
+
z: Number(args[2]),
|
|
27
|
+
lines: options.lines
|
|
28
|
+
})));
|
|
29
|
+
return command;
|
|
30
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { createRequestAction } from "./request-helpers.js";
|
|
3
|
+
export function createStatusCommand() {
|
|
4
|
+
const command = new Command("status").description("Player status queries");
|
|
5
|
+
const statusLabels = {
|
|
6
|
+
health: "Get health and hunger",
|
|
7
|
+
effects: "Get active potion effects",
|
|
8
|
+
experience: "Get XP level and progress",
|
|
9
|
+
gamemode: "Get current game mode",
|
|
10
|
+
world: "Get current world info",
|
|
11
|
+
all: "Get all status at once"
|
|
12
|
+
};
|
|
13
|
+
for (const sub of ["health", "effects", "experience", "gamemode", "world", "all"]) {
|
|
14
|
+
command.command(sub).description(statusLabels[sub]).action(createRequestAction(`status.${sub}`, () => ({})));
|
|
15
|
+
}
|
|
16
|
+
return command;
|
|
17
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { createRequestAction, withTransportTimeoutBuffer } from "./request-helpers.js";
|
|
3
|
+
export function createWaitCommand() {
|
|
4
|
+
return new Command("wait")
|
|
5
|
+
.description("Wait and synchronization")
|
|
6
|
+
.argument("[seconds]", "Wait for a number of seconds")
|
|
7
|
+
.option("--ticks <ticks>", "Wait for a number of game ticks", Number)
|
|
8
|
+
.option("--until-health-above <value>", "Wait until health is above this value", Number)
|
|
9
|
+
.option("--until-gui-open", "Wait until a GUI opens")
|
|
10
|
+
.option("--until-on-ground", "Wait until the player is on the ground")
|
|
11
|
+
.option("--timeout <seconds>", "Timeout in seconds", Number)
|
|
12
|
+
.action(createRequestAction("wait.perform", ({ args, options }) => ({
|
|
13
|
+
seconds: args[0] ? Number(args[0]) : undefined,
|
|
14
|
+
ticks: options.ticks,
|
|
15
|
+
untilHealthAbove: options.untilHealthAbove,
|
|
16
|
+
untilGuiOpen: Boolean(options.untilGuiOpen),
|
|
17
|
+
untilOnGround: Boolean(options.untilOnGround),
|
|
18
|
+
timeout: options.timeout
|
|
19
|
+
}), ({ options, args }, context) => {
|
|
20
|
+
const requested = options.timeout ? Number(options.timeout) : args[0] ? Number(args[0]) : context.config.timeout.default;
|
|
21
|
+
return withTransportTimeoutBuffer(requested, context.config.timeout.default);
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ServerType } from "./VersionMatrix.js";
|
|
2
|
+
import type { LoaderType } from "./types.js";
|
|
3
|
+
export declare function resolveCacheRoot(): string;
|
|
4
|
+
export declare class CacheManager {
|
|
5
|
+
private readonly rootDir;
|
|
6
|
+
constructor(rootDir?: string);
|
|
7
|
+
getRootDir(): string;
|
|
8
|
+
getServerJarPath(type: ServerType, version: string, build: string): string;
|
|
9
|
+
getServerFile(type: ServerType, version: string, build: string): string;
|
|
10
|
+
getMinecraftDir(version: string): string;
|
|
11
|
+
getClientMetadataFile(scope: "version-manifest" | "minecraft-version" | "fabric-loader-profile", id: string): string;
|
|
12
|
+
getClientLibraryFile(relativePath: string): string;
|
|
13
|
+
getClientJarPath(version: string): string;
|
|
14
|
+
getClientAssetIndexPath(assetIndexId: string): string;
|
|
15
|
+
getClientAssetObjectPath(hash: string): string;
|
|
16
|
+
getClientAssetsRoot(): string;
|
|
17
|
+
getClientLoggingConfigPath(fileName: string): string;
|
|
18
|
+
getLoaderDir(loader: LoaderType): string;
|
|
19
|
+
getModFile(fileName: string): string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function resolveCacheRoot() {
|
|
4
|
+
return process.env.MCT_CACHE_DIR || path.join(os.homedir(), ".mct", "cache");
|
|
5
|
+
}
|
|
6
|
+
export class CacheManager {
|
|
7
|
+
rootDir;
|
|
8
|
+
constructor(rootDir = resolveCacheRoot()) {
|
|
9
|
+
this.rootDir = rootDir;
|
|
10
|
+
}
|
|
11
|
+
getRootDir() {
|
|
12
|
+
return this.rootDir;
|
|
13
|
+
}
|
|
14
|
+
getServerJarPath(type, version, build) {
|
|
15
|
+
return path.join(this.rootDir, "server", type, `${version}-${build}.jar`);
|
|
16
|
+
}
|
|
17
|
+
getServerFile(type, version, build) {
|
|
18
|
+
return this.getServerJarPath(type, version, build);
|
|
19
|
+
}
|
|
20
|
+
getMinecraftDir(version) {
|
|
21
|
+
return path.join(this.rootDir, "client", "minecraft", version);
|
|
22
|
+
}
|
|
23
|
+
getClientMetadataFile(scope, id) {
|
|
24
|
+
return path.join(this.rootDir, "client", "metadata", scope, `${id}.json`);
|
|
25
|
+
}
|
|
26
|
+
getClientLibraryFile(relativePath) {
|
|
27
|
+
return path.join(this.rootDir, "client", "libraries", relativePath);
|
|
28
|
+
}
|
|
29
|
+
getClientJarPath(version) {
|
|
30
|
+
return path.join(this.rootDir, "client", "jars", `minecraft-client-${version}.jar`);
|
|
31
|
+
}
|
|
32
|
+
getClientAssetIndexPath(assetIndexId) {
|
|
33
|
+
return path.join(this.rootDir, "client", "assets", "indexes", `${assetIndexId}.json`);
|
|
34
|
+
}
|
|
35
|
+
getClientAssetObjectPath(hash) {
|
|
36
|
+
return path.join(this.rootDir, "client", "assets", "objects", hash.slice(0, 2), hash);
|
|
37
|
+
}
|
|
38
|
+
getClientAssetsRoot() {
|
|
39
|
+
return path.join(this.rootDir, "client", "assets");
|
|
40
|
+
}
|
|
41
|
+
getClientLoggingConfigPath(fileName) {
|
|
42
|
+
return path.join(this.rootDir, "client", "logging", fileName);
|
|
43
|
+
}
|
|
44
|
+
getLoaderDir(loader) {
|
|
45
|
+
return path.join(this.rootDir, "client", loader);
|
|
46
|
+
}
|
|
47
|
+
getModFile(fileName) {
|
|
48
|
+
return path.join(this.rootDir, "mod", fileName);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { MctError } from "../util/errors.js";
|
|
4
|
+
export async function copyFileIfMissing(sourcePath, targetPath) {
|
|
5
|
+
const content = await readFile(sourcePath);
|
|
6
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
7
|
+
await writeFile(targetPath, content);
|
|
8
|
+
}
|
|
9
|
+
export async function downloadFile(url, targetPath, fetchImpl = fetch) {
|
|
10
|
+
const response = await fetchImpl(url);
|
|
11
|
+
if (!response.ok) {
|
|
12
|
+
throw new MctError({
|
|
13
|
+
code: "DOWNLOAD_FAILED",
|
|
14
|
+
message: `Failed to download ${url}`,
|
|
15
|
+
details: {
|
|
16
|
+
status: response.status
|
|
17
|
+
}
|
|
18
|
+
}, 2);
|
|
19
|
+
}
|
|
20
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
21
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
22
|
+
await writeFile(targetPath, Buffer.from(arrayBuffer));
|
|
23
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface JavaDetectionResult {
|
|
2
|
+
available: boolean;
|
|
3
|
+
command: string;
|
|
4
|
+
majorVersion?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function parseJavaMajorVersion(output: string): number | undefined;
|
|
7
|
+
export declare function detectJava(command?: string): Promise<JavaDetectionResult>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
export function parseJavaMajorVersion(output) {
|
|
5
|
+
const match = output.match(/version "(?<version>[^"]+)"/);
|
|
6
|
+
if (!match?.groups?.version) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const [first, second] = match.groups.version.split(".");
|
|
10
|
+
if (first === "1" && second) {
|
|
11
|
+
return Number.parseInt(second, 10);
|
|
12
|
+
}
|
|
13
|
+
return Number.parseInt(first, 10);
|
|
14
|
+
}
|
|
15
|
+
export async function detectJava(command = "java") {
|
|
16
|
+
try {
|
|
17
|
+
const result = await execFileAsync(command, ["-version"]);
|
|
18
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
19
|
+
return {
|
|
20
|
+
available: true,
|
|
21
|
+
command,
|
|
22
|
+
majorVersion: parseJavaMajorVersion(output)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return {
|
|
27
|
+
available: false,
|
|
28
|
+
command
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ModVariantCatalog, ModVariant } from "./types.js";
|
|
2
|
+
export declare function loadModVariantCatalog(filePath?: string): Promise<ModVariantCatalog>;
|
|
3
|
+
export declare function loadModVariantCatalogSync(filePath?: string): ModVariantCatalog;
|
|
4
|
+
export declare function sortVariants(variants: ModVariant[]): ModVariant[];
|
|
5
|
+
export declare function findVariant(catalog: ModVariantCatalog, variantId: string): ModVariant | undefined;
|
|
6
|
+
export declare function getDefaultVariant(catalog: ModVariantCatalog): ModVariant | undefined;
|
|
7
|
+
export declare function findVariantByVersionAndLoader(catalog: ModVariantCatalog, minecraftVersion: string, loader: ModVariant["loader"]): ModVariant | undefined;
|
|
8
|
+
export declare function getBuildableFabricVariants(catalog: ModVariantCatalog): ModVariant[];
|
|
9
|
+
export declare function getSupportedMinecraftVersions(catalog: ModVariantCatalog): string[];
|
|
10
|
+
export declare function getModArtifactFileName(variant: ModVariant): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const DEFAULT_VARIANTS_PATH = fileURLToPath(new URL("../../data/variants.json", import.meta.url));
|
|
5
|
+
function compareVersions(left, right) {
|
|
6
|
+
const leftParts = left.split(".").map((value) => Number.parseInt(value, 10));
|
|
7
|
+
const rightParts = right.split(".").map((value) => Number.parseInt(value, 10));
|
|
8
|
+
const length = Math.max(leftParts.length, rightParts.length);
|
|
9
|
+
for (let index = 0; index < length; index += 1) {
|
|
10
|
+
const delta = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
|
|
11
|
+
if (delta !== 0) {
|
|
12
|
+
return delta;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
export async function loadModVariantCatalog(filePath = DEFAULT_VARIANTS_PATH) {
|
|
18
|
+
const raw = await readFile(filePath, "utf8");
|
|
19
|
+
return JSON.parse(raw);
|
|
20
|
+
}
|
|
21
|
+
export function loadModVariantCatalogSync(filePath = DEFAULT_VARIANTS_PATH) {
|
|
22
|
+
const raw = readFileSync(filePath, "utf8");
|
|
23
|
+
return JSON.parse(raw);
|
|
24
|
+
}
|
|
25
|
+
export function sortVariants(variants) {
|
|
26
|
+
return [...variants].sort((left, right) => {
|
|
27
|
+
const versionDelta = compareVersions(right.minecraftVersion, left.minecraftVersion);
|
|
28
|
+
if (versionDelta !== 0) {
|
|
29
|
+
return versionDelta;
|
|
30
|
+
}
|
|
31
|
+
return left.loader.localeCompare(right.loader);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function findVariant(catalog, variantId) {
|
|
35
|
+
return catalog.variants.find((variant) => variant.id === variantId);
|
|
36
|
+
}
|
|
37
|
+
export function getDefaultVariant(catalog) {
|
|
38
|
+
return findVariant(catalog, catalog.defaultVariant);
|
|
39
|
+
}
|
|
40
|
+
export function findVariantByVersionAndLoader(catalog, minecraftVersion, loader) {
|
|
41
|
+
return catalog.variants.find((variant) => variant.minecraftVersion === minecraftVersion && variant.loader === loader);
|
|
42
|
+
}
|
|
43
|
+
export function getBuildableFabricVariants(catalog) {
|
|
44
|
+
return sortVariants(catalog.variants.filter((variant) => variant.loader === "fabric" && variant.yarnMappings && variant.fabricLoaderVersion
|
|
45
|
+
&& (variant.support === "ready" || variant.support === "configured")));
|
|
46
|
+
}
|
|
47
|
+
export function getSupportedMinecraftVersions(catalog) {
|
|
48
|
+
return [...new Set(catalog.variants.map((variant) => variant.minecraftVersion))].sort(compareVersions).reverse();
|
|
49
|
+
}
|
|
50
|
+
export function getModArtifactFileName(variant) {
|
|
51
|
+
return `mct-client-mod-${variant.loader}-${variant.minecraftVersion}.jar`;
|
|
52
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ClientLoader, type ServerType } from "./VersionMatrix.js";
|
|
2
|
+
export interface ServerSearchCommandResult {
|
|
3
|
+
type: ServerType;
|
|
4
|
+
versions: Array<{
|
|
5
|
+
version: string;
|
|
6
|
+
build?: string;
|
|
7
|
+
requiresBuildTools?: boolean;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export interface ClientSearchCommandResult {
|
|
11
|
+
version: string;
|
|
12
|
+
javaVersion: string;
|
|
13
|
+
loaders: Array<{
|
|
14
|
+
loader: ClientLoader;
|
|
15
|
+
supported: boolean;
|
|
16
|
+
loaderVersion?: string;
|
|
17
|
+
modVersion?: string;
|
|
18
|
+
validation?: "verified" | "limited" | "planned";
|
|
19
|
+
notes?: string;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export declare function buildServerSearchResults(filter?: {
|
|
23
|
+
type?: ServerType;
|
|
24
|
+
version?: string;
|
|
25
|
+
}): ServerSearchCommandResult[];
|
|
26
|
+
export declare function buildClientSearchResults(filter?: {
|
|
27
|
+
loader?: ClientLoader;
|
|
28
|
+
version?: string;
|
|
29
|
+
}): ClientSearchCommandResult[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { searchClientVersions, searchServerVersions } from "./VersionMatrix.js";
|
|
2
|
+
export function buildServerSearchResults(filter) {
|
|
3
|
+
const grouped = new Map();
|
|
4
|
+
for (const entry of searchServerVersions(filter).filter((item) => item.supported)) {
|
|
5
|
+
const current = grouped.get(entry.type) ?? {
|
|
6
|
+
type: entry.type,
|
|
7
|
+
versions: []
|
|
8
|
+
};
|
|
9
|
+
current.versions.push({
|
|
10
|
+
version: entry.minecraftVersion,
|
|
11
|
+
...(entry.latestBuild != null ? { build: String(entry.latestBuild) } : {}),
|
|
12
|
+
...(entry.requiresBuildTools ? { requiresBuildTools: true } : {})
|
|
13
|
+
});
|
|
14
|
+
grouped.set(entry.type, current);
|
|
15
|
+
}
|
|
16
|
+
return [...grouped.values()];
|
|
17
|
+
}
|
|
18
|
+
export function buildClientSearchResults(filter) {
|
|
19
|
+
const grouped = new Map();
|
|
20
|
+
for (const entry of searchClientVersions(filter)) {
|
|
21
|
+
const current = grouped.get(entry.minecraftVersion) ?? {
|
|
22
|
+
version: entry.minecraftVersion,
|
|
23
|
+
javaVersion: entry.javaVersion,
|
|
24
|
+
loaders: []
|
|
25
|
+
};
|
|
26
|
+
current.loaders.push({
|
|
27
|
+
loader: entry.loader,
|
|
28
|
+
supported: entry.supported,
|
|
29
|
+
...(entry.loaderVersion ? { loaderVersion: entry.loaderVersion } : {}),
|
|
30
|
+
...(entry.modVersion ? { modVersion: entry.modVersion } : {}),
|
|
31
|
+
...(entry.validation ? { validation: entry.validation } : {}),
|
|
32
|
+
...(entry.notes ? { notes: entry.notes } : {})
|
|
33
|
+
});
|
|
34
|
+
grouped.set(entry.minecraftVersion, current);
|
|
35
|
+
}
|
|
36
|
+
return [...grouped.values()];
|
|
37
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export type ServerType = "paper" | "purpur" | "spigot" | "vanilla";
|
|
2
|
+
export type ClientLoader = "fabric" | "forge" | "neoforge";
|
|
3
|
+
export interface ServerSupportInfo {
|
|
4
|
+
supported: boolean;
|
|
5
|
+
latestBuild?: number;
|
|
6
|
+
requiresBuildTools?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface ClientLoaderSupportInfo {
|
|
9
|
+
supported: boolean;
|
|
10
|
+
loaderVersion?: string;
|
|
11
|
+
modVersion?: string;
|
|
12
|
+
validation?: "verified" | "limited" | "planned";
|
|
13
|
+
notes?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface MinecraftSupportEntry {
|
|
16
|
+
minecraftVersion: string;
|
|
17
|
+
javaVersion: string;
|
|
18
|
+
servers: Record<ServerType, ServerSupportInfo>;
|
|
19
|
+
clients: Record<ClientLoader, ClientLoaderSupportInfo>;
|
|
20
|
+
}
|
|
21
|
+
export interface ServerSearchResult {
|
|
22
|
+
type: ServerType;
|
|
23
|
+
minecraftVersion: string;
|
|
24
|
+
supported: boolean;
|
|
25
|
+
latestBuild?: number;
|
|
26
|
+
requiresBuildTools?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface ServerCatalogEntry {
|
|
29
|
+
version: string;
|
|
30
|
+
build?: string;
|
|
31
|
+
requiresBuildTools?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface ClientSearchResult {
|
|
34
|
+
loader: ClientLoader;
|
|
35
|
+
minecraftVersion: string;
|
|
36
|
+
supported: boolean;
|
|
37
|
+
loaderVersion?: string;
|
|
38
|
+
modVersion?: string;
|
|
39
|
+
validation?: "verified" | "limited" | "planned";
|
|
40
|
+
notes?: string;
|
|
41
|
+
javaVersion: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function getVersionMatrix(): {
|
|
44
|
+
minecraftVersion: string;
|
|
45
|
+
javaVersion: string;
|
|
46
|
+
servers: {
|
|
47
|
+
paper: ServerSupportInfo;
|
|
48
|
+
purpur: ServerSupportInfo;
|
|
49
|
+
spigot: ServerSupportInfo;
|
|
50
|
+
vanilla: ServerSupportInfo;
|
|
51
|
+
};
|
|
52
|
+
clients: {
|
|
53
|
+
fabric: ClientLoaderSupportInfo;
|
|
54
|
+
forge: ClientLoaderSupportInfo;
|
|
55
|
+
neoforge: ClientLoaderSupportInfo;
|
|
56
|
+
};
|
|
57
|
+
}[];
|
|
58
|
+
export declare function getSupportedMinecraftVersions(): string[];
|
|
59
|
+
export declare function getMinecraftSupport(version: string): MinecraftSupportEntry | undefined;
|
|
60
|
+
export declare function searchServerVersions(filter?: {
|
|
61
|
+
type?: ServerType;
|
|
62
|
+
version?: string;
|
|
63
|
+
}): ServerSearchResult[];
|
|
64
|
+
export declare function searchClientVersions(filter?: {
|
|
65
|
+
loader?: ClientLoader;
|
|
66
|
+
version?: string;
|
|
67
|
+
}): ClientSearchResult[];
|
|
68
|
+
export declare function getServerVersionMatrix(): ServerSearchResult[];
|
|
69
|
+
export declare function getClientVersionMatrix(): ClientSearchResult[];
|
|
70
|
+
export declare function getServerVersionCatalog(): Record<ServerType, ServerCatalogEntry[]>;
|
|
71
|
+
export declare function getServerTypes(): ServerType[];
|
|
72
|
+
export declare function getClientLoaders(): ClientLoader[];
|