Parse version manifest and download forge libraries.

TODO:
Integrate base url propagation.
Integrate PackXZExtract to calculate hashes  of jar.pack.xz files.
OR ammend the distribution spec to accept different hash algos (requires helioslauncher update).
This commit is contained in:
Daniel Scalzi
2020-01-12 03:42:34 -05:00
parent 419a4d5e91
commit 0674bd5808
7 changed files with 149 additions and 24 deletions

5
.vscode/tasks.json vendored
View File

@@ -9,7 +9,10 @@
"tsconfig": "tsconfig.json",
"problemMatcher": [
"$tsc"
]
],
"presentation": {
"reveal": "silent"
}
}
]
}

View File

@@ -178,7 +178,8 @@ const testCommand: yargs.CommandModule = {
console.debug(`Invoked test with mcVer ${argv.mcVer} forgeVer ${argv.forgeVer}`)
const resolver = ResolverRegistry.getForgeResolver('1.12.2', '14.23.5.2847', 'D:/TestRoot2', 'D:/TestRoot2')
if (resolver != null) {
await resolver.getModule()
const mdl = await resolver.getModule()
console.log(mdl)
}
}
}

View File

@@ -0,0 +1,20 @@
export interface VersionManifest {
id: string
time: string
releaseTime: string
type: string
minecraftArguments: string
mainClass: string
inheritsFrom: string
jar: string
logging: any
libraries: Array<{
name: string,
url?: string,
checksums?: string[],
serverreq?: boolean,
clientreq?: boolean
}>
}

View File

@@ -43,8 +43,8 @@ export abstract class ModuleStructure extends BaseModelStructure<Module> {
for (const file of files) {
const filePath = resolve(this.containerDirectory, file)
const stats = await lstat(filePath)
const buf = await readFile(filePath)
if (stats.isFile()) {
const buf = await readFile(filePath)
const mdl: Module = {
id: await this.getModuleId(file, filePath, stats, buf),
name: await this.getModuleName(file, filePath, stats, buf),

View File

@@ -15,8 +15,8 @@ export abstract class BaseMavenRepo extends BaseFileStructure {
super(absoluteRoot, relativeRoot, structRoot)
}
public getArtifactById(mavenIdentifier: string): string | null {
const resolved = MavenUtil.mavenIdentifierToString(mavenIdentifier)
public getArtifactById(mavenIdentifier: string, extension?: string): string | null {
const resolved = MavenUtil.mavenIdentifierToString(mavenIdentifier, extension)
return resolved == null ? null : resolve(this.containerDirectory, resolved)
}
@@ -30,9 +30,17 @@ export abstract class BaseMavenRepo extends BaseFileStructure {
return pathExists(path)
}
public async downloadArtifact(url: string, group: string, artifact: string, version: string,
classifier?: string, extension?: string) {
const relative = MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension)
public async downloadArtifactById(url: string, mavenIdentifier: string, extension?: string) {
return this.downloadArtifactBase(url, MavenUtil.mavenIdentifierToString(mavenIdentifier, extension) as string)
}
public async downloadArtifactByComponents(url: string, group: string, artifact: string, version: string,
classifier?: string, extension?: string) {
return this.downloadArtifactBase(url,
MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension))
}
private async downloadArtifactBase(url: string, relative: string) {
const resolvedURL = resolveURL(url, relative).toString()
console.debug(`Downloading ${resolvedURL}..`)
const response = await axios({

View File

@@ -1,5 +1,12 @@
import AdmZip from 'adm-zip'
import { createHash } from 'crypto'
import { lstat, readFile, Stats } from 'fs-extra'
import { VersionManifest } from '../../../model/forge/versionmanifest'
import { Artifact } from '../../../model/spec/artifact'
import { Module } from '../../../model/spec/module'
import { Type } from '../../../model/spec/type'
import { ForgeRepoStructure } from '../../../model/struct/repo/forgerepo.struct'
import { MavenUtil } from '../../../util/maven'
import { ForgeResolver } from '../forge.resolver'
export class Forge18Adapter extends ForgeResolver {
@@ -18,8 +25,7 @@ export class Forge18Adapter extends ForgeResolver {
}
public async getModule(): Promise<Module> {
await this.getForgeByVersion()
return null as unknown as Module
return this.getForgeByVersion()
}
public isForVersion(version: string) {
@@ -33,7 +39,7 @@ export class Forge18Adapter extends ForgeResolver {
console.debug(`Checking for forge version at ${targetLocalPath}..`)
if (!await forgeRepo.artifactExists(targetLocalPath)) {
console.debug(`Forge not found locally, initializing download..`)
await forgeRepo.downloadArtifact(
await forgeRepo.downloadArtifactByComponents(
this.REMOTE_REPOSITORY,
ForgeRepoStructure.FORGE_GROUP,
ForgeRepoStructure.FORGE_ARTIFACT,
@@ -42,11 +48,81 @@ export class Forge18Adapter extends ForgeResolver {
console.debug('Using locally discovered forge.')
}
console.debug(`Beginning processing of Forge v${this.forgeVersion} (Minecraft ${this.minecraftVersion})`)
const forgeUniversalBuffer = await readFile(targetLocalPath)
const zip = new AdmZip(forgeUniversalBuffer)
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 universal jar.')
}
versionManifest = JSON.parse(versionManifest) as VersionManifest
const forgeModule: Module = {
id: MavenUtil.mavenComponentsToIdentifier(
ForgeRepoStructure.FORGE_GROUP,
ForgeRepoStructure.FORGE_ARTIFACT,
artifactVersion, 'universal'
),
name: 'Minecraft Forge',
type: Type.ForgeHosted,
artifact: this.generateArtifact(forgeUniversalBuffer, await lstat(targetLocalPath)),
subModules: []
}
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 libRepo = this.repoStructure.getLibRepoStruct()
const extension = this.determineExtension(lib.checksums)
const localPath = libRepo.getArtifactById(lib.name, extension) as string
if (!await libRepo.artifactExists(localPath)) {
console.debug(`Not found locally, downloading..`)
await libRepo.downloadArtifactById(lib.url || 'https://libraries.minecraft.net/', lib.name, extension)
} else {
console.debug('Using local copy.')
}
const libBuf = await readFile(localPath)
const stats = await lstat(localPath)
forgeModule.subModules?.push({
id: lib.name,
name: `Minecraft Forge (${MavenUtil.getMavenComponents(lib.name)?.artifact})`,
type: Type.Library,
artifact: this.generateArtifact(libBuf, stats)
})
}
return forgeModule
}
// TODO
// extract manifest
// parse manifest
// return module
private generateArtifact(buf: Buffer, stats: Stats): Artifact {
return {
size: stats.size,
MD5: createHash('md5').update(buf).digest('hex'),
url: 'TODO'
}
}
private determineExtension(checksums: string[] | undefined) {
return checksums != null && checksums.length > 1 ? 'jar.pack.xz' : 'jar'
}
}

View File

@@ -6,11 +6,16 @@ export class MavenUtil {
public static readonly ID_REGEX = /(.+):(.+):([^@]+)()(?:@{1}(.+)$)?/
public static readonly ID_REGEX_WITH_CLASSIFIER = /(.+):(.+):(?:([^@]+)(?:-([a-zA-Z]+)))(?:@{1}(.+)$)?/
public static isMavenIdentifier(id: string) {
public static mavenComponentsToIdentifier(group: string, artifact: string, version: string,
classifier?: string, extension?: string) {
return `${group}:${artifact}:${version}${classifier != null ? `-${classifier}` : ''}${extension != null ? `@${extension}` : ''}`
}
public static isMavenIdentifier(id: string): boolean {
return MavenUtil.ID_REGEX.test(id) || MavenUtil.ID_REGEX_WITH_CLASSIFIER.test(id)
}
public static mavenIdentifierToString(id: string, extension = 'jar') {
public static getMavenComponents(id: string, extension = 'jar') {
if (!MavenUtil.isMavenIdentifier(id)) {
return null
}
@@ -24,17 +29,29 @@ export class MavenUtil {
}
if (result != null) {
const group = result[1]
const artifact = result[2]
const version = result[3]
const classifier = result[4] || undefined
const ext = result[5] || extension
return MavenUtil.mavenComponentsToString(group, artifact, version, classifier, ext)
return {
group: result[1],
artifact: result[2],
version: result[3],
classifier: result[4] || undefined,
extension: result[5] || extension
}
}
return null
}
public static mavenIdentifierToString(id: string, extension = 'jar') {
const tmp = MavenUtil.getMavenComponents(id, extension)
if (tmp != null) {
return MavenUtil.mavenComponentsToString(tmp.group, tmp.artifact, tmp.version,
tmp.classifier, tmp.extension)
} else {
return null
}
}
public static mavenComponentsToString(group: string, artifact: string, version: string,
classifier?: string, extension = 'jar') {
return `${group.replace(/\./g, '/')}/${artifact}/${version}/${artifact}-${version}${classifier != null ? `-${classifier}` : ''}.${extension}`