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:
5
.vscode/tasks.json
vendored
5
.vscode/tasks.json
vendored
@@ -9,7 +9,10 @@
|
|||||||
"tsconfig": "tsconfig.json",
|
"tsconfig": "tsconfig.json",
|
||||||
"problemMatcher": [
|
"problemMatcher": [
|
||||||
"$tsc"
|
"$tsc"
|
||||||
]
|
],
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "silent"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,8 @@ const testCommand: yargs.CommandModule = {
|
|||||||
console.debug(`Invoked test with mcVer ${argv.mcVer} forgeVer ${argv.forgeVer}`)
|
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')
|
const resolver = ResolverRegistry.getForgeResolver('1.12.2', '14.23.5.2847', 'D:/TestRoot2', 'D:/TestRoot2')
|
||||||
if (resolver != null) {
|
if (resolver != null) {
|
||||||
await resolver.getModule()
|
const mdl = await resolver.getModule()
|
||||||
|
console.log(mdl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/model/forge/versionmanifest.ts
Normal file
20
src/model/forge/versionmanifest.ts
Normal 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
|
||||||
|
}>
|
||||||
|
|
||||||
|
}
|
||||||
@@ -43,8 +43,8 @@ export abstract class ModuleStructure extends BaseModelStructure<Module> {
|
|||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const filePath = resolve(this.containerDirectory, file)
|
const filePath = resolve(this.containerDirectory, file)
|
||||||
const stats = await lstat(filePath)
|
const stats = await lstat(filePath)
|
||||||
const buf = await readFile(filePath)
|
|
||||||
if (stats.isFile()) {
|
if (stats.isFile()) {
|
||||||
|
const buf = await readFile(filePath)
|
||||||
const mdl: Module = {
|
const mdl: Module = {
|
||||||
id: await this.getModuleId(file, filePath, stats, buf),
|
id: await this.getModuleId(file, filePath, stats, buf),
|
||||||
name: await this.getModuleName(file, filePath, stats, buf),
|
name: await this.getModuleName(file, filePath, stats, buf),
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ export abstract class BaseMavenRepo extends BaseFileStructure {
|
|||||||
super(absoluteRoot, relativeRoot, structRoot)
|
super(absoluteRoot, relativeRoot, structRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
public getArtifactById(mavenIdentifier: string): string | null {
|
public getArtifactById(mavenIdentifier: string, extension?: string): string | null {
|
||||||
const resolved = MavenUtil.mavenIdentifierToString(mavenIdentifier)
|
const resolved = MavenUtil.mavenIdentifierToString(mavenIdentifier, extension)
|
||||||
return resolved == null ? null : resolve(this.containerDirectory, resolved)
|
return resolved == null ? null : resolve(this.containerDirectory, resolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +30,17 @@ export abstract class BaseMavenRepo extends BaseFileStructure {
|
|||||||
return pathExists(path)
|
return pathExists(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async downloadArtifact(url: string, group: string, artifact: string, version: string,
|
public async downloadArtifactById(url: string, mavenIdentifier: string, extension?: string) {
|
||||||
classifier?: string, extension?: string) {
|
return this.downloadArtifactBase(url, MavenUtil.mavenIdentifierToString(mavenIdentifier, extension) as string)
|
||||||
const relative = MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension)
|
}
|
||||||
|
|
||||||
|
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()
|
const resolvedURL = resolveURL(url, relative).toString()
|
||||||
console.debug(`Downloading ${resolvedURL}..`)
|
console.debug(`Downloading ${resolvedURL}..`)
|
||||||
const response = await axios({
|
const response = await axios({
|
||||||
|
|||||||
@@ -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 { Module } from '../../../model/spec/module'
|
||||||
|
import { Type } from '../../../model/spec/type'
|
||||||
import { ForgeRepoStructure } from '../../../model/struct/repo/forgerepo.struct'
|
import { ForgeRepoStructure } from '../../../model/struct/repo/forgerepo.struct'
|
||||||
|
import { MavenUtil } from '../../../util/maven'
|
||||||
import { ForgeResolver } from '../forge.resolver'
|
import { ForgeResolver } from '../forge.resolver'
|
||||||
|
|
||||||
export class Forge18Adapter extends ForgeResolver {
|
export class Forge18Adapter extends ForgeResolver {
|
||||||
@@ -18,8 +25,7 @@ export class Forge18Adapter extends ForgeResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getModule(): Promise<Module> {
|
public async getModule(): Promise<Module> {
|
||||||
await this.getForgeByVersion()
|
return this.getForgeByVersion()
|
||||||
return null as unknown as Module
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public isForVersion(version: string) {
|
public isForVersion(version: string) {
|
||||||
@@ -33,7 +39,7 @@ export class Forge18Adapter extends ForgeResolver {
|
|||||||
console.debug(`Checking for forge version at ${targetLocalPath}..`)
|
console.debug(`Checking for forge version at ${targetLocalPath}..`)
|
||||||
if (!await forgeRepo.artifactExists(targetLocalPath)) {
|
if (!await forgeRepo.artifactExists(targetLocalPath)) {
|
||||||
console.debug(`Forge not found locally, initializing download..`)
|
console.debug(`Forge not found locally, initializing download..`)
|
||||||
await forgeRepo.downloadArtifact(
|
await forgeRepo.downloadArtifactByComponents(
|
||||||
this.REMOTE_REPOSITORY,
|
this.REMOTE_REPOSITORY,
|
||||||
ForgeRepoStructure.FORGE_GROUP,
|
ForgeRepoStructure.FORGE_GROUP,
|
||||||
ForgeRepoStructure.FORGE_ARTIFACT,
|
ForgeRepoStructure.FORGE_ARTIFACT,
|
||||||
@@ -42,11 +48,81 @@ export class Forge18Adapter extends ForgeResolver {
|
|||||||
console.debug('Using locally discovered forge.')
|
console.debug('Using locally discovered forge.')
|
||||||
}
|
}
|
||||||
console.debug(`Beginning processing of Forge v${this.forgeVersion} (Minecraft ${this.minecraftVersion})`)
|
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
|
private generateArtifact(buf: Buffer, stats: Stats): Artifact {
|
||||||
// extract manifest
|
return {
|
||||||
// parse manifest
|
size: stats.size,
|
||||||
// return module
|
MD5: createHash('md5').update(buf).digest('hex'),
|
||||||
|
url: 'TODO'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private determineExtension(checksums: string[] | undefined) {
|
||||||
|
return checksums != null && checksums.length > 1 ? 'jar.pack.xz' : 'jar'
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,16 @@ export class MavenUtil {
|
|||||||
public static readonly ID_REGEX = /(.+):(.+):([^@]+)()(?:@{1}(.+)$)?/
|
public static readonly ID_REGEX = /(.+):(.+):([^@]+)()(?:@{1}(.+)$)?/
|
||||||
public static readonly ID_REGEX_WITH_CLASSIFIER = /(.+):(.+):(?:([^@]+)(?:-([a-zA-Z]+)))(?:@{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)
|
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)) {
|
if (!MavenUtil.isMavenIdentifier(id)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -24,17 +29,29 @@ export class MavenUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
const group = result[1]
|
return {
|
||||||
const artifact = result[2]
|
group: result[1],
|
||||||
const version = result[3]
|
artifact: result[2],
|
||||||
const classifier = result[4] || undefined
|
version: result[3],
|
||||||
const ext = result[5] || extension
|
classifier: result[4] || undefined,
|
||||||
|
extension: result[5] || extension
|
||||||
return MavenUtil.mavenComponentsToString(group, artifact, version, classifier, ext)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
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,
|
public static mavenComponentsToString(group: string, artifact: string, version: string,
|
||||||
classifier?: string, extension = 'jar') {
|
classifier?: string, extension = 'jar') {
|
||||||
return `${group.replace(/\./g, '/')}/${artifact}/${version}/${artifact}-${version}${classifier != null ? `-${classifier}` : ''}.${extension}`
|
return `${group.replace(/\./g, '/')}/${artifact}/${version}/${artifact}-${version}${classifier != null ? `-${classifier}` : ''}.${extension}`
|
||||||
|
|||||||
Reference in New Issue
Block a user