SurviePote 2/4: atlas Nebula (serveur SurviePote-26.3, distribution.json, sans binaires)
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { createLogger, format, transports, Logger } from 'winston'
|
||||
import { SPLAT } from 'triple-beam'
|
||||
import { DateTime } from 'luxon'
|
||||
import { inspect } from 'util'
|
||||
|
||||
export class LoggerUtil {
|
||||
|
||||
public static getLogger(label: string): Logger {
|
||||
return createLogger({
|
||||
format: format.combine(
|
||||
format.label(),
|
||||
format.colorize(),
|
||||
format.label({ label }),
|
||||
format.printf(infoUnknown => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const info = infoUnknown as {[key: string | symbol]: any}
|
||||
if(info[SPLAT]) {
|
||||
if(info[SPLAT].length === 1 && info[SPLAT][0] instanceof Error) {
|
||||
const err: Error = info[SPLAT][0]
|
||||
if(info.message.length > err.message.length && info.message.endsWith(err.message)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
info.message = info.message.substring(0, info.message.length-err.message.length)
|
||||
}
|
||||
} else if(info[SPLAT].length > 0) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
info.message += ' ' + info[SPLAT].map((it: any) => {
|
||||
if(typeof it === 'object' && it != null) {
|
||||
return inspect(it, false, 4, true)
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return it
|
||||
}).join(' ')
|
||||
}
|
||||
}
|
||||
if(typeof info.message === 'object') {
|
||||
info.message = inspect(info.message, false, 4, true)
|
||||
}
|
||||
return `[${DateTime.local().toFormat('yyyy-MM-dd TT').trim()}] [${info.level}] [${info.label}]: ${info.message}${info.stack ? `\n${info.stack}` : ''}`
|
||||
})
|
||||
),
|
||||
level: 'debug',
|
||||
transports: [
|
||||
new transports.Console()
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import { normalize } from 'path'
|
||||
import { URL } from 'url'
|
||||
|
||||
export interface MavenComponents {
|
||||
group: string
|
||||
artifact: string
|
||||
version: string
|
||||
classifier?: string
|
||||
extension: string
|
||||
}
|
||||
|
||||
export class MavenUtil {
|
||||
|
||||
public static readonly ID_REGEX = /([^@:]+):([^@:]+):?([^@:]+)?:?(?:([^@:]+))?:?(?:@{1}([^@:]+))?/
|
||||
|
||||
public static mavenComponentsToIdentifier(
|
||||
group: string,
|
||||
artifact: string,
|
||||
version: string,
|
||||
classifier?: string,
|
||||
extension?: string
|
||||
): string {
|
||||
return `${group}:${artifact}:${version}${classifier != null ? `:${classifier}` : ''}${extension != null ? `@${extension}` : ''}`
|
||||
}
|
||||
|
||||
public static mavenComponentsToExtensionlessIdentifier(
|
||||
group: string,
|
||||
artifact: string,
|
||||
version: string,
|
||||
classifier?: string
|
||||
): string {
|
||||
return MavenUtil.mavenComponentsToIdentifier(group, artifact, version, classifier)
|
||||
}
|
||||
|
||||
public static mavenComponentsToVersionlessIdentifier(
|
||||
group: string,
|
||||
artifact: string
|
||||
): string {
|
||||
return `${group}:${artifact}`
|
||||
}
|
||||
|
||||
public static isMavenIdentifier(id: string): boolean {
|
||||
return MavenUtil.ID_REGEX.test(id)
|
||||
}
|
||||
|
||||
public static getMavenComponents(id: string, extension = 'jar'): MavenComponents {
|
||||
if (!MavenUtil.isMavenIdentifier(id)) {
|
||||
throw new Error('Id is not a maven identifier.')
|
||||
}
|
||||
|
||||
const result = MavenUtil.ID_REGEX.exec(id)
|
||||
|
||||
if (result != null) {
|
||||
return {
|
||||
group: result[1],
|
||||
artifact: result[2],
|
||||
version: result[3],
|
||||
classifier: result[4],
|
||||
extension: result[5] || extension
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Failed to process maven data.')
|
||||
}
|
||||
|
||||
public static mavenIdentifierAsPath(id: string, extension = 'jar'): string {
|
||||
const tmp = MavenUtil.getMavenComponents(id, extension)
|
||||
|
||||
return MavenUtil.mavenComponentsAsPath(
|
||||
tmp.group, tmp.artifact, tmp.version, tmp.classifier, tmp.extension
|
||||
)
|
||||
}
|
||||
|
||||
public static mavenComponentsAsPath(
|
||||
group: string, artifact: string, version: string, classifier?: string, extension = 'jar'
|
||||
): string {
|
||||
return `${group.replace(/\./g, '/')}/${artifact}/${version}/${artifact}-${version}${classifier != null ? `-${classifier}` : ''}.${extension}`
|
||||
}
|
||||
|
||||
public static mavenIdentifierToUrl(id: string, extension = 'jar'): URL {
|
||||
return new URL(MavenUtil.mavenIdentifierAsPath(id, extension))
|
||||
}
|
||||
|
||||
public static mavenComponentsToUrl(
|
||||
group: string, artifact: string, version: string, classifier?: string, extension = 'jar'
|
||||
): URL {
|
||||
return new URL(MavenUtil.mavenComponentsAsPath(group, artifact, version, classifier, extension))
|
||||
}
|
||||
|
||||
public static mavenIdentifierToPath(id: string, extension = 'jar'): string {
|
||||
return normalize(MavenUtil.mavenIdentifierAsPath(id, extension))
|
||||
}
|
||||
|
||||
public static mavenComponentsAsNormalizedPath(
|
||||
group: string, artifact: string, version: string, classifier?: string, extension = 'jar'
|
||||
): string {
|
||||
return normalize(MavenUtil.mavenComponentsAsPath(group, artifact, version, classifier, extension))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export class MetadataUtil {
|
||||
|
||||
public static completeGroupInference(partial: string, id: string): string {
|
||||
|
||||
const bits = partial.split('.')
|
||||
|
||||
let isBadTerm = true
|
||||
while(isBadTerm && bits.length > 2) {
|
||||
const term = bits[bits.length-1]
|
||||
if(term !== id) {
|
||||
isBadTerm = false
|
||||
} else {
|
||||
bits.pop()
|
||||
}
|
||||
}
|
||||
|
||||
return bits.join('.')
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
export class MinecraftVersion {
|
||||
|
||||
private static readonly MINECRAFT_VERSION_REGEX = /(\d+).(\d+).?(\d+)?/
|
||||
|
||||
private readonly major: number
|
||||
private readonly minor: number
|
||||
private readonly revision: number | undefined
|
||||
|
||||
constructor(version: string) {
|
||||
const res = MinecraftVersion.MINECRAFT_VERSION_REGEX.exec(version)
|
||||
if(res != null) {
|
||||
this.major = Number(res[1])
|
||||
this.minor = Number(res[2])
|
||||
this.revision = res[3] != null ? Number(res[3]) : undefined
|
||||
} else {
|
||||
throw new Error(`${version} is not a valid minecraft version!`)
|
||||
}
|
||||
}
|
||||
|
||||
public static isMinecraftVersion(version: string): boolean {
|
||||
return MinecraftVersion.MINECRAFT_VERSION_REGEX.test(version)
|
||||
}
|
||||
|
||||
public getMajor(): number { return this.major }
|
||||
public getMinor(): number { return this.minor }
|
||||
public getRevision(): number | undefined { return this.revision }
|
||||
|
||||
public toString(): string { return `${this.major}.${this.minor}${this.revision != null ? '.' + this.revision : ''}`}
|
||||
|
||||
public compareTo(other: MinecraftVersion): number {
|
||||
// Compare major
|
||||
if (this.major !== other.major) {
|
||||
return this.major - other.major
|
||||
}
|
||||
|
||||
// Compare minor
|
||||
if (this.minor !== other.minor) {
|
||||
return this.minor - other.minor
|
||||
}
|
||||
|
||||
// Compare revision (null as 0)
|
||||
const thisRevision = this.revision ?? 0
|
||||
const otherRevision = other.revision ?? 0
|
||||
return thisRevision - otherRevision
|
||||
}
|
||||
|
||||
public isGreaterThanOrEqualTo(other: MinecraftVersion): boolean {
|
||||
return this.compareTo(other) >= 0
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { mkdirs, pathExists, remove } from 'fs-extra/esm'
|
||||
import { writeFile } from 'fs/promises'
|
||||
import { join, resolve } from 'path'
|
||||
import { createGenerator } from 'ts-json-schema-generator'
|
||||
import { URL, fileURLToPath } from 'url'
|
||||
import { DistroMeta } from '../model/nebula/DistroMeta.js'
|
||||
import { ServerMeta } from '../model/nebula/ServerMeta.js'
|
||||
import { LoggerUtil } from './LoggerUtil.js'
|
||||
|
||||
const logger = LoggerUtil.getLogger('SchemaUtil')
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
interface SchemaType {
|
||||
/**
|
||||
* URL to the JSON schema for this type of file.
|
||||
* This is used by editors to validate and annotate the data.
|
||||
*/
|
||||
$schema?: string
|
||||
}
|
||||
|
||||
export type DistroMetaSchema = DistroMeta & SchemaType
|
||||
export type ServerMetaSchema = ServerMeta & SchemaType
|
||||
|
||||
export enum SchemaTypes {
|
||||
DistroMetaSchema = 'DistroMetaSchema',
|
||||
ServerMetaSchema = 'ServerMetaSchema'
|
||||
}
|
||||
|
||||
function getSchemaFileName(typeName: string): string {
|
||||
return `${typeName}.schema.json`
|
||||
}
|
||||
|
||||
function getSchemaDirectory(absoluteRoot: string): string {
|
||||
return resolve(absoluteRoot, 'schemas')
|
||||
}
|
||||
|
||||
function getSchemaLocation(typeName: string, absoluteRoot: string): string {
|
||||
return resolve(getSchemaDirectory(absoluteRoot), getSchemaFileName(typeName))
|
||||
}
|
||||
|
||||
export function addSchemaToObject<T>(obj: T, typeName: string, absoluteRoot: string): T {
|
||||
return {
|
||||
$schema: new URL(`file:${getSchemaLocation(typeName, absoluteRoot)}`).href,
|
||||
...obj
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateSchemas(absoluteRoot: string): Promise<void> {
|
||||
|
||||
const selfPath = __filename.replace('dist', 'src').replace('.js', '.ts')
|
||||
|
||||
const schemaDir = getSchemaDirectory(absoluteRoot)
|
||||
if(await pathExists(schemaDir)) {
|
||||
await remove(schemaDir)
|
||||
}
|
||||
await mkdirs(schemaDir)
|
||||
|
||||
for(const typeName of Object.values(SchemaTypes)) {
|
||||
|
||||
logger.info(`Generating schema for ${typeName}`)
|
||||
|
||||
const schema = createGenerator({
|
||||
tsconfig: join(__dirname, '..', '..', 'tsconfig.json'),
|
||||
path: selfPath,
|
||||
type: typeName
|
||||
}).createSchema(typeName)
|
||||
|
||||
const schemaString = JSON.stringify(schema)
|
||||
const schemaLoc = getSchemaLocation(typeName, absoluteRoot)
|
||||
await writeFile(schemaLoc, schemaString)
|
||||
|
||||
logger.info(`Schema for ${typeName} saved to ${schemaLoc}`)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
export function capitalize(str: string): string {
|
||||
if (!str) {
|
||||
return str
|
||||
}
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
}
|
||||
|
||||
export function isValidUrl(candidate: string): boolean {
|
||||
try {
|
||||
new URL(candidate)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { MinecraftVersion } from './MinecraftVersion.js'
|
||||
|
||||
export interface VersionSegmented {
|
||||
|
||||
isForVersion(version: MinecraftVersion, libraryVersion: string): boolean
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { ForgeModStructure113 } from '../structure/spec_model/module/forgemod/ForgeMod113.struct.js'
|
||||
import { ForgeModStructure17 } from '../structure/spec_model/module/forgemod/ForgeMod17.struct.js'
|
||||
import { ForgeGradle3Adapter } from '../resolver/forge/adapter/ForgeGradle3.resolver.js'
|
||||
import { ForgeGradle2Adapter } from '../resolver/forge/adapter/ForgeGradle2.resolver.js'
|
||||
import { ForgeResolver } from '../resolver/forge/Forge.resolver.js'
|
||||
import { BaseForgeModStructure } from '../structure/spec_model/module/ForgeMod.struct.js'
|
||||
import { MinecraftVersion } from './MinecraftVersion.js'
|
||||
import { UntrackedFilesOption } from '../model/nebula/ServerMeta.js'
|
||||
|
||||
export class VersionSegmentedRegistry {
|
||||
|
||||
public static readonly FORGE_ADAPTER_IMPL = [
|
||||
ForgeGradle2Adapter,
|
||||
ForgeGradle3Adapter
|
||||
]
|
||||
|
||||
public static readonly FORGEMOD_STRUCT_IMPL = [
|
||||
ForgeModStructure17,
|
||||
ForgeModStructure113
|
||||
]
|
||||
|
||||
public static getForgeResolver(
|
||||
minecraftVersion: MinecraftVersion,
|
||||
forgeVersion: string,
|
||||
absoluteRoot: string,
|
||||
relativeRoot: string,
|
||||
baseURL: string,
|
||||
discardOutput: boolean,
|
||||
invalidateCache: boolean
|
||||
): ForgeResolver {
|
||||
for (const impl of VersionSegmentedRegistry.FORGE_ADAPTER_IMPL) {
|
||||
if (impl.isForVersion(minecraftVersion, forgeVersion)) {
|
||||
return new impl(absoluteRoot, relativeRoot, baseURL, minecraftVersion, forgeVersion, discardOutput, invalidateCache)
|
||||
}
|
||||
}
|
||||
throw new Error(`No forge resolver found for Minecraft ${minecraftVersion}!`)
|
||||
}
|
||||
|
||||
public static getForgeModStruct(
|
||||
minecraftVersion: MinecraftVersion,
|
||||
forgeVersion: string,
|
||||
absoluteRoot: string,
|
||||
relativeRoot: string,
|
||||
baseUrl: string,
|
||||
untrackedFiles: UntrackedFilesOption[]
|
||||
): BaseForgeModStructure<unknown> {
|
||||
for (const impl of VersionSegmentedRegistry.FORGEMOD_STRUCT_IMPL) {
|
||||
if (impl.isForVersion(minecraftVersion, forgeVersion)) {
|
||||
return new impl(absoluteRoot, relativeRoot, baseUrl, minecraftVersion, untrackedFiles)
|
||||
}
|
||||
}
|
||||
throw new Error(`No forge mod structure found for Minecraft ${minecraftVersion}!`)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import got from 'got'
|
||||
import { PromotionsSlim } from '../model/forge/PromotionsSlim.js'
|
||||
import { MinecraftVersion } from './MinecraftVersion.js'
|
||||
import { LoggerUtil } from './LoggerUtil.js'
|
||||
import { FabricInstallerMeta, FabricLoaderMeta, FabricProfileJson, FabricVersionMeta } from '../model/fabric/FabricMeta.js'
|
||||
|
||||
export class VersionUtil {
|
||||
|
||||
private static readonly logger = LoggerUtil.getLogger('VersionUtil')
|
||||
|
||||
public static readonly PROMOTION_TYPE = [
|
||||
'recommended',
|
||||
'latest'
|
||||
]
|
||||
|
||||
public static isVersionAcceptable(version: MinecraftVersion, acceptable: number[]): boolean {
|
||||
if (version.getMajor() === 1) {
|
||||
return acceptable.find((element) => version.getMinor() === element) != null
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public static versionGte(version: string, min: string): boolean {
|
||||
|
||||
if(version === min) {
|
||||
return true
|
||||
}
|
||||
|
||||
const left = version.split('.').map(x => Number(x))
|
||||
const right = min.split('.').map(x => Number(x))
|
||||
|
||||
if(left.length != right.length) {
|
||||
throw new Error('Cannot compare mismatched versions.')
|
||||
}
|
||||
|
||||
for(let i=0; i<left.length; i++) {
|
||||
if(left[i] > right[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
public static isPromotionVersion(version: string): boolean {
|
||||
return VersionUtil.PROMOTION_TYPE.includes(version.toLowerCase())
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Forge
|
||||
|
||||
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.length; i++) {
|
||||
if(verSplit[i] > maxFG2[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
public static async getPromotionIndex(): Promise<PromotionsSlim> {
|
||||
const response = await got.get<PromotionsSlim>({
|
||||
method: 'get',
|
||||
url: 'https://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json',
|
||||
responseType: 'json'
|
||||
})
|
||||
return response.body
|
||||
}
|
||||
|
||||
public static getPromotedVersionStrict(index: PromotionsSlim, minecraftVersion: MinecraftVersion, promotion: string): string {
|
||||
const workingPromotion = promotion.toLowerCase()
|
||||
return index.promos[`${minecraftVersion}-${workingPromotion}`]
|
||||
}
|
||||
|
||||
public static async getPromotedForgeVersion(minecraftVersion: MinecraftVersion, promotion: string): Promise<string> {
|
||||
const workingPromotion = promotion.toLowerCase()
|
||||
const res = await VersionUtil.getPromotionIndex()
|
||||
let version = res.promos[`${minecraftVersion}-${workingPromotion}`]
|
||||
if (version == null) {
|
||||
VersionUtil.logger.warn(`No ${workingPromotion} version found for Forge ${minecraftVersion}.`)
|
||||
VersionUtil.logger.warn('Attempting to pull latest version instead.')
|
||||
version = res.promos[`${minecraftVersion}-latest`]
|
||||
if (version == null) {
|
||||
throw new Error(`No latest version found for Forge ${minecraftVersion}.`)
|
||||
}
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Fabric
|
||||
|
||||
public static async getFabricInstallerMeta(): Promise<FabricInstallerMeta[]> {
|
||||
const response = await got.get<FabricInstallerMeta[]>({
|
||||
method: 'get',
|
||||
url: 'https://meta.fabricmc.net/v2/versions/installer',
|
||||
responseType: 'json'
|
||||
})
|
||||
return response.body
|
||||
}
|
||||
|
||||
public static async getFabricLoaderMeta(): Promise<FabricLoaderMeta[]> {
|
||||
const response = await got.get<FabricLoaderMeta[]>({
|
||||
method: 'get',
|
||||
url: 'https://meta.fabricmc.net/v2/versions/loader',
|
||||
responseType: 'json'
|
||||
})
|
||||
return response.body
|
||||
}
|
||||
|
||||
public static async getFabricGameMeta(): Promise<FabricVersionMeta[]> {
|
||||
const response = await got.get<FabricVersionMeta[]>({
|
||||
method: 'get',
|
||||
url: 'https://meta.fabricmc.net/v2/versions/game',
|
||||
responseType: 'json'
|
||||
})
|
||||
return response.body
|
||||
}
|
||||
|
||||
public static async getFabricProfileJson(gameVersion: string, loaderVersion: string): Promise<FabricProfileJson> {
|
||||
const response = await got.get<FabricProfileJson>({
|
||||
method: 'get',
|
||||
url: `https://meta.fabricmc.net/v2/versions/loader/${gameVersion}/${loaderVersion}/profile/json`,
|
||||
responseType: 'json'
|
||||
})
|
||||
return response.body
|
||||
}
|
||||
|
||||
public static async getPromotedFabricVersion(promotion: string): Promise<string> {
|
||||
const stable = promotion.toLowerCase() === 'recommended'
|
||||
const fabricLoaderMeta = await this.getFabricLoaderMeta()
|
||||
return !stable ? fabricLoaderMeta[0].version : fabricLoaderMeta.find(({ stable }) => stable)!.version
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { JarExecutor } from './JarExecutor.js'
|
||||
import { join, resolve } from 'path'
|
||||
import { ClaritasResult } from '../../model/claritas/ClaritasResult.js'
|
||||
import { MinecraftVersion } from '../MinecraftVersion.js'
|
||||
import { LibraryType } from '../../model/claritas/ClaritasLibraryType.js'
|
||||
import { pathExists, remove, mkdirs } from 'fs-extra/esm'
|
||||
import { readFile, writeFile } from 'fs/promises'
|
||||
|
||||
export class ClaritasWrapper extends JarExecutor<ClaritasResult> {
|
||||
|
||||
private readonly WORK_DIR: string
|
||||
private readonly ARG_FILE: string
|
||||
private readonly OUTPUT_FILE: string
|
||||
|
||||
constructor(cwd: string) {
|
||||
super('Claritas')
|
||||
|
||||
this.WORK_DIR = resolve(cwd, 'claritasWork')
|
||||
this.ARG_FILE = resolve(this.WORK_DIR, 'claritasArgFile.txt')
|
||||
this.OUTPUT_FILE = resolve(this.WORK_DIR, 'claritasOutput.json')
|
||||
|
||||
this.onCloseListeners.push(async (code) => {
|
||||
if(code !== 0) {
|
||||
this.logger.error('Claritas finished with non-zero exit code, ', code)
|
||||
this.lastExecutionResult = undefined!
|
||||
} else {
|
||||
if(await pathExists(this.OUTPUT_FILE)) {
|
||||
this.lastExecutionResult = JSON.parse((await readFile(this.OUTPUT_FILE)).toString('utf8')) as ClaritasResult
|
||||
} else {
|
||||
this.logger.error('Claritas output file not found when exit code is 0, is this a bug?')
|
||||
this.lastExecutionResult = undefined!
|
||||
}
|
||||
}
|
||||
await this.cleanOutput()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
protected getJarPath(): string {
|
||||
return join(process.cwd(), 'libraries', 'java', 'Claritas.jar')
|
||||
}
|
||||
|
||||
private async writeArgFile(...programArgs: string[]): Promise<void> {
|
||||
await mkdirs(this.WORK_DIR)
|
||||
await writeFile(
|
||||
this.ARG_FILE,
|
||||
programArgs.join('\n')
|
||||
)
|
||||
}
|
||||
|
||||
public async execute(libraryType: LibraryType, mcVersion: MinecraftVersion, absoluteJarPaths: string[]): Promise<ClaritasResult> {
|
||||
await this.writeArgFile(
|
||||
'--absoluteJarPaths', absoluteJarPaths.join(','),
|
||||
'--libraryType', libraryType,
|
||||
'--mcVersion', mcVersion.toString(),
|
||||
'--outputFile', this.OUTPUT_FILE,
|
||||
'--previewOutput', 'true'
|
||||
)
|
||||
return await super.executeJar(
|
||||
[`-Dclaritas.argFile=${this.ARG_FILE}`]
|
||||
)
|
||||
}
|
||||
|
||||
private async cleanOutput(): Promise<void> {
|
||||
if(await pathExists(this.WORK_DIR)) {
|
||||
await remove(this.WORK_DIR)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { JavaUtil } from './JavaUtil.js'
|
||||
import { Logger } from 'winston'
|
||||
import { spawn } from 'child_process'
|
||||
import { LoggerUtil } from '../LoggerUtil.js'
|
||||
|
||||
export abstract class JarExecutor<T> {
|
||||
|
||||
protected readonly logger: Logger
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
protected stdoutListeners: ((chunk: any) => void)[] = []
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
protected stderrListeners: ((chunk: any) => void)[] = []
|
||||
protected onCloseListeners: ((code: number | null) => Promise<void>)[] = []
|
||||
|
||||
protected lastExecutionResult!: T
|
||||
|
||||
protected constructor(loggerName: string) {
|
||||
this.logger = LoggerUtil.getLogger(loggerName)
|
||||
}
|
||||
|
||||
protected abstract getJarPath(): string
|
||||
|
||||
protected executeJar(vmOptions: string[], ...args: string[]): Promise<T> {
|
||||
this.lastExecutionResult = undefined!
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(JavaUtil.getJavaExecutable(), [
|
||||
...vmOptions,
|
||||
'-jar',
|
||||
this.getJarPath(),
|
||||
...args
|
||||
])
|
||||
child.stdout.on('data', (data) => this.logger.info(data.toString('utf8').trim()))
|
||||
this.stdoutListeners.forEach(l => child.stdout.on('data', l))
|
||||
|
||||
child.stderr.on('data', (data) => this.logger.error(data.toString('utf8').trim()))
|
||||
this.stderrListeners.forEach(l => child.stderr.on('data', l))
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
child.on('close', async code => {
|
||||
this.logger.info('Exited with code', code)
|
||||
for(const l of this.onCloseListeners) {
|
||||
await l(code)
|
||||
}
|
||||
resolve(this.lastExecutionResult)
|
||||
})
|
||||
|
||||
child.on('error', (err) => {
|
||||
this.logger.info('Error during process execution', err)
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export class JavaUtil {
|
||||
|
||||
public static getJavaExecutable(): string {
|
||||
return process.env.JAVA_EXECUTABLE!
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { join } from 'path'
|
||||
import { JarExecutor } from './JarExecutor.js'
|
||||
|
||||
export class PackXZExtractWrapper extends JarExecutor<void> {
|
||||
|
||||
constructor() {
|
||||
super('PackXZExtract')
|
||||
}
|
||||
|
||||
protected getJarPath(): string {
|
||||
return join(process.cwd(), 'libraries', 'java', 'PackXZExtract.jar')
|
||||
}
|
||||
|
||||
protected execute(command: string, paths: string[]): Promise<void> {
|
||||
return super.executeJar([], command, paths.join(','))
|
||||
}
|
||||
|
||||
public extractUnpack(paths: string[]): Promise<void> {
|
||||
return this.execute('-packxz', paths)
|
||||
}
|
||||
|
||||
public extract(paths: string[]): Promise<void> {
|
||||
return this.execute('-xz', paths)
|
||||
}
|
||||
|
||||
public unpack(paths: string[]): Promise<void> {
|
||||
return this.execute('-pack', paths)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user