UNPKG

22.6 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};
8var __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};
15Object.defineProperty(exports, "__esModule", { value: true });
16const installer_1 = require("@xmcl/installer");
17const net_1 = require("@xmcl/net");
18const task_1 = __importDefault(require("@xmcl/task"));
19const unzip_1 = __importDefault(require("@xmcl/unzip"));
20const util_1 = require("@xmcl/util");
21const version_1 = require("@xmcl/version");
22const path = __importStar(require("path"));
23async 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 * The forge installer Module to install forge to the game
39 */
40var ForgeInstaller;
41(function (ForgeInstaller) {
42 ForgeInstaller.DEFAULT_FORGE_MAVEN = "http://files.minecraftforge.net";
43 /**
44 * Diagnose for specific forge version. Majorly for the current installer forge. (mcversion >= 1.13)
45 *
46 * Don't use this with the version less than 1.13
47 * @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
48 * @param minecraft The minecraft location.
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 // if we have to process file, we have to check if the forge deps are ready
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 * Post processing function for new forge installer (mcversion >= 1.13). You can use this with `ForgeInstaller.diagnose`.
144 *
145 * @param mc The minecraft location
146 * @param proc The processor
147 * @param java The java executor
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 * Install for forge installer step 2 and 3.
218 * @param version The version string or installer profile
219 * @param minecraft The minecraft location
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 * Install for forge installer step 2 and 3.
240 * @param version The version string or installer profile
241 * @param minecraft The minecraft location
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 * 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 install(version, minecraft, option) {
422 return installTask(version, minecraft, option).execute();
423 }
424 ForgeInstaller.install = install;
425 /**
426 * Install forge to target location.
427 * Installation task for forge with mcversion >= 1.13 requires java installed on your pc.
428 * @param version The forge version meta
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"));
444exports.default = ForgeInstaller;
445//# sourceMappingURL=index.js.map
\No newline at end of file