diff --git a/package-lock.json b/package-lock.json index eb07481..083b101 100644 --- a/package-lock.json +++ b/package-lock.json @@ -103,9 +103,9 @@ "dev": true }, "@types/node": { - "version": "12.12.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.42.tgz", - "integrity": "sha512-R/9QdYFLL9dE9l5cWWzWIZByVGFd7lk7JVOJ7KD+E1SJ4gni7XJRLz9QTjyYQiHIqEAgku9VgxdLjMlhhUaAFg==", + "version": "12.12.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.43.tgz", + "integrity": "sha512-KUyZdkGCnVPuXfsKmDUu2XLui65LZIJ2s0M57noy5e+ixUT2oK33ep7zlvgzI8LElcWqbf8AR+o/3GqAPac2zA==", "dev": true }, "@types/yargs": { @@ -124,12 +124,12 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.0.2.tgz", - "integrity": "sha512-ER3bSS/A/pKQT/hjMGCK8UQzlL0yLjuCZ/G8CDFJFVTfl3X65fvq2lNYqOG8JPTfrPa2RULCdwfOyFjZEMNExQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.1.0.tgz", + "integrity": "sha512-D52KwdgkjYc+fmTZKW7CZpH5ZBJREJKZXRrveMiRCmlzZ+Rw9wRVJ1JAmHQ9b/+Ehy1ZeaylofDB9wwXUt83wg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "3.0.2", + "@typescript-eslint/experimental-utils": "3.1.0", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", "semver": "^7.3.2", @@ -137,33 +137,33 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.0.2.tgz", - "integrity": "sha512-4Wc4EczvoY183SSEnKgqAfkj1eLtRgBQ04AAeG+m4RhTVyaazxc1uI8IHf0qLmu7xXe9j1nn+UoDJjbmGmuqXQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.1.0.tgz", + "integrity": "sha512-Zf8JVC2K1svqPIk1CB/ehCiWPaERJBBokbMfNTNRczCbQSlQXaXtO/7OfYz9wZaecNvdSvVADt6/XQuIxhC79w==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "3.0.2", + "@typescript-eslint/typescript-estree": "3.1.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" } }, "@typescript-eslint/parser": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.0.2.tgz", - "integrity": "sha512-80Z7s83e8QXHNUspqVlWwb4t5gdz/1bBBmafElbK1wwAwiD/yvJsFyHRxlEpNrt4rdK6eB3p+2WEFkEDHAKk9w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.1.0.tgz", + "integrity": "sha512-NcDSJK8qTA2tPfyGiPes9HtVKLbksmuYjlgGAUs7Ld2K0swdWibnCq9IJx9kJN8JJdgUJSorFiGaPHBgH81F/Q==", "dev": true, "requires": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "3.0.2", - "@typescript-eslint/typescript-estree": "3.0.2", + "@typescript-eslint/experimental-utils": "3.1.0", + "@typescript-eslint/typescript-estree": "3.1.0", "eslint-visitor-keys": "^1.1.0" } }, "@typescript-eslint/typescript-estree": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.0.2.tgz", - "integrity": "sha512-cs84mxgC9zQ6viV8MEcigfIKQmKtBkZNDYf8Gru2M+MhnA6z9q0NFMZm2IEzKqAwN8lY5mFVd1Z8DiHj6zQ3Tw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.1.0.tgz", + "integrity": "sha512-+4nfYauqeQvK55PgFrmBWFVYb6IskLyOosYEmhH3mSVhfBp9AIJnjExdgDmKWoOBHRcPM8Ihfm2BFpZf0euUZQ==", "dev": true, "requires": { "debug": "^4.1.1", diff --git a/package.json b/package.json index 8e2bd82..fcebca3 100644 --- a/package.json +++ b/package.json @@ -28,10 +28,10 @@ "devDependencies": { "@types/adm-zip": "^0.4.33", "@types/fs-extra": "^9.0.1", - "@types/node": "^12.12.42", + "@types/node": "^12.12.43", "@types/yargs": "^15.0.5", - "@typescript-eslint/eslint-plugin": "^3.0.2", - "@typescript-eslint/parser": "^3.0.2", + "@typescript-eslint/eslint-plugin": "^3.1.0", + "@typescript-eslint/parser": "^3.1.0", "eslint": "^7.1.0", "rimraf": "^3.0.2", "typescript": "^3.9.3" diff --git a/src/model/struct/model/module/forgemod.struct.ts b/src/model/struct/model/module/forgemod.struct.ts index 05fb87e..adf7734 100644 --- a/src/model/struct/model/module/forgemod.struct.ts +++ b/src/model/struct/model/module/forgemod.struct.ts @@ -16,7 +16,7 @@ export abstract class BaseForgeModStructure extends ModuleStructure implements V super(absoluteRoot, relativeRoot, 'forgemods', baseUrl, Type.ForgeMod) } - public abstract isForVersion(version: MinecraftVersion): boolean + public abstract isForVersion(version: MinecraftVersion, libraryVersion: string): boolean // eslint-disable-next-line @typescript-eslint/no-unused-vars protected async getModuleUrl(name: string, path: string, stats: Stats): Promise { diff --git a/src/model/struct/model/module/forgemod/forgemod113.struct.ts b/src/model/struct/model/module/forgemod/forgemod113.struct.ts index 07d7a46..13eac64 100644 --- a/src/model/struct/model/module/forgemod/forgemod113.struct.ts +++ b/src/model/struct/model/module/forgemod/forgemod113.struct.ts @@ -11,7 +11,8 @@ export class ForgeModStructure113 extends BaseForgeModStructure { public static readonly IMPLEMENTATION_VERSION_REGEX = /^Implementation-Version: (.+)[\r\n]/ - public static isForVersion(version: MinecraftVersion): boolean { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public static isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { return VersionUtil.isVersionAcceptable(version, [13, 14, 15]) } @@ -25,8 +26,8 @@ export class ForgeModStructure113 extends BaseForgeModStructure { super(absoluteRoot, relativeRoot, baseUrl) } - public isForVersion(version: MinecraftVersion): boolean { - return ForgeModStructure113.isForVersion(version) + public isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { + return ForgeModStructure113.isForVersion(version, libraryVersion) } protected async getModuleId(name: string, path: string, stats: Stats, buf: Buffer): Promise { diff --git a/src/model/struct/model/module/forgemod/forgemod17.struct.ts b/src/model/struct/model/module/forgemod/forgemod17.struct.ts index 71efac0..98bd8b9 100644 --- a/src/model/struct/model/module/forgemod/forgemod17.struct.ts +++ b/src/model/struct/model/module/forgemod/forgemod17.struct.ts @@ -9,7 +9,8 @@ import { MinecraftVersion } from '../../../../../util/MinecraftVersion' export class ForgeModStructure17 extends BaseForgeModStructure { - public static isForVersion(version: MinecraftVersion): boolean { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public static isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { return VersionUtil.isVersionAcceptable(version, [7, 8, 9, 10, 11, 12]) } @@ -23,8 +24,8 @@ export class ForgeModStructure17 extends BaseForgeModStructure { super(absoluteRoot, relativeRoot, baseUrl) } - public isForVersion(version: MinecraftVersion): boolean { - return ForgeModStructure17.isForVersion(version) + public isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { + return ForgeModStructure17.isForVersion(version, libraryVersion) } protected async getModuleId(name: string, path: string, stats: Stats, buf: Buffer): Promise { diff --git a/src/model/struct/model/server.struct.ts b/src/model/struct/model/server.struct.ts index e48f344..084d3fa 100644 --- a/src/model/struct/model/server.struct.ts +++ b/src/model/struct/model/server.struct.ts @@ -50,6 +50,7 @@ export class ServerStructure extends BaseModelStructure { if (options.forgeVersion != null) { const fms = VersionSegmentedRegistry.getForgeModStruct( minecraftVersion, + options.forgeVersion, absoluteServerRoot, relativeServerRoot, this.baseUrl @@ -123,6 +124,7 @@ export class ServerStructure extends BaseModelStructure { const forgeModStruct = VersionSegmentedRegistry.getForgeModStruct( minecraftVersion, + serverMeta.forgeVersion, absoluteServerRoot, relativeServerRoot, this.baseUrl diff --git a/src/model/struct/repo/BaseMavenRepo.ts b/src/model/struct/repo/BaseMavenRepo.ts index dc8c5be..8dc8331 100644 --- a/src/model/struct/repo/BaseMavenRepo.ts +++ b/src/model/struct/repo/BaseMavenRepo.ts @@ -50,20 +50,24 @@ export abstract class BaseMavenRepo extends BaseFileStructure { private async downloadArtifactBase(url: string, relative: string): Promise { const resolvedURL = resolveURL(url, relative).toString() - console.debug(`Downloading ${resolvedURL}..`) + return this.downloadArtifactDirect(resolvedURL, relative) + } + + public async downloadArtifactDirect(url: string, path: string): Promise { + console.debug(`Downloading ${url}..`) const response = await axios({ method: 'get', - url: resolvedURL, + url, responseType: 'stream' }) - const localPath = resolve(this.containerDirectory, relative) + const localPath = resolve(this.containerDirectory, path) await mkdirs(dirname(localPath)) const writer = createWriteStream(localPath) response.data.pipe(writer) // tslint:disable-next-line: no-shadowed-variable return new Promise((resolve, reject) => { writer.on('finish', () => { - console.debug(`Completed download of ${resolvedURL}.`) + console.debug(`Completed download of ${url}.`) resolve() }) writer.on('error', reject) diff --git a/src/resolver/baseresolver.ts b/src/resolver/baseresolver.ts index 3959a35..d97cf0e 100644 --- a/src/resolver/baseresolver.ts +++ b/src/resolver/baseresolver.ts @@ -12,6 +12,6 @@ export abstract class BaseResolver implements Resolver, VersionSegmented { ) {} public abstract getModule(): Promise - public abstract isForVersion(version: MinecraftVersion): boolean + public abstract isForVersion(version: MinecraftVersion, libraryVersion: string): boolean } diff --git a/src/resolver/forge/adapter/ForgeGradle2.resolver.ts b/src/resolver/forge/adapter/ForgeGradle2.resolver.ts index 8e0cf8e..f70b9da 100644 --- a/src/resolver/forge/adapter/ForgeGradle2.resolver.ts +++ b/src/resolver/forge/adapter/ForgeGradle2.resolver.ts @@ -15,12 +15,13 @@ type ArrayElement = A extends readonly (infer T)[] ? T : never export class ForgeGradle2Adapter extends ForgeResolver { - public static isForVersion(version: MinecraftVersion): boolean { + public static isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { + if(version.getMinor() === 12 && !VersionUtil.isOneDotTwelveFG2(libraryVersion)) { + return false + } return VersionUtil.isVersionAcceptable(version, [7, 8, 9, 10, 11, 12]) } - protected readonly MOJANG_REMOTE_REPOSITORY = 'https://libraries.minecraft.net/' - constructor( absoluteRoot: string, relativeRoot: string, @@ -35,8 +36,8 @@ export class ForgeGradle2Adapter extends ForgeResolver { return this.getForgeByVersion() } - public isForVersion(version: MinecraftVersion): boolean { - return ForgeGradle2Adapter.isForVersion(version) + public isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { + return ForgeGradle2Adapter.isForVersion(version, libraryVersion) } public async getForgeByVersion(): Promise { diff --git a/src/resolver/forge/adapter/ForgeGradle3.resolver.ts b/src/resolver/forge/adapter/ForgeGradle3.resolver.ts index dc5d36c..4e44bfb 100644 --- a/src/resolver/forge/adapter/ForgeGradle3.resolver.ts +++ b/src/resolver/forge/adapter/ForgeGradle3.resolver.ts @@ -1,21 +1,40 @@ -import { spawn } from 'child_process' -import { copy, lstat, mkdirs, move, pathExists, readFile, remove, writeFile } from 'fs-extra' -import { Module, Type } from 'helios-distribution-types' -import { basename, dirname, join } from 'path' -import { VersionManifestFG3 } from '../../../model/forge/VersionManifestFG3' -import { LibRepoStructure } from '../../../model/struct/repo/librepo.struct' -import { JavaUtil } from '../../../util/javautil' -import { MavenUtil } from '../../../util/maven' -import { VersionUtil } from '../../../util/versionutil' +import AdmZip from 'adm-zip' import { ForgeResolver } from '../forge.resolver' import { MinecraftVersion } from '../../../util/MinecraftVersion' +import { VersionUtil } from '../../../util/versionutil' +import { Module, Type } from 'helios-distribution-types' +import { LibRepoStructure } from '../../../model/struct/repo/librepo.struct' +import { pathExists, remove, mkdirs, copy, writeFile, readFile, lstat, move, writeJson } from 'fs-extra' +import { join, basename, dirname } from 'path' +import { spawn } from 'child_process' +import { JavaUtil } from '../../../util/javautil' +import { VersionManifestFG3 } from '../../../model/forge/VersionManifestFG3' +import { MavenUtil } from '../../../util/maven' +import { createHash } from 'crypto' + +interface GeneratedFile { + name: string + group: string + artifact: string + version: string + classifiers: string[] | [undefined] + skipIfNotPresent?: boolean +} export class ForgeGradle3Adapter extends ForgeResolver { - public static isForVersion(version: MinecraftVersion): boolean { - return VersionUtil.isVersionAcceptable(version, [13, 14, 15]) + private static readonly WILDCARD_MCP_VERSION = '${mcpVersion}' + + public static isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { + if(version.getMinor() === 12 && VersionUtil.isOneDotTwelveFG2(libraryVersion)) { + return false + } + return VersionUtil.isVersionAcceptable(version, [12, 13, 14, 15]) } + private generatedFiles: GeneratedFile[] | undefined + private wildcardsInUse: string[] | undefined + constructor( absoluteRoot: string, relativeRoot: string, @@ -24,18 +43,95 @@ export class ForgeGradle3Adapter extends ForgeResolver { forgeVersion: string ) { super(absoluteRoot, relativeRoot, baseUrl, minecraftVersion, forgeVersion) + this.configure() + } + + private configure(): void { + // Configure for 13, 14, 15 + if(VersionUtil.isVersionAcceptable(this.minecraftVersion, [13, 14, 15])) { + this.generatedFiles = [ + { + name: 'base jar', + group: LibRepoStructure.FORGE_GROUP, + artifact: LibRepoStructure.FORGE_ARTIFACT, + version: this.artifactVersion, + classifiers: [undefined] + }, + { + name: 'universal jar', + group: LibRepoStructure.FORGE_GROUP, + artifact: LibRepoStructure.FORGE_ARTIFACT, + version: this.artifactVersion, + classifiers: ['universal'] + }, + { + name: 'client jar', + group: LibRepoStructure.FORGE_GROUP, + artifact: LibRepoStructure.FORGE_ARTIFACT, + version: this.artifactVersion, + classifiers: ['client'] + }, + { + name: 'client slim', + group: LibRepoStructure.MINECRAFT_GROUP, + artifact: LibRepoStructure.MINECRAFT_CLIENT_ARTIFACT, + version: this.minecraftVersion.toString(), + classifiers: [ + 'slim', + 'slim-stable' + ] + }, + { + name: 'client data', + group: LibRepoStructure.MINECRAFT_GROUP, + artifact: LibRepoStructure.MINECRAFT_CLIENT_ARTIFACT, + version: this.minecraftVersion.toString(), + classifiers: ['data'], + skipIfNotPresent: true + }, + { + name: 'client extra', + group: LibRepoStructure.MINECRAFT_GROUP, + artifact: LibRepoStructure.MINECRAFT_CLIENT_ARTIFACT, + version: this.minecraftVersion.toString(), + classifiers: [ + 'extra', + 'extra-stable' + ] + }, + { + name: 'client srg', + group: LibRepoStructure.MINECRAFT_GROUP, + artifact: LibRepoStructure.MINECRAFT_CLIENT_ARTIFACT, + version: `${this.minecraftVersion}-${ForgeGradle3Adapter.WILDCARD_MCP_VERSION}`, + classifiers: ['srg'] + } + ] + this.wildcardsInUse = [ + ForgeGradle3Adapter.WILDCARD_MCP_VERSION + ] + return + } + + // Configure for 12 + if(VersionUtil.isVersionAcceptable(this.minecraftVersion, [12])) { + // NOTHING TO CONFIGURE + return + } } public async getModule(): Promise { return this.process() } - public isForVersion(version: MinecraftVersion): boolean { - return ForgeGradle3Adapter.isForVersion(version) + public isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { + return ForgeGradle3Adapter.isForVersion(version, libraryVersion) } private async process(): Promise { const libRepo = this.repoStructure.getLibRepoStruct() + + // Get Installer const installerPath = libRepo.getLocalForge(this.artifactVersion, 'installer') console.debug(`Checking for forge installer at ${installerPath}..`) if (!await libRepo.artifactExists(installerPath)) { @@ -51,6 +147,18 @@ export class ForgeGradle3Adapter extends ForgeResolver { } console.debug(`Beginning processing of Forge v${this.forgeVersion} (Minecraft ${this.minecraftVersion})`) + if(this.generatedFiles != null && this.generatedFiles.length > 0) { + // Run installer + return this.processWithInstaller(installerPath) + } else { + // Installer not required + return this.processWithoutInstaller(installerPath) + } + + } + + private async processWithInstaller(installerPath: string): Promise { + const workDir = this.repoStructure.getWorkDirectory() if (await pathExists(workDir)) { await remove(workDir) @@ -94,129 +202,67 @@ export class ForgeGradle3Adapter extends ForgeResolver { await remove(workDir) return forgeModule + } - private async processLibraries(manifest: VersionManifestFG3): Promise { + private async processVersionManifest(): Promise<[VersionManifestFG3, Module]> { + const workDir = this.repoStructure.getWorkDirectory() + const versionRepo = this.repoStructure.getVersionRepoStruct() + const versionName = versionRepo.getFileName(this.minecraftVersion, this.forgeVersion) + const versionManifestPath = join(workDir, 'versions', versionName, `${versionName}.json`) - const libDir = join(this.repoStructure.getWorkDirectory(), 'libraries') - const libRepo = this.repoStructure.getLibRepoStruct() + const versionManifestBuf = await readFile(versionManifestPath) + const versionManifest = JSON.parse(versionManifestBuf.toString()) as VersionManifestFG3 - const mdls: Module[] = [] - - for (const entry of manifest.libraries) { - const artifact = entry.downloads.artifact - if (artifact.url) { - - const targetLocalPath = join(libDir, artifact.path) - - if (!await pathExists(targetLocalPath)) { - throw new Error(`Expected library ${entry.name} not found!`) - } - - const components = MavenUtil.getMavenComponents(entry.name) - - mdls.push({ - id: entry.name, - name: `Minecraft Forge (${components.artifact})`, - type: Type.Library, - artifact: this.generateArtifact( - await readFile(targetLocalPath), - await lstat(targetLocalPath), - libRepo.getArtifactUrlByComponents( - this.baseUrl, - components.group, - components.artifact, - components.version, - components.classifier, - components.extension - ) - ) - }) - - const destination = libRepo.getArtifactByComponents( - components.group, - components.artifact, - components.version, - components.classifier, - components.extension - ) - - await move(targetLocalPath, destination, {overwrite: true}) - - } + const versionManifestModule: Module = { + id: this.artifactVersion, + name: 'Minecraft Forge (version.json)', + type: Type.VersionManifest, + artifact: this.generateArtifact( + versionManifestBuf, + await lstat(versionManifestPath), + versionRepo.getVersionManifestURL(this.baseUrl, this.minecraftVersion, this.forgeVersion) + ) } - return mdls + const destination = versionRepo.getVersionManifest( + this.minecraftVersion, + this.forgeVersion + ) + await move(versionManifestPath, destination, {overwrite: true}) + + return [versionManifest, versionManifestModule] } private async processForgeModule(versionManifest: VersionManifestFG3): Promise { const libDir = join(this.repoStructure.getWorkDirectory(), 'libraries') - const mcpVersion = this.getMCPVersion(versionManifest.arguments.game) + + if(this.wildcardsInUse) { + if(this.wildcardsInUse.indexOf(ForgeGradle3Adapter.WILDCARD_MCP_VERSION) > -1) { + + const mcpVersion = this.getMCPVersion(versionManifest.arguments.game) + if(mcpVersion == null) { + throw new Error('MCP Version not found.. did forge change their format?') + } + + this.generatedFiles = this.generatedFiles!.map(f => { + if(f.version.indexOf(ForgeGradle3Adapter.WILDCARD_MCP_VERSION) > -1) { + return { + ...f, + version: f.version.replace(ForgeGradle3Adapter.WILDCARD_MCP_VERSION, mcpVersion) + } + } + return f + }) - const generatedFiles = [ - { - name: 'base jar', - group: LibRepoStructure.FORGE_GROUP, - artifact: LibRepoStructure.FORGE_ARTIFACT, - version: this.artifactVersion, - classifiers: [undefined] - }, - { - name: 'universal jar', - group: LibRepoStructure.FORGE_GROUP, - artifact: LibRepoStructure.FORGE_ARTIFACT, - version: this.artifactVersion, - classifiers: ['universal'] - }, - { - name: 'client jar', - group: LibRepoStructure.FORGE_GROUP, - artifact: LibRepoStructure.FORGE_ARTIFACT, - version: this.artifactVersion, - classifiers: ['client'] - }, - { - name: 'client slim', - group: LibRepoStructure.MINECRAFT_GROUP, - artifact: LibRepoStructure.MINECRAFT_CLIENT_ARTIFACT, - version: this.minecraftVersion.toString(), - classifiers: [ - 'slim', - 'slim-stable' - ] - }, - { - name: 'client data', - group: LibRepoStructure.MINECRAFT_GROUP, - artifact: LibRepoStructure.MINECRAFT_CLIENT_ARTIFACT, - version: this.minecraftVersion.toString(), - classifiers: ['data'], - skipIfNotPresent: true - }, - { - name: 'client extra', - group: LibRepoStructure.MINECRAFT_GROUP, - artifact: LibRepoStructure.MINECRAFT_CLIENT_ARTIFACT, - version: this.minecraftVersion.toString(), - classifiers: [ - 'extra', - 'extra-stable' - ] - }, - { - name: 'client srg', - group: LibRepoStructure.MINECRAFT_GROUP, - artifact: LibRepoStructure.MINECRAFT_CLIENT_ARTIFACT, - version: `${this.minecraftVersion}-${mcpVersion}`, - classifiers: ['srg'] } - ] + } const mdls: Module[] = [] - for (const entry of generatedFiles) { + for (const entry of this.generatedFiles!) { const targetLocations: string[] = [] let located = false @@ -286,34 +332,58 @@ export class ForgeGradle3Adapter extends ForgeResolver { return forgeModule } - private async processVersionManifest(): Promise<[VersionManifestFG3, Module]> { - const workDir = this.repoStructure.getWorkDirectory() - const versionRepo = this.repoStructure.getVersionRepoStruct() - const versionName = versionRepo.getFileName(this.minecraftVersion, this.forgeVersion) - const versionManifestPath = join(workDir, 'versions', versionName, `${versionName}.json`) + private async processLibraries(manifest: VersionManifestFG3): Promise { - const versionManifestBuf = await readFile(versionManifestPath) - const versionManifest = JSON.parse(versionManifestBuf.toString()) as VersionManifestFG3 + const libDir = join(this.repoStructure.getWorkDirectory(), 'libraries') + const libRepo = this.repoStructure.getLibRepoStruct() - const versionManifestModule: Module = { - id: this.artifactVersion, - name: 'Minecraft Forge (version.json)', - type: Type.VersionManifest, - artifact: this.generateArtifact( - versionManifestBuf, - await lstat(versionManifestPath), - versionRepo.getVersionManifestURL(this.baseUrl, this.minecraftVersion, this.forgeVersion) - ) + const mdls: Module[] = [] + + for (const entry of manifest.libraries) { + const artifact = entry.downloads.artifact + if (artifact.url) { + + const targetLocalPath = join(libDir, artifact.path) + + if (!await pathExists(targetLocalPath)) { + throw new Error(`Expected library ${entry.name} not found!`) + } + + const components = MavenUtil.getMavenComponents(entry.name) + + mdls.push({ + id: entry.name, + name: `Minecraft Forge (${components.artifact})`, + type: Type.Library, + artifact: this.generateArtifact( + await readFile(targetLocalPath), + await lstat(targetLocalPath), + libRepo.getArtifactUrlByComponents( + this.baseUrl, + components.group, + components.artifact, + components.version, + components.classifier, + components.extension + ) + ) + }) + + const destination = libRepo.getArtifactByComponents( + components.group, + components.artifact, + components.version, + components.classifier, + components.extension + ) + + await move(targetLocalPath, destination, {overwrite: true}) + + } } - const destination = this.repoStructure.getVersionRepoStruct().getVersionManifest( - this.minecraftVersion, - this.forgeVersion - ) + return mdls - await move(versionManifestPath, destination, {overwrite: true}) - - return [versionManifest, versionManifestModule] } private executeInstaller(installerExec: string): Promise { @@ -342,4 +412,169 @@ export class ForgeGradle3Adapter extends ForgeResolver { return null } -} + private async processWithoutInstaller(installerPath: string): Promise { + + // Extract version.json from installer. + + const forgeInstallerBuffer = await readFile(installerPath) + const zip = new AdmZip(forgeInstallerBuffer) + const zipEntries = zip.getEntries() + + let versionManifest + + for (const entry of zipEntries) { + if (entry.entryName === 'version.json') { + versionManifest = zip.readAsText(entry) + break + } + } + + if (!versionManifest) { + throw new Error('Failed to find version.json in forge installer jar.') + } + + versionManifest = JSON.parse(versionManifest) as VersionManifestFG3 + + // Save Version Manifest + const versionManifestDest = this.repoStructure.getVersionRepoStruct().getVersionManifest( + this.minecraftVersion, + this.forgeVersion + ) + await mkdirs(dirname(versionManifestDest)) + await writeJson(versionManifestDest, versionManifest, { spaces: 4 }) + + const libRepo = this.repoStructure.getLibRepoStruct() + const universalLocalPath = libRepo.getLocalForge(this.artifactVersion, 'universal') + console.debug(`Checking for Forge Universal jar at ${universalLocalPath}..`) + + const forgeMdl = versionManifest.libraries.find(val => val.name.startsWith('net.minecraftforge:forge:')) + + if(forgeMdl == null) { + throw new Error('Forge entry not found in version.json!') + } + + let forgeUniversalBuffer + + // Check for local universal jar. + if (await libRepo.artifactExists(universalLocalPath)) { + const localUniBuf = await readFile(universalLocalPath) + const sha1 = createHash('sha1').update(localUniBuf).digest('hex') + if(sha1 !== forgeMdl.downloads.artifact.sha1) { + console.debug('SHA-1 of local universal jar does not match version.json entry.') + console.debug('Redownloading Forge Universal jar..') + } else { + console.debug('Using locally discovered forge.') + forgeUniversalBuffer = localUniBuf + } + } else { + console.debug('Forge Universal jar not found locally, initializing download..') + } + + // Download if local is missing or corrupt + if(!forgeUniversalBuffer) { + await libRepo.downloadArtifactByComponents( + this.REMOTE_REPOSITORY, + LibRepoStructure.FORGE_GROUP, + LibRepoStructure.FORGE_ARTIFACT, + this.artifactVersion, 'universal', 'jar') + forgeUniversalBuffer = await readFile(universalLocalPath) + } + + console.debug(`Beginning processing of Forge v${this.forgeVersion} (Minecraft ${this.minecraftVersion})`) + + const forgeModule: Module = { + id: MavenUtil.mavenComponentsToIdentifier( + LibRepoStructure.FORGE_GROUP, + LibRepoStructure.FORGE_ARTIFACT, + this.artifactVersion, 'universal' + ), + name: 'Minecraft Forge', + type: Type.ForgeHosted, + artifact: this.generateArtifact( + forgeUniversalBuffer, + await lstat(universalLocalPath), + libRepo.getArtifactUrlByComponents( + this.baseUrl, + LibRepoStructure.FORGE_GROUP, + LibRepoStructure.FORGE_ARTIFACT, + this.artifactVersion, 'universal' + ) + ), + subModules: [] + } + + // Attach Version Manifest module. + forgeModule.subModules?.push({ + id: this.artifactVersion, + name: 'Minecraft Forge (version.json)', + type: Type.VersionManifest, + artifact: this.generateArtifact( + await readFile(versionManifestDest), + await lstat(versionManifestDest), + this.repoStructure.getVersionRepoStruct().getVersionManifestURL( + this.baseUrl, this.minecraftVersion, this.forgeVersion) + ) + }) + + for(const lib of versionManifest.libraries) { + if (lib.name.startsWith('net.minecraftforge:forge:')) { + // We've already processed forge. + continue + } + console.debug(`Processing ${lib.name}..`) + + const extension = 'jar' + const localPath = libRepo.getArtifactById(lib.name, extension) + + let queueDownload = !await libRepo.artifactExists(localPath) + let libBuf + + if (!queueDownload) { + libBuf = await readFile(localPath) + const sha1 = createHash('sha1').update(libBuf).digest('hex') + if (sha1 !== lib.downloads.artifact.sha1) { + console.debug('Hashes do not match, redownloading..') + queueDownload = true + } + } else { + console.debug('Not found locally, downloading..') + queueDownload = true + } + + if (queueDownload) { + await libRepo.downloadArtifactDirect(lib.downloads.artifact.url, lib.downloads.artifact.path) + libBuf = await readFile(localPath) + } else { + console.debug('Using local copy.') + } + + const stats = await lstat(localPath) + + const mavenComponents = MavenUtil.getMavenComponents(lib.name) + const properId = MavenUtil.mavenComponentsToIdentifier( + mavenComponents.group, mavenComponents.artifact, mavenComponents.version, + mavenComponents.classifier, extension + ) + + forgeModule.subModules?.push({ + id: properId, + name: `Minecraft Forge (${mavenComponents?.artifact})`, + type: Type.Library, + artifact: this.generateArtifact( + libBuf as Buffer, + stats, + libRepo.getArtifactUrlByComponents( + this.baseUrl, + mavenComponents.group, mavenComponents.artifact, + mavenComponents.version, mavenComponents.classifier, extension + ) + ) + }) + + } + + return forgeModule + + } + +} \ No newline at end of file diff --git a/src/resolver/forge/forge.resolver.ts b/src/resolver/forge/forge.resolver.ts index c2a7fa0..670dd48 100644 --- a/src/resolver/forge/forge.resolver.ts +++ b/src/resolver/forge/forge.resolver.ts @@ -7,6 +7,7 @@ import { MinecraftVersion } from '../../util/MinecraftVersion' export abstract class ForgeResolver extends BaseResolver { + protected readonly MOJANG_REMOTE_REPOSITORY = 'https://libraries.minecraft.net/' protected readonly REMOTE_REPOSITORY = 'https://files.minecraftforge.net/maven/' protected repoStructure: RepoStructure diff --git a/src/util/VersionSegmented.ts b/src/util/VersionSegmented.ts index 64a4cf1..9a57d97 100644 --- a/src/util/VersionSegmented.ts +++ b/src/util/VersionSegmented.ts @@ -2,6 +2,6 @@ import { MinecraftVersion } from './MinecraftVersion' export interface VersionSegmented { - isForVersion(version: MinecraftVersion): boolean + isForVersion(version: MinecraftVersion, libraryVersion: string): boolean } diff --git a/src/util/VersionSegmentedRegistry.ts b/src/util/VersionSegmentedRegistry.ts index 7ab1419..b016970 100644 --- a/src/util/VersionSegmentedRegistry.ts +++ b/src/util/VersionSegmentedRegistry.ts @@ -26,7 +26,7 @@ export class VersionSegmentedRegistry { baseURL: string ): ForgeResolver { for (const impl of VersionSegmentedRegistry.FORGE_ADAPTER_IMPL) { - if (impl.isForVersion(minecraftVersion)) { + if (impl.isForVersion(minecraftVersion, forgeVersion)) { return new impl(absoluteRoot, relativeRoot, baseURL, minecraftVersion, forgeVersion) } } @@ -35,12 +35,13 @@ export class VersionSegmentedRegistry { public static getForgeModStruct( minecraftVersion: MinecraftVersion, + forgeVersion: string, absoluteRoot: string, relativeRoot: string, baseUrl: string ): BaseForgeModStructure { for (const impl of VersionSegmentedRegistry.FORGEMOD_STRUCT_IML) { - if (impl.isForVersion(minecraftVersion)) { + if (impl.isForVersion(minecraftVersion, forgeVersion)) { return new impl(absoluteRoot, relativeRoot, baseUrl) } } diff --git a/src/util/versionutil.ts b/src/util/versionutil.ts index 8adefc1..39849bf 100644 --- a/src/util/versionutil.ts +++ b/src/util/versionutil.ts @@ -18,6 +18,19 @@ export class VersionUtil { return false } + public static isOneDotTwelveFG2(libraryVersion: string): boolean { + const maxFG2 = [14, 23, 5, 2847] + const verSplit = libraryVersion.split('.').map(v => Number(v)) + + for(let i=0; i maxFG2[i]) { + return false + } + } + + return true + } + public static isPromotionVersion(version: string): boolean { return VersionUtil.PROMOTION_TYPE.indexOf(version.toLowerCase()) > -1 }