@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.
Files changed (99) hide show
  1. package/bin/mct +2 -0
  2. package/data/variants.json +139 -0
  3. package/dist/client/ClientManager.d.ts +82 -0
  4. package/dist/client/ClientManager.js +213 -0
  5. package/dist/client/WebSocketClient.d.ts +15 -0
  6. package/dist/client/WebSocketClient.js +76 -0
  7. package/dist/commands/block.d.ts +2 -0
  8. package/dist/commands/block.js +52 -0
  9. package/dist/commands/book.d.ts +2 -0
  10. package/dist/commands/book.js +21 -0
  11. package/dist/commands/channel.d.ts +2 -0
  12. package/dist/commands/channel.js +24 -0
  13. package/dist/commands/chat.d.ts +2 -0
  14. package/dist/commands/chat.js +31 -0
  15. package/dist/commands/client.d.ts +2 -0
  16. package/dist/commands/client.js +87 -0
  17. package/dist/commands/combat.d.ts +2 -0
  18. package/dist/commands/combat.js +46 -0
  19. package/dist/commands/craft.d.ts +5 -0
  20. package/dist/commands/craft.js +45 -0
  21. package/dist/commands/effects.d.ts +2 -0
  22. package/dist/commands/effects.js +16 -0
  23. package/dist/commands/entity.d.ts +2 -0
  24. package/dist/commands/entity.js +53 -0
  25. package/dist/commands/gui.d.ts +2 -0
  26. package/dist/commands/gui.js +49 -0
  27. package/dist/commands/hud.d.ts +2 -0
  28. package/dist/commands/hud.js +16 -0
  29. package/dist/commands/input.d.ts +2 -0
  30. package/dist/commands/input.js +124 -0
  31. package/dist/commands/inventory.d.ts +2 -0
  32. package/dist/commands/inventory.js +28 -0
  33. package/dist/commands/look.d.ts +2 -0
  34. package/dist/commands/look.js +37 -0
  35. package/dist/commands/move.d.ts +2 -0
  36. package/dist/commands/move.js +50 -0
  37. package/dist/commands/position.d.ts +2 -0
  38. package/dist/commands/position.js +7 -0
  39. package/dist/commands/request-helpers.d.ts +26 -0
  40. package/dist/commands/request-helpers.js +58 -0
  41. package/dist/commands/resourcepack.d.ts +2 -0
  42. package/dist/commands/resourcepack.js +9 -0
  43. package/dist/commands/rotation.d.ts +2 -0
  44. package/dist/commands/rotation.js +7 -0
  45. package/dist/commands/screen.d.ts +2 -0
  46. package/dist/commands/screen.js +7 -0
  47. package/dist/commands/screenshot.d.ts +2 -0
  48. package/dist/commands/screenshot.js +14 -0
  49. package/dist/commands/server.d.ts +2 -0
  50. package/dist/commands/server.js +66 -0
  51. package/dist/commands/sign.d.ts +2 -0
  52. package/dist/commands/sign.js +30 -0
  53. package/dist/commands/status.d.ts +2 -0
  54. package/dist/commands/status.js +17 -0
  55. package/dist/commands/wait.d.ts +2 -0
  56. package/dist/commands/wait.js +23 -0
  57. package/dist/download/CacheManager.d.ts +20 -0
  58. package/dist/download/CacheManager.js +50 -0
  59. package/dist/download/DownloadUtils.d.ts +2 -0
  60. package/dist/download/DownloadUtils.js +23 -0
  61. package/dist/download/JavaDetector.d.ts +7 -0
  62. package/dist/download/JavaDetector.js +31 -0
  63. package/dist/download/ModVariantCatalog.d.ts +10 -0
  64. package/dist/download/ModVariantCatalog.js +52 -0
  65. package/dist/download/SearchCommand.d.ts +29 -0
  66. package/dist/download/SearchCommand.js +37 -0
  67. package/dist/download/VersionMatrix.d.ts +72 -0
  68. package/dist/download/VersionMatrix.js +227 -0
  69. package/dist/download/client/Arm64LwjglPatcher.d.ts +5 -0
  70. package/dist/download/client/Arm64LwjglPatcher.js +153 -0
  71. package/dist/download/client/ClientDownloader.d.ts +42 -0
  72. package/dist/download/client/ClientDownloader.js +233 -0
  73. package/dist/download/client/FabricRuntimeDownloader.d.ts +10 -0
  74. package/dist/download/client/FabricRuntimeDownloader.js +91 -0
  75. package/dist/download/server/ServerDownloader.d.ts +51 -0
  76. package/dist/download/server/ServerDownloader.js +196 -0
  77. package/dist/download/types.d.ts +37 -0
  78. package/dist/download/types.js +1 -0
  79. package/dist/index.d.ts +2 -0
  80. package/dist/index.js +89 -0
  81. package/dist/server/ServerManager.d.ts +63 -0
  82. package/dist/server/ServerManager.js +114 -0
  83. package/dist/util/command.d.ts +10 -0
  84. package/dist/util/command.js +35 -0
  85. package/dist/util/config.d.ts +30 -0
  86. package/dist/util/config.js +59 -0
  87. package/dist/util/context.d.ts +17 -0
  88. package/dist/util/context.js +16 -0
  89. package/dist/util/errors.d.ts +20 -0
  90. package/dist/util/errors.js +37 -0
  91. package/dist/util/net.d.ts +5 -0
  92. package/dist/util/net.js +35 -0
  93. package/dist/util/output.d.ts +4 -0
  94. package/dist/util/output.js +23 -0
  95. package/dist/util/process.d.ts +3 -0
  96. package/dist/util/process.js +32 -0
  97. package/dist/util/state.d.ts +10 -0
  98. package/dist/util/state.js +40 -0
  99. package/package.json +54 -0
@@ -0,0 +1,227 @@
1
+ import { loadModVariantCatalogSync } from "./ModVariantCatalog.js";
2
+ const VERSION_MATRIX = [
3
+ {
4
+ minecraftVersion: "1.21.4",
5
+ javaVersion: "21+",
6
+ servers: {
7
+ vanilla: { supported: true },
8
+ paper: { supported: true, latestBuild: 170 },
9
+ purpur: { supported: true, latestBuild: 2406 },
10
+ spigot: { supported: true, requiresBuildTools: true }
11
+ },
12
+ clients: {
13
+ fabric: { supported: true, loaderVersion: "0.16.10", modVersion: "0.1.0", validation: "verified" },
14
+ forge: { supported: false, notes: "不支持此版本" },
15
+ neoforge: { supported: true, loaderVersion: "21.4.x", modVersion: "0.1.0" }
16
+ }
17
+ },
18
+ {
19
+ minecraftVersion: "1.21.1",
20
+ javaVersion: "21+",
21
+ servers: {
22
+ vanilla: { supported: true },
23
+ paper: { supported: true, latestBuild: 119 },
24
+ purpur: { supported: true, latestBuild: 2324 },
25
+ spigot: { supported: true, requiresBuildTools: true }
26
+ },
27
+ clients: {
28
+ fabric: { supported: true, loaderVersion: "0.16.10", modVersion: "0.1.0", validation: "verified" },
29
+ forge: { supported: false, notes: "不支持此版本" },
30
+ neoforge: { supported: true, loaderVersion: "21.1.x", modVersion: "0.1.0" }
31
+ }
32
+ },
33
+ {
34
+ minecraftVersion: "1.20.4",
35
+ javaVersion: "17+",
36
+ servers: {
37
+ vanilla: { supported: true },
38
+ paper: { supported: true, latestBuild: 496 },
39
+ purpur: { supported: true, latestBuild: 2176 },
40
+ spigot: { supported: true, requiresBuildTools: true }
41
+ },
42
+ clients: {
43
+ fabric: { supported: true, loaderVersion: "0.16.10", modVersion: "0.1.0" },
44
+ forge: { supported: true, loaderVersion: "49.0.49", modVersion: "0.1.0" },
45
+ neoforge: { supported: false, notes: "不支持此版本" }
46
+ }
47
+ },
48
+ {
49
+ minecraftVersion: "1.20.3",
50
+ javaVersion: "17+",
51
+ servers: {
52
+ vanilla: { supported: true },
53
+ paper: { supported: false },
54
+ purpur: { supported: false },
55
+ spigot: { supported: true, requiresBuildTools: true }
56
+ },
57
+ clients: {
58
+ fabric: { supported: true, loaderVersion: "0.16.10", modVersion: "0.1.0", validation: "verified" },
59
+ forge: { supported: false, notes: "当前未接入此 loader" },
60
+ neoforge: { supported: false, notes: "不支持此版本" }
61
+ }
62
+ },
63
+ {
64
+ minecraftVersion: "1.20.2",
65
+ javaVersion: "17+",
66
+ servers: {
67
+ vanilla: { supported: true },
68
+ paper: { supported: true, latestBuild: 318 },
69
+ purpur: { supported: true, latestBuild: 2095 },
70
+ spigot: { supported: true, requiresBuildTools: true }
71
+ },
72
+ clients: {
73
+ fabric: { supported: true, loaderVersion: "0.16.10", modVersion: "0.1.0" },
74
+ forge: { supported: false, notes: "当前未接入此 loader" },
75
+ neoforge: { supported: false, notes: "不支持此版本" }
76
+ }
77
+ },
78
+ {
79
+ minecraftVersion: "1.20.1",
80
+ javaVersion: "17+",
81
+ servers: {
82
+ vanilla: { supported: true },
83
+ paper: { supported: true, latestBuild: 196 },
84
+ purpur: { supported: true, latestBuild: 2062 },
85
+ spigot: { supported: true, requiresBuildTools: true }
86
+ },
87
+ clients: {
88
+ fabric: { supported: true, loaderVersion: "0.16.10", modVersion: "0.1.0" },
89
+ forge: { supported: true, loaderVersion: "47.x", modVersion: "0.1.0" },
90
+ neoforge: { supported: false, notes: "不支持此版本" }
91
+ }
92
+ },
93
+ {
94
+ minecraftVersion: "1.18.2",
95
+ javaVersion: "17+",
96
+ servers: {
97
+ vanilla: { supported: true },
98
+ paper: { supported: true, latestBuild: 388 },
99
+ purpur: { supported: false },
100
+ spigot: { supported: true, requiresBuildTools: true }
101
+ },
102
+ clients: {
103
+ fabric: { supported: true, loaderVersion: "0.16.10", modVersion: "0.1.0", validation: "verified" },
104
+ forge: { supported: true, loaderVersion: "40.x", modVersion: "0.1.0" },
105
+ neoforge: { supported: false, notes: "不支持此版本" }
106
+ }
107
+ },
108
+ {
109
+ minecraftVersion: "1.16.5",
110
+ javaVersion: "8+",
111
+ servers: {
112
+ vanilla: { supported: true },
113
+ paper: { supported: true, latestBuild: 794 },
114
+ purpur: { supported: false },
115
+ spigot: { supported: true, requiresBuildTools: true }
116
+ },
117
+ clients: {
118
+ fabric: { supported: false, notes: "当前未接入此版本 mod" },
119
+ forge: { supported: true, loaderVersion: "36.x", modVersion: "0.1.0" },
120
+ neoforge: { supported: false, notes: "不支持此版本" }
121
+ }
122
+ },
123
+ {
124
+ minecraftVersion: "1.12.2",
125
+ javaVersion: "8+",
126
+ servers: {
127
+ vanilla: { supported: true },
128
+ paper: { supported: true, latestBuild: 1620 },
129
+ purpur: { supported: false },
130
+ spigot: { supported: true, requiresBuildTools: true }
131
+ },
132
+ clients: {
133
+ fabric: { supported: false, notes: "不支持此版本" },
134
+ forge: { supported: true, loaderVersion: "14.23.x", modVersion: "0.1.0" },
135
+ neoforge: { supported: false, notes: "不支持此版本" }
136
+ }
137
+ }
138
+ ];
139
+ function overlayClientSupport(entry, loader) {
140
+ const catalog = loadModVariantCatalogSync();
141
+ const variant = catalog.variants.find((candidate) => candidate.minecraftVersion === entry.minecraftVersion && candidate.loader === loader);
142
+ if (!variant) {
143
+ return { ...entry.clients[loader] };
144
+ }
145
+ return {
146
+ supported: variant.support === "ready" || variant.support === "configured",
147
+ loaderVersion: variant.fabricLoaderVersion ?? variant.forgeVersion ?? variant.neoforgeVersion,
148
+ modVersion: variant.modVersion,
149
+ validation: variant.validation,
150
+ notes: variant.notes
151
+ };
152
+ }
153
+ export function getVersionMatrix() {
154
+ return VERSION_MATRIX.map((entry) => ({
155
+ minecraftVersion: entry.minecraftVersion,
156
+ javaVersion: entry.javaVersion,
157
+ servers: { ...entry.servers },
158
+ clients: {
159
+ fabric: overlayClientSupport(entry, "fabric"),
160
+ forge: overlayClientSupport(entry, "forge"),
161
+ neoforge: overlayClientSupport(entry, "neoforge")
162
+ }
163
+ }));
164
+ }
165
+ export function getSupportedMinecraftVersions() {
166
+ return VERSION_MATRIX.map((entry) => entry.minecraftVersion);
167
+ }
168
+ export function getMinecraftSupport(version) {
169
+ return VERSION_MATRIX.find((entry) => entry.minecraftVersion === version);
170
+ }
171
+ export function searchServerVersions(filter) {
172
+ const types = filter?.type ? [filter.type] : getServerTypes();
173
+ const entries = filter?.version ? VERSION_MATRIX.filter((entry) => entry.minecraftVersion === filter.version) : VERSION_MATRIX;
174
+ return types.flatMap((type) => entries.map((entry) => ({
175
+ type,
176
+ minecraftVersion: entry.minecraftVersion,
177
+ supported: entry.servers[type].supported,
178
+ latestBuild: entry.servers[type].latestBuild,
179
+ requiresBuildTools: entry.servers[type].requiresBuildTools
180
+ })));
181
+ }
182
+ export function searchClientVersions(filter) {
183
+ const loaders = filter?.loader ? [filter.loader] : getClientLoaders();
184
+ const entries = filter?.version ? VERSION_MATRIX.filter((entry) => entry.minecraftVersion === filter.version) : VERSION_MATRIX;
185
+ return loaders.flatMap((loader) => entries.map((entry) => {
186
+ const support = overlayClientSupport(entry, loader);
187
+ return {
188
+ loader,
189
+ minecraftVersion: entry.minecraftVersion,
190
+ supported: support.supported,
191
+ ...(support.loaderVersion ? { loaderVersion: support.loaderVersion } : {}),
192
+ ...(support.modVersion ? { modVersion: support.modVersion } : {}),
193
+ ...(support.validation ? { validation: support.validation } : {}),
194
+ ...(support.notes ? { notes: support.notes } : {}),
195
+ javaVersion: entry.javaVersion
196
+ };
197
+ }));
198
+ }
199
+ export function getServerVersionMatrix() {
200
+ return searchServerVersions();
201
+ }
202
+ export function getClientVersionMatrix() {
203
+ return searchClientVersions();
204
+ }
205
+ export function getServerVersionCatalog() {
206
+ return getServerTypes().reduce((catalog, type) => {
207
+ catalog[type] = VERSION_MATRIX
208
+ .filter((entry) => entry.servers[type].supported)
209
+ .map((entry) => ({
210
+ version: entry.minecraftVersion,
211
+ build: entry.servers[type].latestBuild != null ? String(entry.servers[type].latestBuild) : undefined,
212
+ requiresBuildTools: entry.servers[type].requiresBuildTools
213
+ }));
214
+ return catalog;
215
+ }, {
216
+ vanilla: [],
217
+ paper: [],
218
+ purpur: [],
219
+ spigot: []
220
+ });
221
+ }
222
+ export function getServerTypes() {
223
+ return ["vanilla", "paper", "purpur", "spigot"];
224
+ }
225
+ export function getClientLoaders() {
226
+ return ["fabric", "forge", "neoforge"];
227
+ }
@@ -0,0 +1,5 @@
1
+ export interface Arm64PatchDependencies {
2
+ fetchImpl?: typeof fetch;
3
+ }
4
+ export declare function needsArm64Patch(): boolean;
5
+ export declare function applyArm64LwjglPatch(runtimeRoot: string, versionId: string, dependencies?: Arm64PatchDependencies): Promise<string | null>;
@@ -0,0 +1,153 @@
1
+ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { MinecraftFolder } from "@xmcl/core";
5
+ const LWJGL_ARM64_PATCH = {
6
+ patchVersion: "3.3.1",
7
+ jars: {
8
+ "org.lwjgl:lwjgl": { path: "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar", url: "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar", sha1: "ae58664f88e18a9bb2c77b063833ca7aaec484cb", size: 0 },
9
+ "org.lwjgl:lwjgl-jemalloc": { path: "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar", url: "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar", sha1: "a817bcf213db49f710603677457567c37d53e103", size: 0 },
10
+ "org.lwjgl:lwjgl-openal": { path: "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar", url: "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar", sha1: "2623a6b8ae1dfcd880738656a9f0243d2e6840bd", size: 0 },
11
+ "org.lwjgl:lwjgl-opengl": { path: "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar", url: "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar", sha1: "831a5533a21a5f4f81bbc51bb13e9899319b5411", size: 0 },
12
+ "org.lwjgl:lwjgl-stb": { path: "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar", url: "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar", sha1: "b119297cf8ed01f247abe8685857f8e7fcf5980f", size: 0 },
13
+ "org.lwjgl:lwjgl-tinyfd": { path: "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar", url: "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar", sha1: "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347", size: 0 },
14
+ "org.lwjgl:lwjgl-glfw": { path: "org/glavo/hmcl/mmachina/lwjgl-glfw/3.3.1-mmachina.1/lwjgl-glfw-3.3.1-mmachina.1.jar", url: "https://repo1.maven.org/maven2/org/glavo/hmcl/mmachina/lwjgl-glfw/3.3.1-mmachina.1/lwjgl-glfw-3.3.1-mmachina.1.jar", sha1: "e9a101bca4fa30d26b21b526ff28e7c2d8927f1b", size: 0 }
15
+ },
16
+ natives: {
17
+ "org.lwjgl:lwjgl": { path: "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos-arm64.jar", url: "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos-arm64.jar", sha1: "71d0d5e469c9c95351eb949064497e3391616ac9", size: 0 },
18
+ "org.lwjgl:lwjgl-jemalloc": { path: "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos-arm64.jar", url: "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos-arm64.jar", sha1: "e577b87d8ad2ade361aaea2fcf226c660b15dee8", size: 0 },
19
+ "org.lwjgl:lwjgl-openal": { path: "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos-arm64.jar", url: "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos-arm64.jar", sha1: "23d55e7490b57495320f6c9e1936d78fd72c4ef8", size: 0 },
20
+ "org.lwjgl:lwjgl-opengl": { path: "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos-arm64.jar", url: "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos-arm64.jar", sha1: "eafe34b871d966292e8db0f1f3d6b8b110d4e91d", size: 0 },
21
+ "org.lwjgl:lwjgl-stb": { path: "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos-arm64.jar", url: "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos-arm64.jar", sha1: "fcf073ed911752abdca5f0b00a53cfdf17ff8e8b", size: 0 },
22
+ "org.lwjgl:lwjgl-tinyfd": { path: "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos-arm64.jar", url: "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos-arm64.jar", sha1: "972ecc17bad3571e81162153077b4d47b7b9eaa9", size: 0 },
23
+ "org.lwjgl:lwjgl-glfw": { path: "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos-arm64.jar", url: "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos-arm64.jar", sha1: "cac0d3f712a3da7641fa174735a5f315de7ffe0a", size: 0 }
24
+ }
25
+ };
26
+ export function needsArm64Patch() {
27
+ return os.arch() === "arm64" && os.platform() === "darwin";
28
+ }
29
+ async function downloadJar(entry, minecraft, fetchImpl) {
30
+ const localPath = minecraft.getLibraryByPath(entry.path);
31
+ try {
32
+ await access(localPath);
33
+ return localPath;
34
+ }
35
+ catch {
36
+ console.error(`[MCT] Downloading ${path.basename(entry.path)}...`);
37
+ const response = await fetchImpl(entry.url);
38
+ if (!response.ok) {
39
+ throw new Error(`HTTP ${response.status}: ${entry.url}`);
40
+ }
41
+ await mkdir(path.dirname(localPath), { recursive: true });
42
+ await writeFile(localPath, Buffer.from(await response.arrayBuffer()));
43
+ return localPath;
44
+ }
45
+ }
46
+ export async function applyArm64LwjglPatch(runtimeRoot, versionId, dependencies = {}) {
47
+ if (!needsArm64Patch()) {
48
+ return null;
49
+ }
50
+ const fetchImpl = dependencies.fetchImpl ?? fetch;
51
+ const minecraft = MinecraftFolder.from(runtimeRoot);
52
+ const nativesDir = minecraft.getNativesRoot(versionId);
53
+ // Walk version JSON chain to detect old LWJGL 3.2.x
54
+ let targetJsonPath = minecraft.getVersionJson(versionId);
55
+ let needsJsonPatch = false;
56
+ for (let depth = 0; depth < 5; depth++) {
57
+ const raw = await readFile(targetJsonPath, "utf-8").catch(() => null);
58
+ if (!raw) {
59
+ break;
60
+ }
61
+ const json = JSON.parse(raw);
62
+ if ((json.libraries || []).some((lib) => lib.name?.startsWith("org.lwjgl:lwjgl:3.2"))) {
63
+ needsJsonPatch = true;
64
+ break;
65
+ }
66
+ if ((json.libraries || []).some((lib) => lib.name?.startsWith("org.lwjgl:lwjgl:3.3"))) {
67
+ break;
68
+ }
69
+ if (!json.inheritsFrom) {
70
+ break;
71
+ }
72
+ targetJsonPath = minecraft.getVersionJson(json.inheritsFrom);
73
+ }
74
+ if (!needsJsonPatch) {
75
+ return null;
76
+ }
77
+ console.error("[MCT] Applying LWJGL 3.3.1 arm64 patch (based on HMCL NativePatcher)...");
78
+ const { open, walkEntriesGenerator, openEntryReadStream } = await import("@xmcl/unzip");
79
+ const { createWriteStream } = await import("node:fs");
80
+ const { pipeline } = await import("node:stream");
81
+ const { promisify } = await import("node:util");
82
+ const pipelineAsync = promisify(pipeline);
83
+ // 1. Download and extract arm64 natives
84
+ await mkdir(nativesDir, { recursive: true });
85
+ for (const [libKey, entry] of Object.entries(LWJGL_ARM64_PATCH.natives)) {
86
+ const localJar = await downloadJar(entry, minecraft, fetchImpl).catch((e) => { console.error(`[MCT] Skip ${libKey}: ${e.message}`); return null; });
87
+ if (!localJar) {
88
+ continue;
89
+ }
90
+ const zip = await open(localJar, { lazyEntries: true, autoClose: false });
91
+ for await (const zipEntry of walkEntriesGenerator(zip)) {
92
+ const name = zipEntry.fileName;
93
+ if (!name.endsWith(".dylib") || name.endsWith("/")) {
94
+ continue;
95
+ }
96
+ const dest = path.join(nativesDir, path.basename(name));
97
+ await pipelineAsync(await openEntryReadStream(zip, zipEntry), createWriteStream(dest));
98
+ }
99
+ }
100
+ // 2. Download Java JARs
101
+ for (const [libKey, entry] of Object.entries(LWJGL_ARM64_PATCH.jars)) {
102
+ await downloadJar(entry, minecraft, fetchImpl).catch((e) => console.error(`[MCT] Skip ${libKey}: ${e.message}`));
103
+ }
104
+ // 3. Patch vanilla version JSON to swap LWJGL 3.2.x -> 3.3.1
105
+ let curJsonPath = minecraft.getVersionJson(versionId);
106
+ for (let depth = 0; depth < 5; depth++) {
107
+ const raw = await readFile(curJsonPath, "utf-8").catch(() => null);
108
+ if (!raw) {
109
+ break;
110
+ }
111
+ const json = JSON.parse(raw);
112
+ const hasOldLwjgl = (json.libraries || []).some((lib) => lib.name?.startsWith("org.lwjgl:lwjgl:3.2"));
113
+ if (hasOldLwjgl) {
114
+ const newLibraries = [];
115
+ for (const lib of json.libraries) {
116
+ const name = lib.name || "";
117
+ const parts = name.split(":");
118
+ const baseKey = parts.slice(0, 2).join(":");
119
+ if (!(baseKey in LWJGL_ARM64_PATCH.jars) && !(baseKey in LWJGL_ARM64_PATCH.natives)) {
120
+ newLibraries.push(lib);
121
+ continue;
122
+ }
123
+ const newLib = { ...lib };
124
+ if ((baseKey in LWJGL_ARM64_PATCH.jars) && lib.downloads?.artifact) {
125
+ const jarEntry = LWJGL_ARM64_PATCH.jars[baseKey];
126
+ const newName = baseKey === "org.lwjgl:lwjgl-glfw"
127
+ ? "org.glavo.hmcl.mmachina:lwjgl-glfw:3.3.1-mmachina.1"
128
+ : `${baseKey}:3.3.1`;
129
+ newLib.name = newName;
130
+ newLib.downloads = { ...lib.downloads, artifact: { path: jarEntry.path, sha1: jarEntry.sha1, size: jarEntry.size, url: jarEntry.url } };
131
+ }
132
+ if ((baseKey in LWJGL_ARM64_PATCH.natives) && lib.downloads?.classifiers?.["natives-macos"]) {
133
+ const nativeEntry = LWJGL_ARM64_PATCH.natives[baseKey];
134
+ newLib.downloads = {
135
+ ...newLib.downloads,
136
+ classifiers: { "natives-macos": { path: nativeEntry.path, sha1: nativeEntry.sha1, size: nativeEntry.size, url: nativeEntry.url } }
137
+ };
138
+ }
139
+ newLibraries.push(newLib);
140
+ }
141
+ json.libraries = newLibraries;
142
+ await writeFile(curJsonPath, JSON.stringify(json, null, 2), "utf-8");
143
+ break;
144
+ }
145
+ if (!json.inheritsFrom) {
146
+ break;
147
+ }
148
+ curJsonPath = minecraft.getVersionJson(json.inheritsFrom);
149
+ }
150
+ await writeFile(path.join(nativesDir, ".arm64-patched"), "3.3.1");
151
+ console.error("[MCT] arm64 LWJGL 3.3.1 patch applied");
152
+ return nativesDir;
153
+ }
@@ -0,0 +1,42 @@
1
+ import type { CommandContext } from "../../util/context.js";
2
+ import { CacheManager } from "../CacheManager.js";
3
+ import { detectJava } from "../JavaDetector.js";
4
+ import type { LoaderType } from "../types.js";
5
+ import { prepareManagedFabricRuntime } from "./FabricRuntimeDownloader.js";
6
+ export interface DownloadClientOptions {
7
+ loader?: LoaderType;
8
+ version?: string;
9
+ dir?: string;
10
+ name?: string;
11
+ wsPort?: number;
12
+ server?: string;
13
+ instanceDir?: string;
14
+ metaDir?: string;
15
+ librariesDir?: string;
16
+ assetsDir?: string;
17
+ nativesDir?: string;
18
+ java?: string;
19
+ }
20
+ export interface DownloadClientDependencies {
21
+ cacheManager?: CacheManager;
22
+ detectJavaImpl?: typeof detectJava;
23
+ fetchImpl?: typeof fetch;
24
+ prepareManagedRuntimeImpl?: typeof prepareManagedFabricRuntime;
25
+ }
26
+ export declare function downloadClientMod(context: CommandContext, options: DownloadClientOptions, dependencies?: DownloadClientDependencies): Promise<{
27
+ downloaded: boolean;
28
+ variantId: string;
29
+ minecraftVersion: string;
30
+ loader: LoaderType;
31
+ javaCommand: string;
32
+ javaVersion: number | undefined;
33
+ clientRootDir: string;
34
+ minecraftDir: string;
35
+ modsDir: string;
36
+ jar: string;
37
+ cachePath: string;
38
+ clientName: string;
39
+ launchCommandConfigured: boolean;
40
+ runtimeRootDir: string | undefined;
41
+ runtimeVersionId: string | undefined;
42
+ }>;
@@ -0,0 +1,233 @@
1
+ import { access, mkdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+ import { DEFAULT_WS_PORT_BASE, loadConfig, writeConfig } from "../../util/config.js";
5
+ import { MctError } from "../../util/errors.js";
6
+ import { CacheManager } from "../CacheManager.js";
7
+ import { copyFileIfMissing, downloadFile } from "../DownloadUtils.js";
8
+ import { detectJava } from "../JavaDetector.js";
9
+ import { findVariantByVersionAndLoader, getDefaultVariant, getModArtifactFileName, loadModVariantCatalog } from "../ModVariantCatalog.js";
10
+ import { prepareManagedFabricRuntime } from "./FabricRuntimeDownloader.js";
11
+ const GITHUB_RELEASE_BASE_URL = process.env.MCT_MOD_DOWNLOAD_BASE_URL || "https://github.com/kzheart/mc-pilot/releases/download";
12
+ function ensureSupportedVariant(variant) {
13
+ if (variant.loader !== "fabric") {
14
+ throw new MctError({
15
+ code: "UNSUPPORTED_LOADER",
16
+ message: `Loader ${variant.loader} is not implemented yet`
17
+ }, 4);
18
+ }
19
+ if (!variant.fabricLoaderVersion || !variant.yarnMappings) {
20
+ throw new MctError({
21
+ code: "VARIANT_NOT_BUILDABLE",
22
+ message: `Variant ${variant.id} is not buildable yet`,
23
+ details: {
24
+ support: variant.support,
25
+ validation: variant.validation
26
+ }
27
+ }, 4);
28
+ }
29
+ }
30
+ async function resolveArtifact(context, variant, cacheManager, fetchImpl = fetch) {
31
+ const artifactFileName = getModArtifactFileName(variant);
32
+ const gradleModule = variant.gradleModule ?? `version-${variant.minecraftVersion}`;
33
+ const buildArtifactPath = path.join(context.cwd, "client-mod", gradleModule, "build", "libs", artifactFileName);
34
+ const cacheArtifactPath = cacheManager.getModFile(artifactFileName);
35
+ // 1. Check local build artifact
36
+ try {
37
+ await access(buildArtifactPath);
38
+ await copyFileIfMissing(buildArtifactPath, cacheArtifactPath);
39
+ return { sourcePath: buildArtifactPath, cachePath: cacheArtifactPath, artifactFileName, source: "local-build" };
40
+ }
41
+ catch { }
42
+ // 2. Check cache
43
+ try {
44
+ await access(cacheArtifactPath);
45
+ return { sourcePath: cacheArtifactPath, cachePath: cacheArtifactPath, artifactFileName, source: "cache" };
46
+ }
47
+ catch { }
48
+ // 3. Download from GitHub Releases
49
+ const modVersion = variant.modVersion ?? "0.1.0";
50
+ const releaseTag = `v${modVersion}`;
51
+ const downloadUrl = `${GITHUB_RELEASE_BASE_URL}/${releaseTag}/${artifactFileName}`;
52
+ try {
53
+ await downloadFile(downloadUrl, cacheArtifactPath, fetchImpl);
54
+ return { sourcePath: cacheArtifactPath, cachePath: cacheArtifactPath, artifactFileName, source: "github-release" };
55
+ }
56
+ catch (error) {
57
+ throw new MctError({
58
+ code: "ARTIFACT_NOT_FOUND",
59
+ message: `Could not find mod artifact for ${variant.id}. Tried local build, cache, and GitHub Releases.`,
60
+ details: {
61
+ localBuild: buildArtifactPath,
62
+ cache: cacheArtifactPath,
63
+ downloadUrl,
64
+ downloadError: error instanceof Error ? error.message : String(error)
65
+ }
66
+ }, 4);
67
+ }
68
+ }
69
+ function resolveLaunchRuntimePaths(context, options) {
70
+ const runtimePaths = {
71
+ instanceDir: options.instanceDir,
72
+ metaDir: options.metaDir,
73
+ librariesDir: options.librariesDir,
74
+ assetsDir: options.assetsDir,
75
+ nativesDir: options.nativesDir
76
+ };
77
+ const requiredEntries = Object.entries({
78
+ instanceDir: runtimePaths.instanceDir,
79
+ metaDir: runtimePaths.metaDir,
80
+ librariesDir: runtimePaths.librariesDir,
81
+ assetsDir: runtimePaths.assetsDir
82
+ });
83
+ const providedRequired = requiredEntries.filter(([, value]) => Boolean(value));
84
+ if (providedRequired.length === 0) {
85
+ return undefined;
86
+ }
87
+ const missing = requiredEntries
88
+ .filter(([, value]) => !value)
89
+ .map(([key]) => key);
90
+ if (missing.length > 0) {
91
+ throw new MctError({
92
+ code: "INVALID_PARAMS",
93
+ message: "Client runtime directories must be configured together",
94
+ details: {
95
+ missing
96
+ }
97
+ }, 4);
98
+ }
99
+ return {
100
+ instanceDir: path.resolve(context.cwd, runtimePaths.instanceDir),
101
+ metaDir: path.resolve(context.cwd, runtimePaths.metaDir),
102
+ librariesDir: path.resolve(context.cwd, runtimePaths.librariesDir),
103
+ assetsDir: path.resolve(context.cwd, runtimePaths.assetsDir),
104
+ ...(runtimePaths.nativesDir
105
+ ? {
106
+ nativesDir: path.resolve(context.cwd, runtimePaths.nativesDir)
107
+ }
108
+ : {})
109
+ };
110
+ }
111
+ function buildLaunchCommand(context, runtimePaths, variant) {
112
+ return [
113
+ process.execPath,
114
+ path.join(context.cwd, "scripts", "launch-fabric-client.mjs"),
115
+ "--instance-dir",
116
+ runtimePaths.instanceDir,
117
+ "--meta-dir",
118
+ runtimePaths.metaDir,
119
+ "--libraries-dir",
120
+ runtimePaths.librariesDir,
121
+ "--assets-dir",
122
+ runtimePaths.assetsDir,
123
+ ...(runtimePaths.nativesDir ? ["--natives-dir", runtimePaths.nativesDir] : []),
124
+ "--minecraft-version",
125
+ variant.minecraftVersion,
126
+ "--fabric-loader-version",
127
+ variant.fabricLoaderVersion ?? "0.16.10"
128
+ ];
129
+ }
130
+ function buildManagedLaunchCommand(context, runtimeRootDir, versionId, gameDir) {
131
+ return [
132
+ process.execPath,
133
+ path.join(context.cwd, "scripts", "launch-fabric-client.mjs"),
134
+ "--runtime-root",
135
+ runtimeRootDir,
136
+ "--version-id",
137
+ versionId,
138
+ "--game-dir",
139
+ gameDir
140
+ ];
141
+ }
142
+ async function ensureJavaReady(variant, detectJavaImpl, command) {
143
+ const result = await detectJavaImpl(command ?? "java");
144
+ const requiredVersion = variant.javaVersion ?? 17;
145
+ if (!result.available) {
146
+ throw new MctError({
147
+ code: "JAVA_NOT_FOUND",
148
+ message: `Java ${requiredVersion}+ is required for ${variant.id}`,
149
+ details: {
150
+ command: result.command
151
+ }
152
+ }, 4);
153
+ }
154
+ if ((result.majorVersion ?? 0) < requiredVersion) {
155
+ throw new MctError({
156
+ code: "JAVA_VERSION_TOO_LOW",
157
+ message: `Java ${requiredVersion}+ is required for ${variant.id}`,
158
+ details: {
159
+ detected: result.majorVersion,
160
+ command: result.command
161
+ }
162
+ }, 4);
163
+ }
164
+ return result;
165
+ }
166
+ export async function downloadClientMod(context, options, dependencies = {}) {
167
+ const loader = options.loader ?? "fabric";
168
+ const cacheManager = dependencies.cacheManager ?? new CacheManager();
169
+ const detectJavaImpl = dependencies.detectJavaImpl ?? detectJava;
170
+ const fetchImpl = dependencies.fetchImpl ?? fetch;
171
+ const prepareManagedRuntimeImpl = dependencies.prepareManagedRuntimeImpl ?? prepareManagedFabricRuntime;
172
+ const catalog = await loadModVariantCatalog();
173
+ const variant = options.version
174
+ ? findVariantByVersionAndLoader(catalog, options.version, loader)
175
+ : getDefaultVariant(catalog);
176
+ if (!variant) {
177
+ throw new MctError({
178
+ code: "VARIANT_NOT_FOUND",
179
+ message: `No mod variant found for ${options.version ?? "default"} / ${loader}`
180
+ }, 4);
181
+ }
182
+ ensureSupportedVariant(variant);
183
+ const java = await ensureJavaReady(variant, detectJavaImpl, options.java);
184
+ const artifact = await resolveArtifact(context, variant, cacheManager, fetchImpl);
185
+ const clientRootDir = path.resolve(context.cwd, options.dir ?? "./client");
186
+ const minecraftDir = path.join(clientRootDir, "minecraft");
187
+ const modsDir = path.join(minecraftDir, "mods");
188
+ await mkdir(minecraftDir, { recursive: true });
189
+ await mkdir(modsDir, { recursive: true });
190
+ const targetJarPath = path.join(modsDir, artifact.artifactFileName);
191
+ await copyFileIfMissing(artifact.sourcePath, targetJarPath);
192
+ const clientName = options.name ?? "default";
193
+ const latestConfig = await loadConfig(context.configPath, context.cwd);
194
+ const configuredClient = latestConfig.clients[clientName] ?? {};
195
+ const runtimePaths = resolveLaunchRuntimePaths(context, options);
196
+ const managedRuntime = runtimePaths
197
+ ? undefined
198
+ : await prepareManagedRuntimeImpl(variant, clientRootDir, { fetchImpl });
199
+ const generatedLaunchCommand = runtimePaths
200
+ ? buildLaunchCommand(context, runtimePaths, variant)
201
+ : buildManagedLaunchCommand(context, managedRuntime.runtimeRootDir, managedRuntime.versionId, managedRuntime.gameDir);
202
+ latestConfig.clients[clientName] = {
203
+ ...configuredClient,
204
+ version: variant.minecraftVersion,
205
+ wsPort: options.wsPort ?? configuredClient.wsPort ?? DEFAULT_WS_PORT_BASE,
206
+ server: options.server ?? configuredClient.server ?? "localhost:25565",
207
+ workingDir: path.relative(context.cwd, minecraftDir) || ".",
208
+ env: {
209
+ ...configuredClient.env,
210
+ MCT_CLIENT_MOD_VARIANT: variant.id,
211
+ MCT_CLIENT_MOD_JAR: path.relative(context.cwd, targetJarPath)
212
+ },
213
+ ...(generatedLaunchCommand ? { launchCommand: generatedLaunchCommand } : {})
214
+ };
215
+ await writeConfig(context.configPath, context.cwd, latestConfig);
216
+ return {
217
+ downloaded: true,
218
+ variantId: variant.id,
219
+ minecraftVersion: variant.minecraftVersion,
220
+ loader: variant.loader,
221
+ javaCommand: java.command,
222
+ javaVersion: java.majorVersion,
223
+ clientRootDir,
224
+ minecraftDir,
225
+ modsDir,
226
+ jar: targetJarPath,
227
+ cachePath: artifact.cachePath,
228
+ clientName,
229
+ launchCommandConfigured: Boolean(generatedLaunchCommand),
230
+ runtimeRootDir: managedRuntime?.runtimeRootDir,
231
+ runtimeVersionId: managedRuntime?.versionId
232
+ };
233
+ }
@@ -0,0 +1,10 @@
1
+ import type { ModVariant } from "../types.js";
2
+ export interface PrepareFabricRuntimeDependencies {
3
+ fetchImpl?: typeof fetch;
4
+ }
5
+ export interface PreparedFabricRuntime {
6
+ runtimeRootDir: string;
7
+ gameDir: string;
8
+ versionId: string;
9
+ }
10
+ export declare function prepareManagedFabricRuntime(variant: ModVariant, clientRootDir: string, dependencies?: PrepareFabricRuntimeDependencies): Promise<PreparedFabricRuntime>;