HeliosLauncher/src/main/distromanager.ts

297 lines
8.7 KiB
TypeScript

import request from 'request'
import { Distribution, Module, Type, TypeMetadata, Server } from 'helios-distribution-types'
import { readJson, writeJson } from 'fs-extra'
import { join } from 'path'
import { LoggerUtil } from './loggerutil'
import { ConfigManager } from './configmanager'
const logger = new LoggerUtil('%c[DistroManager]', 'color: #a02d2a; font-weight: bold')
interface ArtifactMeta {
group: string
artifact: string
version: string
classifier?: string
extension: string
}
export class ModuleWrapper {
private artifactMeta: ArtifactMeta
private subModules: ModuleWrapper[] = []
constructor(public module: Module, private serverId: string) {
this.artifactMeta = this.resolveMetaData()
this.resolveArtifactPath()
this.resolveRequired()
if (this.module.subModules != null) {
this.subModules = this.module.subModules.map(mdl => new ModuleWrapper(mdl, serverId))
}
}
private resolveMetaData(): ArtifactMeta {
try {
const m0 = this.module.id.split('@')
const m1 = m0[0].split(':')
return {
group: m1[0] || '???',
artifact: m1[1] || '???',
version: m1[2] || '???',
classifier: m1[3] || undefined,
extension: m0[1] || TypeMetadata[this.module.type].defaultExtension || 'undefined'
}
} catch (err) {
logger.error('Improper ID for module', this.module.id, err)
return {
group: '???',
artifact: '???',
version: '???',
classifier: undefined,
extension: '???'
}
}
}
private resolveArtifactPath(): void {
const relativePath = this.module.artifact.path == null ? join(
...this.artifactMeta.group.split('.'),
this.artifactMeta.artifact,
this.artifactMeta.version,
`${this.artifactMeta.artifact}-${this.artifactMeta.version}${this.artifactMeta.classifier != undefined ? `-${this.artifactMeta.classifier}` : ''}.${this.artifactMeta.extension}`
) : this.module.artifact.path
switch (this.module.type){
case Type.Library:
case Type.ForgeHosted:
case Type.LiteLoader:
this.module.artifact.path = join(ConfigManager.getCommonDirectory(), 'libraries', relativePath)
break
case Type.ForgeMod:
case Type.LiteMod:
this.module.artifact.path = join(ConfigManager.getCommonDirectory(), 'modstore', relativePath)
break
case Type.VersionManifest:
this.module.artifact.path = join(ConfigManager.getCommonDirectory(), 'versions', this.module.id, `${this.module.id}.json`)
break
case Type.File:
default:
this.module.artifact.path = join(ConfigManager.getInstanceDirectory(), this.serverId, relativePath)
break
}
}
private resolveRequired(): void {
if (this.module.required == null) {
this.module.required = {
value: true,
def: true
}
} else {
if (this.module.required.value == null) {
this.module.required.value = true
}
if (this.module.required.def == null) {
this.module.required.def = true
}
}
}
/**
* @returns {string} The maven identifier of this module's artifact.
*/
public getArtifact(): string {
return this.artifactMeta.artifact
}
/**
* @returns {string} The maven group of this module's artifact.
*/
public getGroup(): string {
return this.artifactMeta.group
}
/**
* @returns {string} The version of this module's artifact.
*/
public getVersion(): string {
return this.artifactMeta.version
}
/**
* @returns {string | undefined} The classifier of this module's artifact
*/
public getClassifier(): string | undefined {
return this.artifactMeta.classifier
}
/**
* @returns {string} The extension of this module's artifact.
*/
public getExtension(): string {
return this.artifactMeta.extension
}
/**
* @returns {string} The identifier without he version or extension.
*/
public getVersionlessID(): string {
return this.artifactMeta.group + ':' + this.artifactMeta.artifact
}
/**
* @returns {string} The identifier without the extension.
*/
public getExtensionlessID(): string {
return this.module.id.split('@')[0]
}
/**
* @returns {boolean} Whether or not this module has sub modules.
*/
public hasSubModules(): boolean {
return this.module.subModules != null
}
public getWrappedSubmodules(): ModuleWrapper[] {
return this.subModules
}
}
export class ServerWrapper {
private modules: ModuleWrapper[] = []
constructor(public server: Server) {
this.server.modules.map(mdl => new ModuleWrapper(mdl, server.id))
}
public getWrappedModules() {
return this.modules
}
}
export class DistributionWrapper {
private mainServer: ServerWrapper | null = null
private servers: ServerWrapper[]
constructor(public distro: Distribution) {
this.servers = this.distro.servers.map(serv => new ServerWrapper(serv))
this.resolveMainServer()
}
private resolveMainServer(): void {
for(const serv of this.servers){
if(serv.server.mainServer){
this.mainServer = serv
return
}
}
// If no server declares default_selected, default to the first one declared.
this.mainServer = (this.servers.length > 0) ? this.servers[0] : null
}
public getServer(id: string): ServerWrapper | null {
for(const serv of this.servers){
if(serv.server.id === id){
return serv
}
}
return null
}
public getMainServer(): ServerWrapper | null {
return this.mainServer
}
}
export class DistroManager {
private static readonly DISTRO_PATH = join(ConfigManager.getLauncherDirectory(), 'distribution.json')
private static readonly DEV_PATH = join(ConfigManager.getLauncherDirectory(), 'dev_distribution.json')
private static readonly DISTRIBUTION_URL = 'http://mc.westeroscraft.com/WesterosCraftLauncher/distribution.json'
// private static readonly DISTRIBUTION_URL = 'https://gist.githubusercontent.com/dscalzi/53b1ba7a11d26a5c353f9d5ae484b71b/raw/'
private static DEV_MODE = false
private static distro: DistributionWrapper
public static isDevMode() {
return DistroManager.DEV_MODE
}
public static setDevMode(value: boolean) {
if(value){
logger.log('Developer mode enabled.')
logger.log('If you don\'t know what that means, revert immediately.')
} else {
logger.log('Developer mode disabled.')
}
DistroManager.DEV_MODE = value
}
public static pullRemote(): Promise<DistributionWrapper> {
if(DistroManager.DEV_MODE){
return DistroManager.pullLocal()
}
return new Promise((resolve, reject) => {
const opts = {
url: DistroManager.DISTRIBUTION_URL,
timeout: 2500
}
const distroDest = join(ConfigManager.getLauncherDirectory(), 'distribution.json')
request(opts, async (error, resp, body) => {
if(!error){
let data: Distribution
try {
data = JSON.parse(body) as Distribution
DistroManager.distro = new DistributionWrapper(data)
} catch (e) {
reject(e)
return;
}
try {
await writeJson(distroDest, DistroManager.distro)
resolve(DistroManager.distro)
} catch (err) {
reject(err)
}
} else {
reject(error)
}
})
})
}
public static async pullLocal(): Promise<DistributionWrapper> {
const data = await readJson(DistroManager.DEV_MODE ? DistroManager.DEV_PATH : DistroManager.DISTRO_PATH) as Distribution
DistroManager.distro = new DistributionWrapper(data)
return DistroManager.distro
}
public static getDistribution(): DistributionWrapper {
return DistroManager.distro
}
}