UNPKG

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