UNPKG

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