1 | "use strict";
|
2 | function __export(m) {
|
3 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
4 | }
|
5 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
6 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
7 | };
|
8 | var __importStar = (this && this.__importStar) || function (mod) {
|
9 | if (mod && mod.__esModule) return mod;
|
10 | var result = {};
|
11 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
12 | result["default"] = mod;
|
13 | return result;
|
14 | };
|
15 | Object.defineProperty(exports, "__esModule", { value: true });
|
16 | const installer_1 = require("@xmcl/installer");
|
17 | const net_1 = require("@xmcl/net");
|
18 | const task_1 = __importDefault(require("@xmcl/task"));
|
19 | const unzip_1 = __importDefault(require("@xmcl/unzip"));
|
20 | const util_1 = require("@xmcl/util");
|
21 | const version_1 = require("@xmcl/version");
|
22 | const path = __importStar(require("path"));
|
23 | async function findMainClass(lib) {
|
24 | const zip = await unzip_1.default.open(lib, { lazyEntries: true });
|
25 | const [manifest] = await zip.filterEntries(["META-INF/MANIFEST.MF"]);
|
26 | let mainClass;
|
27 | if (manifest) {
|
28 | const content = await zip.readEntry(manifest).then((b) => b.toString());
|
29 | const mainClassPair = content.split("\n").map((l) => l.split(": ")).filter((arr) => arr[0] === "Main-Class")[0];
|
30 | if (mainClassPair) {
|
31 | mainClass = mainClassPair[1].trim();
|
32 | }
|
33 | }
|
34 | zip.close();
|
35 | return mainClass;
|
36 | }
|
37 |
|
38 |
|
39 |
|
40 | var ForgeInstaller;
|
41 | (function (ForgeInstaller) {
|
42 | ForgeInstaller.DEFAULT_FORGE_MAVEN = "http://files.minecraftforge.net";
|
43 | |
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 | async function diagnoseForgeVersion(versionOrProfile, minecraft) {
|
51 | const version = typeof versionOrProfile === "string" ? versionOrProfile : versionOrProfile.version;
|
52 | const mc = util_1.MinecraftFolder.from(minecraft);
|
53 | const verRoot = mc.getVersionRoot(version);
|
54 | const versionJsonPath = mc.getVersionJson(version);
|
55 | const diag = {
|
56 | badProcessedFiles: [],
|
57 | missingInstallDependencies: [],
|
58 | badVersionJson: false,
|
59 | missingBinpatch: false,
|
60 | badInstall: false,
|
61 | missingSrgJar: false,
|
62 | missingMinecraftExtraJar: false,
|
63 | missingForgePatchesJar: false,
|
64 | };
|
65 | let prof;
|
66 | if (typeof versionOrProfile === "string") {
|
67 | const installProfPath = path.join(verRoot, "install_profile.json");
|
68 | if (await util_1.vfs.exists(installProfPath)) {
|
69 | prof = JSON.parse(await util_1.vfs.readFile(installProfPath).then((b) => b.toString()));
|
70 | }
|
71 | }
|
72 | else {
|
73 | prof = versionOrProfile;
|
74 | }
|
75 | if (prof) {
|
76 | const processedProfile = postProcessInstallProfile(mc, prof);
|
77 | for (const proc of processedProfile.processors) {
|
78 | if (proc.outputs) {
|
79 | let bad = false;
|
80 | for (const file in proc.outputs) {
|
81 | if (!await util_1.vfs.validate(file, { algorithm: "sha1", hash: proc.outputs[file].replace(/\'/g, "") })) {
|
82 | bad = true;
|
83 | break;
|
84 | }
|
85 | }
|
86 | if (bad) {
|
87 | diag.badProcessedFiles.push(proc);
|
88 | }
|
89 | }
|
90 | }
|
91 |
|
92 | if (diag.badProcessedFiles.length !== 0) {
|
93 | const libValidMask = await Promise.all(processedProfile.libraries.map(async (lib) => {
|
94 | const artifact = lib.downloads.artifact;
|
95 | const libPath = mc.getLibraryByPath(artifact.path);
|
96 | if (await util_1.vfs.exists(libPath)) {
|
97 | return artifact.sha1 ? util_1.vfs.validate(libPath) : true;
|
98 | }
|
99 | return false;
|
100 | }));
|
101 | const missingLibraries = processedProfile.libraries.filter((_, i) => !libValidMask[i]);
|
102 | diag.missingInstallDependencies.push(...missingLibraries);
|
103 | const validClient = await util_1.vfs.stat(processedProfile.data.BINPATCH.client).then((s) => s.size !== 0).catch((_) => false);
|
104 | if (!validClient) {
|
105 | diag.missingBinpatch = true;
|
106 | diag.badInstall = true;
|
107 | }
|
108 | }
|
109 | }
|
110 | if (await util_1.vfs.exists(versionJsonPath)) {
|
111 | const versionJSON = JSON.parse(await util_1.vfs.readFile(versionJsonPath).then((b) => b.toString()));
|
112 | if (versionJSON.arguments && versionJSON.arguments.game) {
|
113 | const args = versionJSON.arguments.game;
|
114 | const forgeVersion = args.indexOf("--fml.forgeVersion") + 1;
|
115 | const mcVersion = args.indexOf("--fml.mcVersion") + 1;
|
116 | const mcpVersion = args.indexOf("--fml.mcpVersion") + 1;
|
117 | if (!forgeVersion || !mcVersion || !mcpVersion) {
|
118 | diag.badVersionJson = true;
|
119 | diag.badInstall = true;
|
120 | }
|
121 | else {
|
122 | const srgPath = mc.getLibraryByPath(`net/minecraft/client/${mcVersion}-${mcpVersion}/client-${mcVersion}-${mcpVersion}-srg.jar`);
|
123 | const extraPath = mc.getLibraryByPath(`net/minecraft/client/${mcVersion}/client-${mcVersion}-extra.jar`);
|
124 | const forgePatchPath = mc.getLibraryByPath(`net/minecraftforge/forge/${mcVersion}-${forgeVersion}/forge-${mcVersion}-${forgeVersion}-client.jar`);
|
125 | diag.missingSrgJar = await util_1.vfs.missing(srgPath);
|
126 | diag.missingMinecraftExtraJar = await util_1.vfs.missing(extraPath);
|
127 | diag.missingForgePatchesJar = await util_1.vfs.missing(forgePatchPath);
|
128 | }
|
129 | }
|
130 | else {
|
131 | diag.badVersionJson = true;
|
132 | diag.badInstall = true;
|
133 | }
|
134 | }
|
135 | else {
|
136 | diag.badVersionJson = true;
|
137 | diag.badInstall = true;
|
138 | }
|
139 | return diag;
|
140 | }
|
141 | ForgeInstaller.diagnoseForgeVersion = diagnoseForgeVersion;
|
142 | |
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 | async function postProcess(mc, proc, java) {
|
150 | const jarRealPath = mc.getLibraryByPath(version_1.Version.getLibraryInfo(proc.jar).path);
|
151 | const mainClass = await findMainClass(jarRealPath);
|
152 | if (!mainClass) {
|
153 | throw new Error(`Cannot find main class for processor ${proc.jar}.`);
|
154 | }
|
155 | const cp = [...proc.classpath, proc.jar].map(version_1.Version.getLibraryInfo).map((p) => mc.getLibraryByPath(p.path)).join(path.delimiter);
|
156 | const cmd = ["-cp", cp, mainClass, ...proc.args];
|
157 | await java(cmd);
|
158 | let failed = false;
|
159 | if (proc.outputs) {
|
160 | for (const file in proc.outputs) {
|
161 | if (!await util_1.vfs.validate(file, { algorithm: "sha1", hash: proc.outputs[file].replace(/\'/g, "") })) {
|
162 | console.error(`Fail to process ${proc.jar} @ ${file} since its validation failed.`);
|
163 | failed = true;
|
164 | }
|
165 | }
|
166 | }
|
167 | if (failed) {
|
168 | console.error(`Java arguments: ${JSON.stringify(cmd)}`);
|
169 | throw new Error(`Fail to process post processing since its validation failed.`);
|
170 | }
|
171 | }
|
172 | ForgeInstaller.postProcess = postProcess;
|
173 | function postProcessInstallProfile(mc, installProfile) {
|
174 | function processValue(v) {
|
175 | if (v.match(/^\[.+\]$/g)) {
|
176 | const targetId = v.substring(1, v.length - 1);
|
177 | return mc.getLibraryByPath(version_1.Version.getLibraryInfo(targetId).path);
|
178 | }
|
179 | return v;
|
180 | }
|
181 | function processMapping(data, m) {
|
182 | m = processValue(m);
|
183 | if (m.match(/^{.+}$/g)) {
|
184 | const key = m.substring(1, m.length - 1);
|
185 | m = data[key].client;
|
186 | }
|
187 | return m;
|
188 | }
|
189 | const profile = JSON.parse(JSON.stringify(installProfile));
|
190 | profile.data.MINECRAFT_JAR = {
|
191 | client: mc.getVersionJar(profile.minecraft),
|
192 | server: "",
|
193 | };
|
194 | for (const key in profile.data) {
|
195 | const value = profile.data[key];
|
196 | value.client = processValue(value.client);
|
197 | value.server = processValue(value.server);
|
198 | if (key === "BINPATCH") {
|
199 | const verRoot = mc.getVersionRoot(profile.version);
|
200 | value.client = path.join(verRoot, value.client);
|
201 | value.server = path.join(verRoot, value.server);
|
202 | }
|
203 | }
|
204 | for (const proc of profile.processors) {
|
205 | proc.args = proc.args.map((a) => processMapping(profile.data, a));
|
206 | if (proc.outputs) {
|
207 | const replacedOutput = {};
|
208 | for (const key in proc.outputs) {
|
209 | replacedOutput[processMapping(profile.data, key)] = processMapping(profile.data, proc.outputs[key]);
|
210 | }
|
211 | proc.outputs = replacedOutput;
|
212 | }
|
213 | }
|
214 | return profile;
|
215 | }
|
216 | |
217 |
|
218 |
|
219 |
|
220 |
|
221 | function installByInstallerPartialTask(version, minecraft, option = {}) {
|
222 | return task_1.default.create("installForge", async (context) => {
|
223 | const mc = util_1.MinecraftFolder.from(minecraft);
|
224 | let prof;
|
225 | let ver;
|
226 | if (typeof version === "string") {
|
227 | const versionRoot = mc.getVersionRoot(version);
|
228 | prof = await util_1.vfs.readFile(path.join(versionRoot, "install_profile.json")).then((b) => b.toString()).then(JSON.parse);
|
229 | }
|
230 | else {
|
231 | prof = version;
|
232 | }
|
233 | ver = await util_1.vfs.readFile(mc.getVersionJson(prof.version)).then((b) => b.toString()).then(JSON.parse);
|
234 | await installByInstallerPartialWork(mc, prof, ver, option.java || util_1.JavaExecutor.createSimple("java"), option)(context);
|
235 | });
|
236 | }
|
237 | ForgeInstaller.installByInstallerPartialTask = installByInstallerPartialTask;
|
238 | |
239 |
|
240 |
|
241 |
|
242 |
|
243 | async function installByInstallerPartial(version, minecraft, option = {}) {
|
244 | return installByInstallerPartialTask(version, minecraft, option).execute();
|
245 | }
|
246 | ForgeInstaller.installByInstallerPartial = installByInstallerPartial;
|
247 | function installByInstallerPartialWork(mc, profile, versionJson, java, installLibOption) {
|
248 | return async (context) => {
|
249 | profile = postProcessInstallProfile(mc, profile);
|
250 | const parsedLibs = version_1.Version.resolveLibraries([...profile.libraries, ...versionJson.libraries]);
|
251 | await context.execute("downloadLibraries", installer_1.Installer.installLibrariesDirectTask(parsedLibs, mc, installLibOption).work);
|
252 | await context.execute("postProcessing", async (ctx) => {
|
253 | ctx.update(0, profile.processors.length);
|
254 | let i = 0;
|
255 | const errs = [];
|
256 | for (const proc of profile.processors) {
|
257 | try {
|
258 | await postProcess(mc, proc, java);
|
259 | }
|
260 | catch (e) {
|
261 | errs.push(e);
|
262 | }
|
263 | ctx.update(i += 1, profile.processors.length);
|
264 | }
|
265 | i += 1;
|
266 | ctx.update(i, profile.processors.length);
|
267 | if (errs.length !== 0) {
|
268 | throw new Error(`Fail to post processing`);
|
269 | }
|
270 | });
|
271 | };
|
272 | }
|
273 | function installByInstallerTask(version, minecraft, maven, installLibOption, java, tempDir, clearTempDir = true) {
|
274 | return async (context) => {
|
275 | const mc = typeof minecraft === "string" ? new util_1.MinecraftFolder(minecraft) : minecraft;
|
276 | const forgeVersion = `${version.mcversion}-${version.version}`;
|
277 | let versionId;
|
278 | const installerURLFallback = `${maven}/maven/net/minecraftforge/forge/${forgeVersion}/forge-${forgeVersion}-installer.jar`;
|
279 | const installerURL = `${maven}${version.installer.path}`;
|
280 | function downloadInstallerTask(installer, dest) {
|
281 | return async (ctx) => {
|
282 | let inStream;
|
283 | if (await util_1.vfs.validate(dest, { algorithm: "md5", hash: version.installer.md5 }, { algorithm: "sha1", hash: version.installer.sha1 })) {
|
284 | inStream = util_1.vfs.createReadStream(dest);
|
285 | }
|
286 | if (!inStream) {
|
287 | inStream = net_1.got.stream(installer, {
|
288 | method: "GET",
|
289 | headers: { connection: "keep-alive" },
|
290 | }).on("error", () => { })
|
291 | .on("downloadProgress", (progress) => { ctx.update(progress.transferred, progress.total); });
|
292 | inStream.pipe(util_1.vfs.createWriteStream(dest));
|
293 | }
|
294 | return inStream.pipe(unzip_1.default.createParseStream({ lazyEntries: true }))
|
295 | .wait();
|
296 | };
|
297 | }
|
298 | const temp = tempDir || await util_1.vfs.mkdtemp(mc.root + path.sep);
|
299 | await util_1.vfs.ensureDir(temp);
|
300 | const installJar = path.join(temp, "forge-installer.jar");
|
301 | let profile;
|
302 | let versionJson;
|
303 | async function processVersion(zip, installProfileEntry, versionEntry, clientDataEntry) {
|
304 | profile = await zip.readEntry(installProfileEntry).then((b) => b.toString()).then(JSON.parse);
|
305 | versionJson = await zip.readEntry(versionEntry).then((b) => b.toString()).then(JSON.parse);
|
306 | versionId = versionJson.id;
|
307 | const rootPath = mc.getVersionRoot(versionJson.id);
|
308 | const jsonPath = path.join(rootPath, `${versionJson.id}.json`);
|
309 | const installJsonPath = path.join(rootPath, `install_profile.json`);
|
310 | const clientDataPath = path.join(rootPath, profile.data.BINPATCH.client);
|
311 | await util_1.vfs.ensureFile(jsonPath);
|
312 | await util_1.vfs.writeFile(installJsonPath, JSON.stringify(profile));
|
313 | await util_1.vfs.writeFile(jsonPath, JSON.stringify(versionJson));
|
314 | await util_1.vfs.ensureFile(clientDataPath);
|
315 | const stream = await zip.openEntry(clientDataEntry);
|
316 | await util_1.vfs.waitStream(stream.pipe(util_1.vfs.createWriteStream(clientDataPath)));
|
317 | }
|
318 | async function processExtractLibrary(s, p) {
|
319 | const file = mc.getLibraryByPath(p);
|
320 | await util_1.vfs.ensureFile(file);
|
321 | await util_1.vfs.waitStream(s.pipe(util_1.vfs.createWriteStream(file)));
|
322 | }
|
323 | try {
|
324 | let zip;
|
325 | try {
|
326 | zip = await context.execute("downloadInstaller", downloadInstallerTask(installerURL, installJar));
|
327 | }
|
328 | catch (_a) {
|
329 | zip = await context.execute("downloadInstaller", downloadInstallerTask(installerURLFallback, installJar));
|
330 | }
|
331 | const [forgeEntry, forgeUniversalEntry, clientDataEntry, installProfileEntry, versionEntry] = await zip.filterEntries([
|
332 | `maven/net/minecraftforge/forge/${forgeVersion}/forge-${forgeVersion}.jar`,
|
333 | `maven/net/minecraftforge/forge/${forgeVersion}/forge-${forgeVersion}-universal.jar`,
|
334 | "data/client.lzma",
|
335 | "install_profile.json",
|
336 | "version.json"
|
337 | ]);
|
338 | if (!forgeEntry) {
|
339 | throw new Error("Missing forge jar entry");
|
340 | }
|
341 | if (!forgeUniversalEntry) {
|
342 | throw new Error("Missing forge universal entry");
|
343 | }
|
344 | if (!installProfileEntry) {
|
345 | throw new Error("Missing install profile");
|
346 | }
|
347 | if (!versionEntry) {
|
348 | throw new Error("Missing version entry");
|
349 | }
|
350 | await processExtractLibrary(await zip.openEntry(forgeEntry), forgeEntry.fileName);
|
351 | await processExtractLibrary(await zip.openEntry(forgeUniversalEntry), forgeUniversalEntry.fileName);
|
352 | await processVersion(zip, installProfileEntry, versionEntry, clientDataEntry);
|
353 | await installByInstallerPartialWork(mc, profile, versionJson, java, installLibOption)(context);
|
354 | return versionId;
|
355 | }
|
356 | catch (e) {
|
357 | if (clearTempDir) {
|
358 | await util_1.vfs.remove(temp);
|
359 | }
|
360 | throw e;
|
361 | }
|
362 | };
|
363 | }
|
364 | function installByUniversalTask(version, minecraft, maven, checkDependecies) {
|
365 | return async (context) => {
|
366 | const mc = typeof minecraft === "string" ? new util_1.MinecraftFolder(minecraft) : minecraft;
|
367 | const forgeVersion = `${version.mcversion}-${version.version}`;
|
368 | const jarPath = mc.getLibraryByPath(`net/minecraftforge/forge/${forgeVersion}/forge-${forgeVersion}.jar`);
|
369 | let fullVersion;
|
370 | let realJarPath;
|
371 | const universalURLFallback = `${maven}/maven/net/minecraftforge/forge/${forgeVersion}/forge-${forgeVersion}-universal.jar`;
|
372 | const universalURL = `${maven}${version.universal.path}`;
|
373 | await context.execute("installForgeJar", async () => {
|
374 | if (await util_1.vfs.exists(jarPath)) {
|
375 | const valid = await util_1.vfs.validate(jarPath, { algorithm: "md5", hash: version.universal.md5 }, { algorithm: "sha1", hash: version.universal.sha1 });
|
376 | if (valid) {
|
377 | return;
|
378 | }
|
379 | }
|
380 | try {
|
381 | await context.execute("downloadJar", net_1.downloadFileWork({ url: universalURL, destination: jarPath }));
|
382 | }
|
383 | catch (_a) {
|
384 | await context.execute("redownloadJar", net_1.downloadFileWork({ url: universalURLFallback, destination: jarPath }));
|
385 | }
|
386 | });
|
387 | await context.execute("installForgeJson", async () => {
|
388 | const zip = await unzip_1.default.open(jarPath, { lazyEntries: true });
|
389 | const [versionEntry] = await zip.filterEntries(["version.json"]);
|
390 | if (versionEntry) {
|
391 | const buf = await zip.readEntry(versionEntry);
|
392 | const raw = JSON.parse(buf.toString());
|
393 | const id = raw.id;
|
394 | fullVersion = id;
|
395 | const rootPath = mc.getVersionRoot(fullVersion);
|
396 | realJarPath = mc.getLibraryByPath(version_1.Version.getLibraryInfo(raw.libraries.find((l) => l.name.startsWith("net.minecraftforge:forge"))).path);
|
397 | await util_1.vfs.ensureDir(rootPath);
|
398 | await util_1.vfs.writeFile(path.join(rootPath, `${id}.json`), buf);
|
399 | }
|
400 | else {
|
401 | throw new Error(`Cannot install forge json for ${version.version} since the version json is missing!`);
|
402 | }
|
403 | });
|
404 | if (realJarPath !== jarPath) {
|
405 | await util_1.vfs.ensureFile(realJarPath);
|
406 | await util_1.vfs.copyFile(jarPath, realJarPath);
|
407 | await util_1.vfs.unlink(jarPath);
|
408 | }
|
409 | if (checkDependecies) {
|
410 | const resolvedVersion = await version_1.Version.parse(minecraft, fullVersion);
|
411 | context.execute("installDependencies", installer_1.Installer.installDependenciesTask(resolvedVersion).work);
|
412 | }
|
413 | return fullVersion;
|
414 | };
|
415 | }
|
416 | |
417 |
|
418 |
|
419 |
|
420 |
|
421 | function install(version, minecraft, option) {
|
422 | return installTask(version, minecraft, option).execute();
|
423 | }
|
424 | ForgeInstaller.install = install;
|
425 | |
426 |
|
427 |
|
428 |
|
429 |
|
430 | function installTask(version, minecraft, option = {}) {
|
431 | let byInstaller = true;
|
432 | try {
|
433 | const minorVersion = Number.parseInt(version.mcversion.split(".")[1], 10);
|
434 | byInstaller = minorVersion >= 13;
|
435 | }
|
436 | catch (_a) { }
|
437 | const work = byInstaller ? installByInstallerTask(version, minecraft, option.maven || ForgeInstaller.DEFAULT_FORGE_MAVEN, option, option.java || util_1.JavaExecutor.createSimple("java"), option.tempDir, option.clearTempDirAfterInstall)
|
438 | : installByUniversalTask(version, minecraft, option.maven || ForgeInstaller.DEFAULT_FORGE_MAVEN, option.forceInstallDependencies === true);
|
439 | return task_1.default.create("installForge", work);
|
440 | }
|
441 | ForgeInstaller.installTask = installTask;
|
442 | })(ForgeInstaller = exports.ForgeInstaller || (exports.ForgeInstaller = {}));
|
443 | __export(require("./forgeweb"));
|
444 | exports.default = ForgeInstaller;
|
445 |
|
\ | No newline at end of file |