First pass at Claritas implementation.

The resolution logic was reworked so that Claritas only needs to be invoked once per supported type, ie only once for ForgeMod and LiteMod resolutions per server. The resolver now uses identifies module candidates and collects them. Claritas is invoked and the resulting metadata is stored. The module resolution then proceeds with all of this data available.
Toggleable module logic was also reworked to first accumulate all candidates and then process. This required the resolution function to optionally take a preprocess and postprocess callback to perform the necessary cleanup and transformations.
The minor rework was necessary because spawning child process is expensive, and we should only do it as often as we must to keep the application performant.
Claritas resolution also supports exceptions defined by the structure class. This is to facilitate handling of special cases (ex. Optifine).
This commit is contained in:
Daniel Scalzi
2020-07-14 23:12:59 -04:00
parent 7620971c99
commit bda96eb24e
26 changed files with 447 additions and 203 deletions

BIN
libraries/java/Claritas.jar Normal file

Binary file not shown.

115
package-lock.json generated
View File

@@ -67,15 +67,6 @@
} }
} }
}, },
"@babel/runtime-corejs3": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.4.tgz",
"integrity": "sha512-BFlgP2SoLO9HJX9WBwN67gHWMBhDX/eDz64Jajd6mR/UAUzqrNMm99d4qHnVaKscAElZoFiPv+JpR/Siud5lXw==",
"requires": {
"core-js-pure": "^3.0.0",
"regenerator-runtime": "^0.13.4"
}
},
"@dabh/diagnostics": { "@dabh/diagnostics": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz",
@@ -113,9 +104,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "12.12.47", "version": "12.12.50",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.47.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.50.tgz",
"integrity": "sha512-yzBInQFhdY8kaZmqoL2+3U5dSTMrKaYcb561VU+lDzAYvqt+2lojvBEy+hmpSNuXnPTx7m9+04CzWYOUqWME2A==", "integrity": "sha512-5ImO01Fb8YsEOYpV+aeyGYztcYcjGsBvN4D7G5r1ef2cuQOpymjWNQi5V0rKHE6PC2ru3HkoUr/Br2/8GUA84w==",
"dev": true "dev": true
}, },
"@types/triple-beam": { "@types/triple-beam": {
@@ -140,12 +131,12 @@
"dev": true "dev": true
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "3.5.0", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.5.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.1.tgz",
"integrity": "sha512-m4erZ8AkSjoIUOf8s4k2V1xdL2c1Vy0D3dN6/jC9d7+nEqjY3gxXCkgi3gW/GAxPaA4hV8biaCoTVdQmfAeTCQ==", "integrity": "sha512-06lfjo76naNeOMDl+mWG9Fh/a0UHKLGhin+mGaIw72FUMbMGBkdi/FEJmgEDzh4eE73KIYzHWvOCYJ0ak7nrJQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "3.5.0", "@typescript-eslint/experimental-utils": "3.6.1",
"debug": "^4.1.1", "debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0", "regexpp": "^3.0.0",
@@ -171,45 +162,45 @@
} }
}, },
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "3.5.0", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.5.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.1.tgz",
"integrity": "sha512-zGNOrVi5Wz0jcjUnFZ6QUD0MCox5hBuVwemGCew2qJzUX5xPoyR+0EzS5qD5qQXL/vnQ8Eu+nv03tpeFRwLrDg==", "integrity": "sha512-oS+hihzQE5M84ewXrTlVx7eTgc52eu+sVmG7ayLfOhyZmJ8Unvf3osyFQNADHP26yoThFfbxcibbO0d2FjnYhg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.3", "@types/json-schema": "^7.0.3",
"@typescript-eslint/types": "3.5.0", "@typescript-eslint/types": "3.6.1",
"@typescript-eslint/typescript-estree": "3.5.0", "@typescript-eslint/typescript-estree": "3.6.1",
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0" "eslint-utils": "^2.0.0"
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "3.5.0", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.5.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.6.1.tgz",
"integrity": "sha512-sU07VbYB70WZHtgOjH/qfAp1+OwaWgrvD1Km1VXqRpcVxt971PMTU7gJtlrCje0M+Sdz7xKAbtiyIu+Y6QdnVA==", "integrity": "sha512-SLihQU8RMe77YJ/jGTqOt0lMq7k3hlPVfp7v/cxMnXA9T0bQYoMDfTsNgHXpwSJM1Iq2aAJ8WqekxUwGv5F67Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/eslint-visitor-keys": "^1.0.0", "@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "3.5.0", "@typescript-eslint/experimental-utils": "3.6.1",
"@typescript-eslint/types": "3.5.0", "@typescript-eslint/types": "3.6.1",
"@typescript-eslint/typescript-estree": "3.5.0", "@typescript-eslint/typescript-estree": "3.6.1",
"eslint-visitor-keys": "^1.1.0" "eslint-visitor-keys": "^1.1.0"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "3.5.0", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.5.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.6.1.tgz",
"integrity": "sha512-Dreqb5idi66VVs1QkbAwVeDmdJG+sDtofJtKwKCZXIaBsINuCN7Jv5eDIHrS0hFMMiOvPH9UuOs4splW0iZe4Q==", "integrity": "sha512-NPxd5yXG63gx57WDTW1rp0cF3XlNuuFFB5G+Kc48zZ+51ZnQn9yjDEsjTPQ+aWM+V+Z0I4kuTFKjKvgcT1F7xQ==",
"dev": true "dev": true
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "3.5.0", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.5.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.1.tgz",
"integrity": "sha512-Na71ezI6QP5WVR4EHxwcBJgYiD+Sre9BZO5iJK2QhrmRPo/42+b0no/HZIrdD1sjghzlYv7t+7Jis05M1uMxQg==", "integrity": "sha512-G4XRe/ZbCZkL1fy09DPN3U0mR6SayIv1zSeBNquRFRk7CnVLgkC2ZPj8llEMJg5Y8dJ3T76SvTGtceytniaztQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "3.5.0", "@typescript-eslint/types": "3.6.1",
"@typescript-eslint/visitor-keys": "3.5.0", "@typescript-eslint/visitor-keys": "3.6.1",
"debug": "^4.1.1", "debug": "^4.1.1",
"glob": "^7.1.6", "glob": "^7.1.6",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@@ -236,9 +227,9 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "3.5.0", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.5.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.1.tgz",
"integrity": "sha512-7cTp9rcX2sz9Z+zua9MCOX4cqp5rYyFD5o8LlbSpXrMTXoRdngTtotRZEkm8+FNMHPWYFhitFK+qt/brK8BVJQ==", "integrity": "sha512-qC8Olwz5ZyMTZrh4Wl3K4U6tfms0R/mzU4/5W3XeUZptVraGVmbptJbn6h2Ey6Rb3hOs3zWoAUebZk8t47KGiQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"eslint-visitor-keys": "^1.1.0" "eslint-visitor-keys": "^1.1.0"
@@ -451,11 +442,6 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true "dev": true
}, },
"core-js-pure": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
"integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA=="
},
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -481,12 +467,9 @@
} }
}, },
"decamelize": { "decamelize": {
"version": "3.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-3.2.0.tgz", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
"requires": {
"xregexp": "^4.2.4"
}
}, },
"deep-is": { "deep-is": {
"version": "0.1.3", "version": "0.1.3",
@@ -976,9 +959,9 @@
} }
}, },
"lodash": { "lodash": {
"version": "4.17.15", "version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
"dev": true "dev": true
}, },
"logform": { "logform": {
@@ -1156,11 +1139,6 @@
"util-deprecate": "^1.0.1" "util-deprecate": "^1.0.1"
} }
}, },
"regenerator-runtime": {
"version": "0.13.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
},
"regexpp": { "regexpp": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
@@ -1559,26 +1537,18 @@
"mkdirp": "^0.5.1" "mkdirp": "^0.5.1"
} }
}, },
"xregexp": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz",
"integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==",
"requires": {
"@babel/runtime-corejs3": "^7.8.3"
}
},
"y18n": { "y18n": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
}, },
"yargs": { "yargs": {
"version": "15.4.0", "version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.0.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-D3fRFnZwLWp8jVAAhPZBsmeIHY8tTsb8ItV9KaAaopmC6wde2u6Yw29JBIZHXw14kgkRnYmDgmQU4FVMDlIsWw==", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"requires": { "requires": {
"cliui": "^6.0.0", "cliui": "^6.0.0",
"decamelize": "^3.2.0", "decamelize": "^1.2.0",
"find-up": "^4.1.0", "find-up": "^4.1.0",
"get-caller-file": "^2.0.1", "get-caller-file": "^2.0.1",
"require-directory": "^2.1.1", "require-directory": "^2.1.1",
@@ -1597,13 +1567,6 @@
"requires": { "requires": {
"camelcase": "^5.0.0", "camelcase": "^5.0.0",
"decamelize": "^1.2.0" "decamelize": "^1.2.0"
},
"dependencies": {
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
}
} }
} }
} }

View File

@@ -27,11 +27,11 @@
"homepage": "https://github.com/dscalzi/Nebula#readme", "homepage": "https://github.com/dscalzi/Nebula#readme",
"devDependencies": { "devDependencies": {
"@types/fs-extra": "^9.0.1", "@types/fs-extra": "^9.0.1",
"@types/node": "^12.12.47", "@types/node": "^12.12.50",
"@types/triple-beam": "^1.3.1", "@types/triple-beam": "^1.3.1",
"@types/yargs": "^15.0.5", "@types/yargs": "^15.0.5",
"@typescript-eslint/eslint-plugin": "^3.5.0", "@typescript-eslint/eslint-plugin": "^3.6.1",
"@typescript-eslint/parser": "^3.5.0", "@typescript-eslint/parser": "^3.6.1",
"eslint": "^7.4.0", "eslint": "^7.4.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^3.9.6" "typescript": "^3.9.6"
@@ -46,6 +46,6 @@
"toml": "^3.0.0", "toml": "^3.0.0",
"triple-beam": "^1.3.0", "triple-beam": "^1.3.0",
"winston": "^3.3.3", "winston": "^3.3.3",
"yargs": "^15.4.0" "yargs": "^15.4.1"
} }
} }

View File

@@ -0,0 +1,6 @@
export enum LibraryType {
FORGE = 'FORGE',
LITELOADER = 'LITELOADER'
}

View File

@@ -0,0 +1,26 @@
export interface ClaritasModuleMetadata {
/**
* Present on ForgeMods
*/
id?: string
/**
* Always Present
*/
group: string
/**
* Possibly present on ForgeMods 1.12-
*/
version?: string
/**
* Possibly present on ForgeMods 1.12-
*/
name?: string
}
export interface ClaritasResult {
[jarPath: string]: ClaritasModuleMetadata | undefined
}

View File

@@ -1,9 +1,12 @@
import { mkdirs } from 'fs-extra' import { mkdirs } from 'fs-extra'
import { join, resolve } from 'path' import { join, resolve } from 'path'
import { FileStructure } from './FileStructure' import { FileStructure } from './FileStructure'
import { Logger } from 'winston'
import { LoggerUtil } from '../../util/LoggerUtil'
export abstract class BaseFileStructure implements FileStructure { export abstract class BaseFileStructure implements FileStructure {
protected logger: Logger
protected containerDirectory: string protected containerDirectory: string
constructor( constructor(
@@ -13,10 +16,13 @@ export abstract class BaseFileStructure implements FileStructure {
) { ) {
this.relativeRoot = join(relativeRoot, structRoot) this.relativeRoot = join(relativeRoot, structRoot)
this.containerDirectory = resolve(absoluteRoot, structRoot) this.containerDirectory = resolve(absoluteRoot, structRoot)
this.logger = LoggerUtil.getLogger(this.getLoggerName())
} }
public async init(): Promise<void> { public async init(): Promise<void> {
mkdirs(this.containerDirectory) mkdirs(this.containerDirectory)
} }
public abstract getLoggerName(): string
} }

View File

@@ -5,15 +5,21 @@ import { resolve as resolveURL } from 'url'
import { ModuleStructure } from './module.struct' import { ModuleStructure } from './module.struct'
import { readdir, stat } from 'fs-extra' import { readdir, stat } from 'fs-extra'
import { join, resolve, sep } from 'path' import { join, resolve, sep } from 'path'
import { MinecraftVersion } from '../../../../util/MinecraftVersion'
export class MiscFileStructure extends ModuleStructure { export class MiscFileStructure extends ModuleStructure {
constructor( constructor(
absoluteRoot: string, absoluteRoot: string,
relativeRoot: string, relativeRoot: string,
baseUrl: string baseUrl: string,
minecraftVersion: MinecraftVersion
) { ) {
super(absoluteRoot, relativeRoot, 'files', baseUrl, Type.File) super(absoluteRoot, relativeRoot, 'files', baseUrl, minecraftVersion, Type.File)
}
public getLoggerName(): string {
return 'MiscFileStructure'
} }
public async getSpecModel(): Promise<Module[]> { public async getSpecModel(): Promise<Module[]> {

View File

@@ -5,6 +5,8 @@ import { resolve } from 'url'
import { VersionSegmented } from '../../../../util/VersionSegmented' import { VersionSegmented } from '../../../../util/VersionSegmented'
import { MinecraftVersion } from '../../../../util/MinecraftVersion' import { MinecraftVersion } from '../../../../util/MinecraftVersion'
import { ToggleableModuleStructure } from './toggleablemodule.struct' import { ToggleableModuleStructure } from './toggleablemodule.struct'
import { LibraryType } from '../../../claritas/ClaritasLibraryType'
import { ClaritasException } from './module.struct'
export abstract class BaseForgeModStructure extends ToggleableModuleStructure implements VersionSegmented { export abstract class BaseForgeModStructure extends ToggleableModuleStructure implements VersionSegmented {
@@ -13,9 +15,10 @@ export abstract class BaseForgeModStructure extends ToggleableModuleStructure im
constructor( constructor(
absoluteRoot: string, absoluteRoot: string,
relativeRoot: string, relativeRoot: string,
baseUrl: string baseUrl: string,
minecraftVersion: MinecraftVersion
) { ) {
super(absoluteRoot, relativeRoot, 'forgemods', baseUrl, Type.ForgeMod) super(absoluteRoot, relativeRoot, 'forgemods', baseUrl, minecraftVersion, Type.ForgeMod)
} }
public abstract isForVersion(version: MinecraftVersion, libraryVersion: string): boolean public abstract isForVersion(version: MinecraftVersion, libraryVersion: string): boolean
@@ -29,4 +32,21 @@ export abstract class BaseForgeModStructure extends ToggleableModuleStructure im
return null return null
} }
protected getClaritasExceptions(): ClaritasException[] {
return [{
exceptionName: 'optifine',
proxyMetadata: {
group: 'net.optifine'
}
}]
}
protected getClaritasType(): LibraryType {
return LibraryType.FORGE
}
protected discernResult(claritasValue: string | undefined, crudeInference: string): string {
return (claritasValue == null || claritasValue == '') ? crudeInference : claritasValue
}
} }

View File

@@ -5,12 +5,9 @@ import { VersionUtil } from '../../../../../util/versionutil'
import { ModsToml } from '../../../../forge/modstoml' import { ModsToml } from '../../../../forge/modstoml'
import { BaseForgeModStructure } from '../forgemod.struct' import { BaseForgeModStructure } from '../forgemod.struct'
import { MinecraftVersion } from '../../../../../util/MinecraftVersion' import { MinecraftVersion } from '../../../../../util/MinecraftVersion'
import { LoggerUtil } from '../../../../../util/LoggerUtil'
export class ForgeModStructure113 extends BaseForgeModStructure { export class ForgeModStructure113 extends BaseForgeModStructure {
private static readonly logger = LoggerUtil.getLogger('ForgeModStructure (1.13)')
public static readonly IMPLEMENTATION_VERSION_REGEX = /^Implementation-Version: (.+)[\r\n]/ public static readonly IMPLEMENTATION_VERSION_REGEX = /^Implementation-Version: (.+)[\r\n]/
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -23,18 +20,23 @@ export class ForgeModStructure113 extends BaseForgeModStructure {
constructor( constructor(
absoluteRoot: string, absoluteRoot: string,
relativeRoot: string, relativeRoot: string,
baseUrl: string baseUrl: string,
minecraftVersion: MinecraftVersion
) { ) {
super(absoluteRoot, relativeRoot, baseUrl) super(absoluteRoot, relativeRoot, baseUrl, minecraftVersion)
} }
public isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { public isForVersion(version: MinecraftVersion, libraryVersion: string): boolean {
return ForgeModStructure113.isForVersion(version, libraryVersion) return ForgeModStructure113.isForVersion(version, libraryVersion)
} }
public getLoggerName(): string {
return 'ForgeModStructure (1.13)'
}
protected async getModuleId(name: string, path: string): Promise<string> { protected async getModuleId(name: string, path: string): Promise<string> {
const fmData = await this.getForgeModMetadata(name, path) const fmData = await this.getForgeModMetadata(name, path)
return this.generateMavenIdentifier(fmData.mods[0].modId, fmData.mods[0].version) return this.generateMavenIdentifier(this.getClaritasGroup(path), fmData.mods[0].modId, fmData.mods[0].version)
} }
protected async getModuleName(name: string, path: string): Promise<string> { protected async getModuleName(name: string, path: string): Promise<string> {
return capitalize((await this.getForgeModMetadata(name, path)).mods[0].displayName) return capitalize((await this.getForgeModMetadata(name, path)).mods[0].displayName)
@@ -52,7 +54,7 @@ export class ForgeModStructure113 extends BaseForgeModStructure {
zip.on('error', err => reject(err)) zip.on('error', err => reject(err))
zip.on('ready', () => { zip.on('ready', () => {
try { try {
const res = this.processZip(zip, name) const res = this.processZip(zip, name, path)
zip.close() zip.close()
resolve(res) resolve(res)
return return
@@ -71,7 +73,7 @@ export class ForgeModStructure113 extends BaseForgeModStructure {
}) })
} }
private processZip(zip: StreamZip, name: string): ModsToml { private processZip(zip: StreamZip, name: string, path: string): ModsToml {
// Optifine is a tweak that can be loaded as a forge mod. It does not // Optifine is a tweak that can be loaded as a forge mod. It does not
// appear to contain a mcmod.info class. This a special case we will // appear to contain a mcmod.info class. This a special case we will
@@ -116,12 +118,21 @@ export class ForgeModStructure113 extends BaseForgeModStructure {
const parsed = toml.parse(raw.toString()) as ModsToml const parsed = toml.parse(raw.toString()) as ModsToml
this.forgeModMetadata[name] = parsed this.forgeModMetadata[name] = parsed
} catch (err) { } catch (err) {
ForgeModStructure113.logger.error(`ForgeMod ${name} contains an invalid mods.toml file.`) this.logger.error(`ForgeMod ${name} contains an invalid mods.toml file.`)
} }
} else { } else {
ForgeModStructure113.logger.error(`ForgeMod ${name} does not contain mods.toml file.`) this.logger.error(`ForgeMod ${name} does not contain mods.toml file.`)
} }
const cRes = this.claritasResult?.[path]
if(cRes == null) {
this.logger.error(`Claritas failed to yield metadata for ForgeMod ${name}!`)
this.logger.error('Is this mod malformated or does Claritas need an update?')
}
const claritasId = cRes?.id
const crudeInference = this.attemptCrudeInference(name) const crudeInference = this.attemptCrudeInference(name)
if(this.forgeModMetadata[name] != null) { if(this.forgeModMetadata[name] != null) {
@@ -130,7 +141,7 @@ export class ForgeModStructure113 extends BaseForgeModStructure {
for(const entry of x.mods) { for(const entry of x.mods) {
if(entry.modId === this.EXAMPLE_MOD_ID) { if(entry.modId === this.EXAMPLE_MOD_ID) {
entry.modId = crudeInference.name.toLowerCase() entry.modId = this.discernResult(claritasId, crudeInference.name.toLowerCase())
entry.displayName = crudeInference.name entry.displayName = crudeInference.name
} }
@@ -139,16 +150,16 @@ export class ForgeModStructure113 extends BaseForgeModStructure {
try { try {
const manifest = zip.entryDataSync('META-INF/MANIFEST.MF') const manifest = zip.entryDataSync('META-INF/MANIFEST.MF')
const keys = manifest.toString().split('\n') const keys = manifest.toString().split('\n')
ForgeModStructure113.logger.debug(keys) this.logger.debug(keys)
for (const key of keys) { for (const key of keys) {
const match = ForgeModStructure113.IMPLEMENTATION_VERSION_REGEX.exec(key) const match = ForgeModStructure113.IMPLEMENTATION_VERSION_REGEX.exec(key)
if (match != null) { if (match != null) {
version = match[1] version = match[1]
} }
} }
ForgeModStructure113.logger.debug(`ForgeMod ${name} contains a version wildcard, inferring ${version}`) this.logger.debug(`ForgeMod ${name} contains a version wildcard, inferring ${version}`)
} catch { } catch {
ForgeModStructure113.logger.debug(`ForgeMod ${name} contains a version wildcard yet no MANIFEST.MF.. Defaulting to ${version}`) this.logger.debug(`ForgeMod ${name} contains a version wildcard yet no MANIFEST.MF.. Defaulting to ${version}`)
} }
entry.version = version entry.version = version
} }
@@ -159,7 +170,7 @@ export class ForgeModStructure113 extends BaseForgeModStructure {
modLoader: 'javafml', modLoader: 'javafml',
loaderVersion: '', loaderVersion: '',
mods: [{ mods: [{
modId: crudeInference.name.toLowerCase(), modId: this.discernResult(claritasId, crudeInference.name.toLowerCase()),
version: crudeInference.version, version: crudeInference.version,
displayName: crudeInference.name, displayName: crudeInference.name,
description: '' description: ''

View File

@@ -5,12 +5,9 @@ import { McModInfo } from '../../../../forge/mcmodinfo'
import { McModInfoList } from '../../../../forge/mcmodinfolist' import { McModInfoList } from '../../../../forge/mcmodinfolist'
import { BaseForgeModStructure } from '../forgemod.struct' import { BaseForgeModStructure } from '../forgemod.struct'
import { MinecraftVersion } from '../../../../../util/MinecraftVersion' import { MinecraftVersion } from '../../../../../util/MinecraftVersion'
import { LoggerUtil } from '../../../../../util/LoggerUtil'
export class ForgeModStructure17 extends BaseForgeModStructure { export class ForgeModStructure17 extends BaseForgeModStructure {
private static readonly logger = LoggerUtil.getLogger('ForgeModStructure (1.7)')
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
public static isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { public static isForVersion(version: MinecraftVersion, libraryVersion: string): boolean {
return VersionUtil.isVersionAcceptable(version, [7, 8, 9, 10, 11, 12]) return VersionUtil.isVersionAcceptable(version, [7, 8, 9, 10, 11, 12])
@@ -21,18 +18,23 @@ export class ForgeModStructure17 extends BaseForgeModStructure {
constructor( constructor(
absoluteRoot: string, absoluteRoot: string,
relativeRoot: string, relativeRoot: string,
baseUrl: string baseUrl: string,
minecraftVersion: MinecraftVersion
) { ) {
super(absoluteRoot, relativeRoot, baseUrl) super(absoluteRoot, relativeRoot, baseUrl, minecraftVersion)
} }
public isForVersion(version: MinecraftVersion, libraryVersion: string): boolean { public isForVersion(version: MinecraftVersion, libraryVersion: string): boolean {
return ForgeModStructure17.isForVersion(version, libraryVersion) return ForgeModStructure17.isForVersion(version, libraryVersion)
} }
public getLoggerName(): string {
return 'ForgeModStructure (1.7)'
}
protected async getModuleId(name: string, path: string): Promise<string> { protected async getModuleId(name: string, path: string): Promise<string> {
const fmData = await this.getForgeModMetadata(name, path) const fmData = await this.getForgeModMetadata(name, path)
return this.generateMavenIdentifier(fmData.modid, fmData.version) return this.generateMavenIdentifier(this.getClaritasGroup(path), fmData.modid, fmData.version)
} }
protected async getModuleName(name: string, path: string): Promise<string> { protected async getModuleName(name: string, path: string): Promise<string> {
return capitalize((await this.getForgeModMetadata(name, path)).name) return capitalize((await this.getForgeModMetadata(name, path)).name)
@@ -50,7 +52,7 @@ export class ForgeModStructure17 extends BaseForgeModStructure {
zip.on('error', err => reject(err)) zip.on('error', err => reject(err))
zip.on('ready', () => { zip.on('ready', () => {
try { try {
const res = this.processZip(zip, name) const res = this.processZip(zip, name, path)
zip.close() zip.close()
resolve(res) resolve(res)
return return
@@ -69,7 +71,7 @@ export class ForgeModStructure17 extends BaseForgeModStructure {
}) })
} }
private processZip(zip: StreamZip, name: string): McModInfo { private processZip(zip: StreamZip, name: string, path: string): McModInfo {
// Optifine is a tweak that can be loaded as a forge mod. It does not // Optifine is a tweak that can be loaded as a forge mod. It does not
// appear to contain a mcmod.info class. This a special case we will // appear to contain a mcmod.info class. This a special case we will
// account for. // account for.
@@ -113,38 +115,50 @@ export class ForgeModStructure17 extends BaseForgeModStructure {
} }
} catch (err) { } catch (err) {
ForgeModStructure17.logger.error(`ForgeMod ${name} contains an invalid mcmod.info file.`) this.logger.error(`ForgeMod ${name} contains an invalid mcmod.info file.`)
} }
} else { } else {
ForgeModStructure17.logger.error(`ForgeMod ${name} does not contain mcmod.info file.`) this.logger.error(`ForgeMod ${name} does not contain mcmod.info file.`)
} }
const cRes = this.claritasResult[path]
if(cRes == null) {
this.logger.error(`Claritas failed to yield metadata for ForgeMod ${name}!`)
this.logger.error('Is this mod malformated or does Claritas need an update?')
}
const claritasId = cRes?.id
const claritasVersion = cRes?.version
const claritasName = cRes?.name
// Validate // Validate
const crudeInference = this.attemptCrudeInference(name) const crudeInference = this.attemptCrudeInference(name)
if(this.forgeModMetadata[name] != null) { if(this.forgeModMetadata[name] != null) {
const x = this.forgeModMetadata[name]! const x = this.forgeModMetadata[name]!
if(x.modid == null || x.modid === '' || x.modid === this.EXAMPLE_MOD_ID) { if(x.modid == null || x.modid === '' || x.modid === this.EXAMPLE_MOD_ID) {
x.modid = crudeInference.name.toLowerCase() x.modid = this.discernResult(claritasId, crudeInference.name.toLowerCase())
x.name = crudeInference.name x.name = this.discernResult(claritasName, crudeInference.name)
} }
// Ex. @VERSION@, ${version} // Ex. @VERSION@, ${version}
if(this.forgeModMetadata[name]!.version != null) { if(this.forgeModMetadata[name]!.version != null) {
const isVersionWildcard = this.forgeModMetadata[name]!.version.indexOf('@') > -1 || this.forgeModMetadata[name]!.version.indexOf('$') > -1 const isVersionWildcard = this.forgeModMetadata[name]!.version.indexOf('@') > -1 || this.forgeModMetadata[name]!.version.indexOf('$') > -1
if(isVersionWildcard) { if(isVersionWildcard) {
x.version = crudeInference.version x.version = this.discernResult(claritasVersion, crudeInference.version)
} }
} else { } else {
x.version = crudeInference.version x.version = this.discernResult(claritasVersion, crudeInference.version)
} }
} else { } else {
this.forgeModMetadata[name] = ({ this.forgeModMetadata[name] = ({
modid: crudeInference.name.toLowerCase(), modid: this.discernResult(claritasId, crudeInference.name.toLowerCase()),
name: crudeInference.name, name: this.discernResult(claritasName, crudeInference.name),
version: crudeInference.version version: this.discernResult(claritasVersion, crudeInference.version)
}) as McModInfo }) as McModInfo
} }

View File

@@ -3,23 +3,29 @@ import { Type, TypeMetadata } from 'helios-distribution-types'
import { Stats } from 'fs-extra' import { Stats } from 'fs-extra'
import { join } from 'path' import { join } from 'path'
import { resolve } from 'url' import { resolve } from 'url'
import { MinecraftVersion } from '../../../../util/MinecraftVersion'
export class LibraryStructure extends ModuleStructure { export class LibraryStructure extends ModuleStructure {
constructor( constructor(
absoluteRoot: string, absoluteRoot: string,
relativeRoot: string, relativeRoot: string,
baseUrl: string baseUrl: string,
minecraftVersion: MinecraftVersion
) { ) {
super(absoluteRoot, relativeRoot, 'libraries', baseUrl, Type.Library, (name: string) => { super(absoluteRoot, relativeRoot, 'libraries', baseUrl, minecraftVersion, Type.Library, (name: string) => {
return name.toLowerCase().endsWith(TypeMetadata[this.type].defaultExtension!) return name.toLowerCase().endsWith(TypeMetadata[this.type].defaultExtension!)
}) })
} }
public getLoggerName(): string {
return 'LibraryStructure'
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async getModuleId(name: string, path: string): Promise<string> { protected async getModuleId(name: string, path: string): Promise<string> {
const inference = this.attemptCrudeInference(name) const inference = this.attemptCrudeInference(name)
return this.generateMavenIdentifier(inference.name, inference.version) return this.generateMavenIdentifier(this.getDefaultGroup(), inference.name, inference.version)
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async getModuleName(name: string, path: string): Promise<string> { protected async getModuleName(name: string, path: string): Promise<string> {

View File

@@ -6,6 +6,8 @@ import { resolve } from 'url'
import { capitalize } from '../../../../util/stringutils' import { capitalize } from '../../../../util/stringutils'
import { LiteMod } from '../../../liteloader/litemod' import { LiteMod } from '../../../liteloader/litemod'
import { ToggleableModuleStructure } from './toggleablemodule.struct' import { ToggleableModuleStructure } from './toggleablemodule.struct'
import { MinecraftVersion } from '../../../../util/MinecraftVersion'
import { LibraryType } from '../../../claritas/ClaritasLibraryType'
export class LiteModStructure extends ToggleableModuleStructure { export class LiteModStructure extends ToggleableModuleStructure {
@@ -14,14 +16,19 @@ export class LiteModStructure extends ToggleableModuleStructure {
constructor( constructor(
absoluteRoot: string, absoluteRoot: string,
relativeRoot: string, relativeRoot: string,
baseUrl: string baseUrl: string,
minecraftVersion: MinecraftVersion
) { ) {
super(absoluteRoot, relativeRoot, 'litemods', baseUrl, Type.LiteMod) super(absoluteRoot, relativeRoot, 'litemods', baseUrl, minecraftVersion, Type.LiteMod)
}
public getLoggerName(): string {
return 'LiteModStructure'
} }
protected async getModuleId(name: string, path: string): Promise<string> { protected async getModuleId(name: string, path: string): Promise<string> {
const liteModData = await this.getLiteModMetadata(name, path) const liteModData = await this.getLiteModMetadata(name, path)
return this.generateMavenIdentifier(liteModData.name, `${liteModData.version}-${liteModData.mcversion}`) return this.generateMavenIdentifier(this.getClaritasGroup(path), liteModData.name, `${liteModData.version}-${liteModData.mcversion}`)
} }
protected async getModuleName(name: string, path: string): Promise<string> { protected async getModuleName(name: string, path: string): Promise<string> {
return capitalize((await this.getLiteModMetadata(name, path)).name) return capitalize((await this.getLiteModMetadata(name, path)).name)
@@ -35,6 +42,10 @@ export class LiteModStructure extends ToggleableModuleStructure {
return null return null
} }
protected getClaritasType(): LibraryType {
return LibraryType.LITELOADER
}
private getLiteModMetadata(name: string, path: string): Promise<LiteMod> { private getLiteModMetadata(name: string, path: string): Promise<LiteMod> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!Object.prototype.hasOwnProperty.call(this.liteModMetadata, name)) { if (!Object.prototype.hasOwnProperty.call(this.liteModMetadata, name)) {

View File

@@ -3,17 +3,35 @@ import { lstat, pathExists, readdir, readFile, Stats } from 'fs-extra'
import { Module, Type, TypeMetadata } from 'helios-distribution-types' import { Module, Type, TypeMetadata } from 'helios-distribution-types'
import { resolve } from 'path' import { resolve } from 'path'
import { BaseModelStructure } from '../basemodel.struct' import { BaseModelStructure } from '../basemodel.struct'
import { LibraryType } from '../../../claritas/ClaritasLibraryType'
import { ClaritasResult, ClaritasModuleMetadata } from '../../../claritas/ClaritasResult'
import { ClaritasWrapper } from '../../../../util/java/ClaritasWrapper'
import { MinecraftVersion } from '../../../../util/MinecraftVersion'
export interface ModuleCandidate {
file: string
filePath: string
stats: Stats
}
export interface ClaritasException {
exceptionName: string
proxyMetadata: ClaritasModuleMetadata
}
export abstract class ModuleStructure extends BaseModelStructure<Module> { export abstract class ModuleStructure extends BaseModelStructure<Module> {
private readonly crudeRegex = /(.+?)-(.+).[jJ][aA][rR]/ private readonly crudeRegex = /(.+?)-(.+).[jJ][aA][rR]/
protected readonly DEFAULT_VERSION = '0.0.0' protected readonly DEFAULT_VERSION = '0.0.0'
protected claritasResult!: ClaritasResult
constructor( constructor(
absoluteRoot: string, absoluteRoot: string,
relativeRoot: string, relativeRoot: string,
structRoot: string, structRoot: string,
baseUrl: string, baseUrl: string,
protected minecraftVersion: MinecraftVersion,
protected type: Type, protected type: Type,
protected filter?: ((name: string, path: string, stats: Stats) => boolean) protected filter?: ((name: string, path: string, stats: Stats) => boolean)
) { ) {
@@ -22,14 +40,18 @@ export abstract class ModuleStructure extends BaseModelStructure<Module> {
public async getSpecModel(): Promise<Module[]> { public async getSpecModel(): Promise<Module[]> {
if (this.resolvedModels == null) { if (this.resolvedModels == null) {
this.resolvedModels = await this._doModuleRetrieval(this.containerDirectory) this.resolvedModels = await this._doModuleRetrieval(await this._doModuleDiscovery(this.containerDirectory))
} }
return this.resolvedModels return this.resolvedModels
} }
protected generateMavenIdentifier(name: string, version: string): string { protected getDefaultGroup(): string {
return `generated.${this.type.toLowerCase()}:${name}:${version}@${TypeMetadata[this.type].defaultExtension}` return `generated.${this.type.toLowerCase()}`
}
protected generateMavenIdentifier(group: string, id: string, version: string): string {
return `${group}:${id}:${version}@${TypeMetadata[this.type].defaultExtension}`
} }
protected attemptCrudeInference(name: string): { name: string, version: string } { protected attemptCrudeInference(name: string): { name: string, version: string } {
@@ -47,6 +69,18 @@ export abstract class ModuleStructure extends BaseModelStructure<Module> {
} }
} }
protected getClaritasGroup(path: string): string {
return this.claritasResult[path]?.group || this.getDefaultGroup()
}
protected getClaritasExceptions(): ClaritasException[] {
return []
}
protected getClaritasType(): LibraryType | null {
return null
}
protected async abstract getModuleId(name: string, path: string): Promise<string> protected async abstract getModuleId(name: string, path: string): Promise<string>
protected async abstract getModuleName(name: string, path: string): Promise<string> protected async abstract getModuleName(name: string, path: string): Promise<string>
protected async abstract getModuleUrl(name: string, path: string, stats: Stats): Promise<string> protected async abstract getModuleUrl(name: string, path: string, stats: Stats): Promise<string>
@@ -71,9 +105,9 @@ export abstract class ModuleStructure extends BaseModelStructure<Module> {
return mdl return mdl
} }
protected async _doModuleRetrieval(scanDirectory: string): Promise<Module[]> { protected async _doModuleDiscovery(scanDirectory: string): Promise<ModuleCandidate[]> {
const accumulator: Module[] = [] const moduleCandidates: ModuleCandidate[] = []
if (await pathExists(scanDirectory)) { if (await pathExists(scanDirectory)) {
const files = await readdir(scanDirectory) const files = await readdir(scanDirectory)
@@ -82,11 +116,61 @@ export abstract class ModuleStructure extends BaseModelStructure<Module> {
const stats = await lstat(filePath) const stats = await lstat(filePath)
if (stats.isFile()) { if (stats.isFile()) {
if(this.filter == null || this.filter(file, filePath, stats)) { if(this.filter == null || this.filter(file, filePath, stats)) {
accumulator.push(await this.parseModule(file, filePath, stats)) moduleCandidates.push({file, filePath, stats})
}
}
}
} }
return moduleCandidates
}
protected async _doModuleRetrieval(moduleCandidates: ModuleCandidate[], options?: {
preProcess?: (candidate: ModuleCandidate) => void
postProcess?: (module: Module) => void
}): Promise<Module[]> {
const accumulator: Module[] = []
if(moduleCandidates.length > 0) {
// Invoke Claritas
if(this.getClaritasType() != null) {
const claritasExecutor = new ClaritasWrapper()
let claritasCandidates = moduleCandidates
const exceptionCandidates: [ModuleCandidate, ClaritasException][] = []
for(const exception of this.getClaritasExceptions()) {
const exceptionCandidate = moduleCandidates.find((value) => value.file.toLowerCase().indexOf(exception.exceptionName) > -1)
if(exceptionCandidate != null) {
exceptionCandidates.push([exceptionCandidate, exception])
claritasCandidates = claritasCandidates.filter((value) => value.file.toLowerCase().indexOf(exception.exceptionName) === -1)
} }
} }
this.claritasResult = await claritasExecutor.execute(
this.getClaritasType()!,
this.minecraftVersion,
claritasCandidates.map(entry => entry.filePath)
)
if(this.claritasResult == null) {
this.logger.error('Failed to process Claritas result!')
} else {
for(const [candidate, exception] of exceptionCandidates) {
this.claritasResult[candidate.filePath] = exception.proxyMetadata
}
}
}
// Process Modules
for(const candidate of moduleCandidates) {
options?.preProcess?.(candidate)
const mdl = await this.parseModule(candidate.file, candidate.filePath, candidate.stats)
options?.postProcess?.(mdl)
accumulator.push(mdl)
}
} }
return accumulator return accumulator

View File

@@ -1,7 +1,8 @@
import { ModuleStructure } from './module.struct' import { ModuleStructure, ModuleCandidate } from './module.struct'
import { Type, Module } from 'helios-distribution-types' import { Type, Module } from 'helios-distribution-types'
import { Stats, mkdirs } from 'fs-extra' import { Stats, mkdirs } from 'fs-extra'
import { resolve } from 'path' import { resolve } from 'path'
import { MinecraftVersion } from '../../../../util/MinecraftVersion'
export enum ToggleableNamespace { export enum ToggleableNamespace {
@@ -11,6 +12,10 @@ export enum ToggleableNamespace {
} }
export interface ToggleableModuleCandidate extends ModuleCandidate {
namespace: ToggleableNamespace
}
export abstract class ToggleableModuleStructure extends ModuleStructure { export abstract class ToggleableModuleStructure extends ModuleStructure {
private activeNamespace: string | undefined private activeNamespace: string | undefined
@@ -20,10 +25,11 @@ export abstract class ToggleableModuleStructure extends ModuleStructure {
relativeRoot: string, relativeRoot: string,
structRoot: string, structRoot: string,
baseUrl: string, baseUrl: string,
protected type: Type, minecraftVersion: MinecraftVersion,
protected filter?: ((name: string, path: string, stats: Stats) => boolean) type: Type,
filter?: ((name: string, path: string, stats: Stats) => boolean)
) { ) {
super(absoluteRoot, relativeRoot, structRoot, baseUrl, type, filter) super(absoluteRoot, relativeRoot, structRoot, baseUrl, minecraftVersion, type, filter)
} }
public async init(): Promise<void> { public async init(): Promise<void> {
@@ -36,13 +42,21 @@ export abstract class ToggleableModuleStructure extends ModuleStructure {
public async getSpecModel(): Promise<Module[]> { public async getSpecModel(): Promise<Module[]> {
if (this.resolvedModels == null) { if (this.resolvedModels == null) {
this.resolvedModels = [] const moduleCandidates: ToggleableModuleCandidate[] = []
for(const value of Object.values(ToggleableNamespace)) { for(const value of Object.values(ToggleableNamespace)) {
this.activeNamespace = value moduleCandidates.push(...(await super._doModuleDiscovery(resolve(this.containerDirectory, value))).map(val => ({...val, namespace: value})))
const models = await this._doModuleRetrieval(resolve(this.containerDirectory, value))
models.forEach(this.getNamespaceMapper(value))
this.resolvedModels = this.resolvedModels.concat(models)
} }
this.resolvedModels = await this._doModuleRetrieval(moduleCandidates, {
preProcess: (candidate) => {
this.activeNamespace = (candidate as ToggleableModuleCandidate).namespace
},
postProcess: (module) => {
this.getNamespaceMapper(this.activeNamespace as ToggleableNamespace)(module)
}
})
// Cleanup
this.activeNamespace = undefined this.activeNamespace = undefined
} }

View File

@@ -9,12 +9,9 @@ import { MiscFileStructure } from './module/file.struct'
import { LiteModStructure } from './module/litemod.struct' import { LiteModStructure } from './module/litemod.struct'
import { LibraryStructure } from './module/library.struct' import { LibraryStructure } from './module/library.struct'
import { MinecraftVersion } from '../../../util/MinecraftVersion' import { MinecraftVersion } from '../../../util/MinecraftVersion'
import { LoggerUtil } from '../../../util/LoggerUtil'
export class ServerStructure extends BaseModelStructure<Server> { export class ServerStructure extends BaseModelStructure<Server> {
private static readonly logger = LoggerUtil.getLogger('ServerStructure')
private readonly ID_REGEX = /(.+-(.+)$)/ private readonly ID_REGEX = /(.+-(.+)$)/
private readonly SERVER_META_FILE = 'servermeta.json' private readonly SERVER_META_FILE = 'servermeta.json'
@@ -25,6 +22,10 @@ export class ServerStructure extends BaseModelStructure<Server> {
super(absoluteRoot, '', 'servers', baseUrl) super(absoluteRoot, '', 'servers', baseUrl)
} }
public getLoggerName(): string {
return 'ServerStructure'
}
public async getSpecModel(): Promise<Server[]> { public async getSpecModel(): Promise<Server[]> {
if (this.resolvedModels == null) { if (this.resolvedModels == null) {
this.resolvedModels = await this._doSeverRetrieval() this.resolvedModels = await this._doSeverRetrieval()
@@ -45,7 +46,7 @@ export class ServerStructure extends BaseModelStructure<Server> {
const relativeServerRoot = join(this.relativeRoot, effectiveId) const relativeServerRoot = join(this.relativeRoot, effectiveId)
if (await pathExists(absoluteServerRoot)) { if (await pathExists(absoluteServerRoot)) {
ServerStructure.logger.error('Server already exists! Aborting.') this.logger.error('Server already exists! Aborting.')
return return
} }
@@ -66,7 +67,7 @@ export class ServerStructure extends BaseModelStructure<Server> {
} }
if (options.liteloaderVersion != null) { if (options.liteloaderVersion != null) {
const lms = new LiteModStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl) const lms = new LiteModStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl, minecraftVersion)
await lms.init() await lms.init()
serverMetaOpts.liteloaderVersion = options.liteloaderVersion serverMetaOpts.liteloaderVersion = options.liteloaderVersion
} }
@@ -74,10 +75,10 @@ export class ServerStructure extends BaseModelStructure<Server> {
const serverMeta: ServerMeta = getDefaultServerMeta(id, minecraftVersion.toString(), serverMetaOpts) const serverMeta: ServerMeta = getDefaultServerMeta(id, minecraftVersion.toString(), serverMetaOpts)
await writeFile(resolvePath(absoluteServerRoot, this.SERVER_META_FILE), JSON.stringify(serverMeta, null, 2)) await writeFile(resolvePath(absoluteServerRoot, this.SERVER_META_FILE), JSON.stringify(serverMeta, null, 2))
const libS = new LibraryStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl) const libS = new LibraryStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl, minecraftVersion)
await libS.init() await libS.init()
const mfs = new MiscFileStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl) const mfs = new MiscFileStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl, minecraftVersion)
await mfs.init() await mfs.init()
} }
@@ -93,8 +94,8 @@ export class ServerStructure extends BaseModelStructure<Server> {
const match = this.ID_REGEX.exec(file) const match = this.ID_REGEX.exec(file)
if (match == null) { if (match == null) {
ServerStructure.logger.warn(`Server directory ${file} does not match the defined standard.`) this.logger.warn(`Server directory ${file} does not match the defined standard.`)
ServerStructure.logger.warn('All server ids must end with -<minecraft version> (ex. -1.12.2)') this.logger.warn('All server ids must end with -<minecraft version> (ex. -1.12.2)')
continue continue
} }
@@ -110,7 +111,7 @@ export class ServerStructure extends BaseModelStructure<Server> {
} }
if (!iconUrl) { if (!iconUrl) {
ServerStructure.logger.warn(`No icon file found for server ${file}.`) this.logger.warn(`No icon file found for server ${file}.`)
} }
// Read server meta // Read server meta
@@ -146,16 +147,16 @@ export class ServerStructure extends BaseModelStructure<Server> {
if(serverMeta.liteloader) { if(serverMeta.liteloader) {
const liteModStruct = new LiteModStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl) const liteModStruct = new LiteModStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl, minecraftVersion)
const liteModModules = await liteModStruct.getSpecModel() const liteModModules = await liteModStruct.getSpecModel()
modules.push(...liteModModules) modules.push(...liteModModules)
} }
const libraryStruct = new LibraryStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl) const libraryStruct = new LibraryStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl, minecraftVersion)
const libraryModules = await libraryStruct.getSpecModel() const libraryModules = await libraryStruct.getSpecModel()
modules.push(...libraryModules) modules.push(...libraryModules)
const fileStruct = new MiscFileStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl) const fileStruct = new MiscFileStructure(absoluteServerRoot, relativeServerRoot, this.baseUrl, minecraftVersion)
const fileModules = await fileStruct.getSpecModel() const fileModules = await fileStruct.getSpecModel()
modules.push(...fileModules) modules.push(...fileModules)
@@ -174,7 +175,7 @@ export class ServerStructure extends BaseModelStructure<Server> {
}) })
} else { } else {
ServerStructure.logger.warn(`Path ${file} in server directory is not a directory!`) this.logger.warn(`Path ${file} in server directory is not a directory!`)
} }
} }
return accumulator return accumulator

View File

@@ -18,6 +18,10 @@ export class LibRepoStructure extends BaseMavenRepo {
super(absoluteRoot, relativeRoot, 'lib') super(absoluteRoot, relativeRoot, 'lib')
} }
public getLoggerName(): string {
return 'LibRepoStructure'
}
public getLocalForge(version: string, classifier?: string): string { public getLocalForge(version: string, classifier?: string): string {
return this.getArtifactByComponents( return this.getArtifactByComponents(
LibRepoStructure.FORGE_GROUP, LibRepoStructure.FORGE_GROUP,

View File

@@ -17,6 +17,10 @@ export class RepoStructure extends BaseFileStructure {
this.versionRepoStruct = new VersionRepoStructure(this.containerDirectory, this.relativeRoot) this.versionRepoStruct = new VersionRepoStructure(this.containerDirectory, this.relativeRoot)
} }
public getLoggerName(): string {
return 'RepoStructure'
}
public async init(): Promise<void> { public async init(): Promise<void> {
super.init() super.init()
await this.libRepoStruct.init() await this.libRepoStruct.init()

View File

@@ -12,6 +12,10 @@ export class VersionRepoStructure extends BaseFileStructure {
super(absoluteRoot, relativeRoot, 'versions') super(absoluteRoot, relativeRoot, 'versions')
} }
public getLoggerName(): string {
return 'VersionRepoStructure'
}
public getFileName(minecraftVersion: MinecraftVersion, forgeVersion: string): string { public getFileName(minecraftVersion: MinecraftVersion, forgeVersion: string): string {
return `${minecraftVersion}-forge-${forgeVersion}` return `${minecraftVersion}-forge-${forgeVersion}`
} }

View File

@@ -5,7 +5,7 @@ import { basename, join } from 'path'
import { VersionManifestFG2 } from '../../../model/forge/VersionManifestFG2' import { VersionManifestFG2 } from '../../../model/forge/VersionManifestFG2'
import { LibRepoStructure } from '../../../model/struct/repo/librepo.struct' import { LibRepoStructure } from '../../../model/struct/repo/librepo.struct'
import { MavenUtil } from '../../../util/maven' import { MavenUtil } from '../../../util/maven'
import { PackXZExtractWrapper } from '../../../util/PackXZExtractWrapper' import { PackXZExtractWrapper } from '../../../util/java/PackXZExtractWrapper'
import { VersionUtil } from '../../../util/versionutil' import { VersionUtil } from '../../../util/versionutil'
import { ForgeResolver } from '../forge.resolver' import { ForgeResolver } from '../forge.resolver'
import { MinecraftVersion } from '../../../util/MinecraftVersion' import { MinecraftVersion } from '../../../util/MinecraftVersion'
@@ -227,7 +227,8 @@ export class ForgeGradle2Adapter extends ForgeResolver {
} }
ForgeGradle2Adapter.logger.debug('Spawning PackXZExtract.') ForgeGradle2Adapter.logger.debug('Spawning PackXZExtract.')
await PackXZExtractWrapper.extractUnpack(files) const packXZExecutor = new PackXZExtractWrapper()
await packXZExecutor.extractUnpack(files)
ForgeGradle2Adapter.logger.debug('All files extracted, calculating hashes..') ForgeGradle2Adapter.logger.debug('All files extracted, calculating hashes..')
for (const entry of processingQueue) { for (const entry of processingQueue) {

View File

@@ -7,7 +7,7 @@ import { LibRepoStructure } from '../../../model/struct/repo/librepo.struct'
import { pathExists, remove, mkdirs, copy, writeFile, readFile, lstat, move, writeJson } from 'fs-extra' import { pathExists, remove, mkdirs, copy, writeFile, readFile, lstat, move, writeJson } from 'fs-extra'
import { join, basename, dirname } from 'path' import { join, basename, dirname } from 'path'
import { spawn } from 'child_process' import { spawn } from 'child_process'
import { JavaUtil } from '../../../util/javautil' import { JavaUtil } from '../../../util/java/javautil'
import { VersionManifestFG3 } from '../../../model/forge/VersionManifestFG3' import { VersionManifestFG3 } from '../../../model/forge/VersionManifestFG3'
import { MavenUtil } from '../../../util/maven' import { MavenUtil } from '../../../util/maven'
import { createHash } from 'crypto' import { createHash } from 'crypto'

View File

@@ -1,47 +0,0 @@
import { spawn } from 'child_process'
import { join } from 'path'
import { JavaUtil } from './javautil'
import { LoggerUtil } from './LoggerUtil'
export class PackXZExtractWrapper {
private static readonly logger = LoggerUtil.getLogger('PackXZExtract')
public static getPackXZExtract(): string {
return join(process.cwd(), 'libraries', 'java', 'PackXZExtract.jar')
}
public static extractUnpack(paths: string[]): Promise<void> {
return PackXZExtractWrapper.execute('-packxz', paths)
}
public static extract(paths: string[]): Promise<void> {
return PackXZExtractWrapper.execute('-xz', paths)
}
public static unpack(paths: string[]): Promise<void> {
return PackXZExtractWrapper.execute('-pack', paths)
}
private static execute(command: string, paths: string[]): Promise<void> {
return new Promise((resolve, reject) => {
const child = spawn(JavaUtil.getJavaExecutable(), [
'-jar',
PackXZExtractWrapper.getPackXZExtract(),
command,
paths.join(',')
])
child.stdout.on('data', (data) => PackXZExtractWrapper.logger.info(data.toString('utf8').trim()))
child.stderr.on('data', (data) => PackXZExtractWrapper.logger.error(data.toString('utf8').trim()))
child.on('close', code => {
PackXZExtractWrapper.logger.info('Exited with code', code)
resolve()
})
child.on('error', (err) => {
PackXZExtractWrapper.logger.info('Error during process execution', err)
reject(err)
})
})
}
}

View File

@@ -42,7 +42,7 @@ export class VersionSegmentedRegistry {
): BaseForgeModStructure { ): BaseForgeModStructure {
for (const impl of VersionSegmentedRegistry.FORGEMOD_STRUCT_IML) { for (const impl of VersionSegmentedRegistry.FORGEMOD_STRUCT_IML) {
if (impl.isForVersion(minecraftVersion, forgeVersion)) { if (impl.isForVersion(minecraftVersion, forgeVersion)) {
return new impl(absoluteRoot, relativeRoot, baseUrl) return new impl(absoluteRoot, relativeRoot, baseUrl, minecraftVersion)
} }
} }
throw new Error(`No forge mod structure found for Minecraft ${minecraftVersion}!`) throw new Error(`No forge mod structure found for Minecraft ${minecraftVersion}!`)

View File

@@ -0,0 +1,32 @@
import { JarExecutor } from './JarExecutor'
import { join } from 'path'
import { ClaritasResult } from '../../model/claritas/ClaritasResult'
import { MinecraftVersion } from '../MinecraftVersion'
import { LibraryType } from '../../model/claritas/ClaritasLibraryType'
export class ClaritasWrapper extends JarExecutor<ClaritasResult> {
constructor() {
super('Claritas')
this.stdoutListeners.push((data) => {
const clean = data.toString('utf8').trim() as string
const spike = 'results::'
if(clean.startsWith(spike)) {
this.lastExecutionResult = JSON.parse(clean.substr(spike.length)) as ClaritasResult
}
})
}
protected getJarPath(): string {
return join(process.cwd(), 'libraries', 'java', 'Claritas.jar')
}
public execute(libraryType: LibraryType, mcVersion: MinecraftVersion, absoluteJarPaths: string[]): Promise<ClaritasResult> {
return super.executeJar(
'--absoluteJarPaths', absoluteJarPaths.join(','),
'--libraryType', libraryType,
'--mcVersion', mcVersion.toString()
)
}
}

View File

@@ -0,0 +1,48 @@
import { JavaUtil } from './javautil'
import { Logger } from 'winston'
import { spawn } from 'child_process'
import { LoggerUtil } from '../LoggerUtil'
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 lastExecutionResult!: T
protected constructor(loggerName: string) {
this.logger = LoggerUtil.getLogger(loggerName)
}
protected abstract getJarPath(): string
protected executeJar(...args: string[]): Promise<T> {
this.lastExecutionResult = undefined!
return new Promise((resolve, reject) => {
const child = spawn(JavaUtil.getJavaExecutable(), [
'-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))
child.on('close', code => {
this.logger.info('Exited with code', code)
resolve(this.lastExecutionResult)
})
child.on('error', (err) => {
this.logger.info('Error during process execution', err)
reject(err)
})
})
}
}

View File

@@ -0,0 +1,30 @@
import { join } from 'path'
import { JarExecutor } from './JarExecutor'
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)
}
}