Completed steps up to downloading forge universal jar for 1.8-1.12.

Next step is processing the version.json and transforming it into a deliverable module.
This commit is contained in:
Daniel Scalzi
2020-01-12 01:36:36 -05:00
parent baea8e657c
commit 419a4d5e91
14 changed files with 251 additions and 30 deletions

7
.vscode/launch.json vendored
View File

@@ -8,7 +8,12 @@
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"name": "Launch Program", "name": "Launch Program",
"program": "${workspaceFolder}\\dist\\index.js", "program": "${workspaceFolder}\\src\\index.ts",
"args": [
"test",
"1.12.2",
"14.23.5.2847"
],
"preLaunchTask": "compile", "preLaunchTask": "compile",
"outFiles": [ "outFiles": [
"${workspaceFolder}/dist/**/*.js" "${workspaceFolder}/dist/**/*.js"

29
package-lock.json generated
View File

@@ -96,6 +96,14 @@
"sprintf-js": "~1.0.2" "sprintf-js": "~1.0.2"
} }
}, },
"axios": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.1.tgz",
"integrity": "sha512-Yl+7nfreYKaLRvAvjNPkvfjnQHJM1yLBY3zhqAwcJSwR/6ETkanUgylgtIvkvz0xJ+p/vZuNw8X7Hnb7Whsbpw==",
"requires": {
"follow-redirects": "1.5.10"
}
},
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -195,6 +203,14 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true "dev": true
}, },
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"decamelize": { "decamelize": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
@@ -238,6 +254,14 @@
"path-exists": "^4.0.0" "path-exists": "^4.0.0"
} }
}, },
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"requires": {
"debug": "=3.1.0"
}
},
"fs-extra": { "fs-extra": {
"version": "8.1.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
@@ -361,6 +385,11 @@
"minimist": "0.0.8" "minimist": "0.0.8"
} }
}, },
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",

View File

@@ -36,6 +36,7 @@
}, },
"dependencies": { "dependencies": {
"adm-zip": "^0.4.13", "adm-zip": "^0.4.13",
"axios": "^0.19.1",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",
"yargs": "^15.1.0" "yargs": "^15.1.0"
} }

View File

@@ -1,8 +1,10 @@
/* tslint:disable:no-shadowed-variable */ /* tslint:disable:no-shadowed-variable */
import { writeFile } from 'fs-extra' import { writeFile } from 'fs-extra'
import { resolve as resolvePath } from 'path' import { resolve as resolvePath } from 'path'
import { URL } from 'url'
import yargs from 'yargs' import yargs from 'yargs'
import { DistributionStructure } from './model/struct/model/distribution.struct' import { DistributionStructure } from './model/struct/model/distribution.struct'
import { ResolverRegistry } from './resolver/ResolverRegistry'
function rootOption(yargs: yargs.Argv) { function rootOption(yargs: yargs.Argv) {
return yargs.option('root', { return yargs.option('root', {
@@ -166,6 +168,21 @@ const validateCommand: yargs.CommandModule = {
} }
} }
const testCommand: yargs.CommandModule = {
command: 'test <mcVer> <forgeVer>',
describe: 'Validate a distribution.json against the spec.',
builder: (yargs) => {
return namePositional(yargs)
},
handler: async (argv) => {
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()
}
}
}
// Registering yargs configuration. // Registering yargs configuration.
// tslint:disable-next-line:no-unused-expression // tslint:disable-next-line:no-unused-expression
yargs yargs
@@ -174,6 +191,7 @@ yargs
.command(initCommand) .command(initCommand)
.command(generateCommand) .command(generateCommand)
.command(validateCommand) .command(validateCommand)
.command(testCommand)
.demandCommand() .demandCommand()
.help() .help()
.argv .argv

View File

@@ -1,4 +1,7 @@
import { resolve } from 'path' import axios from 'axios'
import { createWriteStream, mkdirs, pathExists } from 'fs-extra'
import { dirname, resolve } from 'path'
import { resolve as resolveURL } from 'url'
import { MavenUtil } from '../../../util/maven' import { MavenUtil } from '../../../util/maven'
import { BaseFileStructure } from '../BaseFileStructure' import { BaseFileStructure } from '../BaseFileStructure'
@@ -19,8 +22,36 @@ export abstract class BaseMavenRepo extends BaseFileStructure {
public getArtifactByComponents(group: string, artifact: string, version: string, public getArtifactByComponents(group: string, artifact: string, version: string,
classifier?: string, extension = 'jar'): string { classifier?: string, extension = 'jar'): string {
throw resolve(this.containerDirectory, return resolve(this.containerDirectory,
MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension)) MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension))
} }
public async artifactExists(path: string) {
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)
const resolvedURL = resolveURL(url, relative).toString()
console.debug(`Downloading ${resolvedURL}..`)
const response = await axios({
method: 'get',
url: resolvedURL,
responseType: 'stream'
})
const localPath = resolve(this.containerDirectory, relative)
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}.`)
resolve()
})
writer.on('error', reject)
})
}
} }

View File

@@ -2,6 +2,9 @@ import { BaseMavenRepo } from './BaseMavenRepo'
export class ForgeRepoStructure extends BaseMavenRepo { export class ForgeRepoStructure extends BaseMavenRepo {
public static readonly FORGE_GROUP = 'net.minecraftforge'
public static readonly FORGE_ARTIFACT = 'forge'
constructor( constructor(
absoluteRoot: string, absoluteRoot: string,
relativeRoot: string relativeRoot: string
@@ -10,7 +13,10 @@ export class ForgeRepoStructure extends BaseMavenRepo {
} }
public getLocalForge(version: string, classifier?: string) { public getLocalForge(version: string, classifier?: string) {
this.getArtifactByComponents('net.minecraftforge', 'forge', version, classifier, 'jar') return this.getArtifactByComponents(
ForgeRepoStructure.FORGE_GROUP,
ForgeRepoStructure.FORGE_ARTIFACT,
version, classifier, 'jar')
} }
} }

View File

@@ -0,0 +1,23 @@
import { Forge113Adapter } from './forge/adapter/forge113.resolver'
import { Forge18Adapter } from './forge/adapter/forge18.resolver'
import { ForgeResolver } from './forge/forge.resolver'
export class ResolverRegistry {
public static readonly FORGE_ADAPTER_IMPL = [
Forge18Adapter,
Forge113Adapter
]
public static getForgeResolver(
minecraftVersion: string,
forgeVersion: string,
absoluteRoot: string, relativeRoot: string): ForgeResolver | undefined {
for (const impl of ResolverRegistry.FORGE_ADAPTER_IMPL) {
if (impl.isForVersion(minecraftVersion)) {
return new impl(absoluteRoot, relativeRoot, minecraftVersion, forgeVersion)
}
}
}
}

View File

@@ -1,8 +1,17 @@
import { Module } from '../model/spec/module' import { Module } from '../model/spec/module'
import { VersionUtil } from '../util/versionutil'
import { Resolver } from './resolver' import { Resolver } from './resolver'
export abstract class BaseResolver implements Resolver { export abstract class BaseResolver implements Resolver {
protected static isVersionAcceptable(version: string, acceptable: number[]): boolean {
const versionComponents = VersionUtil.getMinecraftVersionComponents(version)
if (versionComponents != null && versionComponents.major === 1) {
return acceptable.find((element) => versionComponents.minor === element) != null
}
return false
}
constructor( constructor(
protected absoluteRoot: string, protected absoluteRoot: string,
protected relativeRoot: string protected relativeRoot: string
@@ -10,4 +19,6 @@ export abstract class BaseResolver implements Resolver {
public abstract getModule(): Promise<Module> public abstract getModule(): Promise<Module>
public abstract isForVersion(version: string): boolean
} }

View File

@@ -0,0 +1,27 @@
import { Module } from '../../../model/spec/module'
import { ForgeResolver } from '../forge.resolver'
export class Forge113Adapter extends ForgeResolver {
public static isForVersion(version: string) {
return Forge113Adapter.isVersionAcceptable(version, [13, 14, 15])
}
constructor(
absoluteRoot: string,
relativeRoot: string,
minecraftVersion: string,
forgeVersion: string
) {
super(absoluteRoot, relativeRoot, minecraftVersion, forgeVersion)
}
public async getModule(): Promise<Module> {
return null as unknown as Module
}
public isForVersion(version: string): boolean {
return Forge113Adapter.isForVersion(version)
}
}

View File

@@ -1,10 +1,52 @@
import { Module } from '../../../model/spec/module' import { Module } from '../../../model/spec/module'
import { ForgeRepoStructure } from '../../../model/struct/repo/forgerepo.struct'
import { ForgeResolver } from '../forge.resolver' import { ForgeResolver } from '../forge.resolver'
export class Forge18Adapter extends ForgeResolver { export class Forge18Adapter extends ForgeResolver {
public static isForVersion(version: string) {
return Forge18Adapter.isVersionAcceptable(version, [8, 9, 10, 11, 12])
}
constructor(
absoluteRoot: string,
relativeRoot: string,
minecraftVersion: string,
forgeVersion: string
) {
super(absoluteRoot, relativeRoot, minecraftVersion, forgeVersion)
}
public async getModule(): Promise<Module> { public async getModule(): Promise<Module> {
await this.getForgeByVersion()
return null as unknown as Module return null as unknown as Module
} }
public isForVersion(version: string) {
return Forge18Adapter.isForVersion(version)
}
public async getForgeByVersion() {
const forgeRepo = this.repoStructure.getForgeRepoStruct()
const artifactVersion = `${this.minecraftVersion}-${this.forgeVersion}`
const targetLocalPath = forgeRepo.getLocalForge(artifactVersion, 'universal')
console.debug(`Checking for forge version at ${targetLocalPath}..`)
if (!await forgeRepo.artifactExists(targetLocalPath)) {
console.debug(`Forge not found locally, initializing download..`)
await forgeRepo.downloadArtifact(
this.REMOTE_REPOSITORY,
ForgeRepoStructure.FORGE_GROUP,
ForgeRepoStructure.FORGE_ARTIFACT,
artifactVersion, 'universal', 'jar')
} else {
console.debug('Using locally discovered forge.')
}
console.debug(`Beginning processing of Forge v${this.forgeVersion} (Minecraft ${this.minecraftVersion})`)
}
// TODO
// extract manifest
// parse manifest
// return module
} }

View File

@@ -1,33 +1,20 @@
import { Module } from '../../model/spec/module'
import { RepoStructure } from '../../model/struct/repo/repo.struct' import { RepoStructure } from '../../model/struct/repo/repo.struct'
import { BaseResolver } from '../baseresolver' import { BaseResolver } from '../baseresolver'
import { Forge18Adapter } from './adapter/forge18.resolver'
export abstract class ForgeResolver extends BaseResolver { export abstract class ForgeResolver extends BaseResolver {
public static getResolver(version: string) { protected readonly REMOTE_REPOSITORY = 'https://files.minecraftforge.net/maven/'
return ForgeResolver.ADAPTER_LIST[version]
}
// tslint:disable: object-literal-key-quotes
private static readonly ADAPTER_LIST: {[version: string]: any} = {
'1.8': Forge18Adapter,
'1.9': Forge18Adapter,
'1.10': Forge18Adapter,
'1.11': Forge18Adapter,
'1.12': Forge18Adapter
}
protected repoStructure: RepoStructure protected repoStructure: RepoStructure
constructor( constructor(
absoluteRoot: string, absoluteRoot: string,
relativeRoot: string relativeRoot: string,
protected minecraftVersion: string,
protected forgeVersion: string
) { ) {
super(absoluteRoot, relativeRoot) super(absoluteRoot, relativeRoot)
this.repoStructure = new RepoStructure(absoluteRoot, relativeRoot) this.repoStructure = new RepoStructure(absoluteRoot, relativeRoot)
} }
public abstract getModule(): Promise<Module>
} }

View File

@@ -3,10 +3,11 @@ import { URL } from 'url'
export class MavenUtil { export class MavenUtil {
public static readonly ID_REGEX = /(.+):(.+):([^@-]+)(?:-{1}([^@]+))?(?:@{1}(.+)$)?/ public static readonly ID_REGEX = /(.+):(.+):([^@]+)()(?:@{1}(.+)$)?/
public static readonly ID_REGEX_WITH_CLASSIFIER = /(.+):(.+):(?:([^@]+)(?:-([a-zA-Z]+)))(?:@{1}(.+)$)?/
public static isMavenIdentifier(id: string) { public static isMavenIdentifier(id: string) {
return MavenUtil.ID_REGEX.test(id) return MavenUtil.ID_REGEX.test(id) || MavenUtil.ID_REGEX_WITH_CLASSIFIER.test(id)
} }
public static mavenIdentifierToString(id: string, extension = 'jar') { public static mavenIdentifierToString(id: string, extension = 'jar') {
@@ -14,12 +15,19 @@ export class MavenUtil {
return null return null
} }
const result = MavenUtil.ID_REGEX.exec(id) let result
if (MavenUtil.ID_REGEX_WITH_CLASSIFIER.test(id)) {
result = MavenUtil.ID_REGEX_WITH_CLASSIFIER.exec(id)
} else {
result = MavenUtil.ID_REGEX.exec(id)
}
if (result != null) { if (result != null) {
const group = result[1] const group = result[1]
const artifact = result[2] const artifact = result[2]
const version = result[3] const version = result[3]
const classifier = result[4] const classifier = result[4] || undefined
const ext = result[5] || extension const ext = result[5] || extension
return MavenUtil.mavenComponentsToString(group, artifact, version, classifier, ext) return MavenUtil.mavenComponentsToString(group, artifact, version, classifier, ext)
@@ -27,19 +35,29 @@ export class MavenUtil {
return null 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}`
} }
public static mavenToUrl(id: string, extension = 'jar') { public static mavenIdentifierToUrl(id: string, extension = 'jar') {
const res = MavenUtil.mavenIdentifierToString(id, extension) const res = MavenUtil.mavenIdentifierToString(id, extension)
return res == null ? null : new URL(res) return res == null ? null : new URL(res)
} }
public static mavenToPath(id: string, extension = 'jar') { public static mavenComponentsToUrl(group: string, artifact: string, version: string,
classifier?: string, extension = 'jar') {
return new URL(MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension))
}
public static mavenIdentifierToPath(id: string, extension = 'jar') {
const res = MavenUtil.mavenIdentifierToString(id, extension) const res = MavenUtil.mavenIdentifierToString(id, extension)
return res == null ? null : normalize(res) return res == null ? null : normalize(res)
} }
public static mavenComponentsToPath(group: string, artifact: string, version: string,
classifier?: string, extension = 'jar') {
return normalize(MavenUtil.mavenComponentsToString(group, artifact, version, classifier, extension))
}
} }

23
src/util/versionutil.ts Normal file
View File

@@ -0,0 +1,23 @@
export class VersionUtil {
public static readonly MINECRAFT_VERSION_REGEX = /(\d+).(\d+).(\d+)/
public static isMinecraftVersion(version: string) {
return VersionUtil.MINECRAFT_VERSION_REGEX.test(version)
}
public static getMinecraftVersionComponents(version: string) {
if (VersionUtil.isMinecraftVersion(version)) {
const result = VersionUtil.MINECRAFT_VERSION_REGEX.exec(version)
if (result != null) {
return {
major: Number(result[1]),
minor: Number(result[2]),
revision: Number(result[3])
}
}
}
return null
}
}

View File

@@ -2,9 +2,9 @@
"compilerOptions": { "compilerOptions": {
/* Basic Options */ /* Basic Options */
// "incremental": true, /* Enable incremental compilation */ // "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "target": "ES2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */ "lib": ["ES2019"], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */ // "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */ // "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */