Skip to content

Commit

Permalink
refactor: modpack service
Browse files Browse the repository at this point in the history
  • Loading branch information
ci010 committed Apr 14, 2021
1 parent 96be4b1 commit 03cf878
Show file tree
Hide file tree
Showing 22 changed files with 658 additions and 251 deletions.
53 changes: 41 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
"node-powershell": "^4.0.0",
"node-watch": "^0.6.4",
"original-fs": "^1.1.0",
"reflect-metadata": "^0.1.13",
"three": "^0.110.0",
"uuid": "^7.0.3",
"vue": "^2.6.11",
Expand All @@ -76,7 +75,6 @@
"yazl": "^2.5.1"
},
"external": [
"reflect-metadata",
"applicationinsights",
"atomically",
"semver",
Expand Down
193 changes: 70 additions & 123 deletions src/main/entities/hmclModpack.ts
Original file line number Diff line number Diff line change
@@ -1,148 +1,95 @@
import { Version } from '@xmcl/core'
import { openEntryReadStream, readAllEntries, readEntry, walkEntriesGenerator } from '@xmcl/unzip'
import { CancelledError, TaskBase } from '@xmcl/task'
import { open, openEntryReadStream, readAllEntries, readEntry, walkEntriesGenerator } from '@xmcl/unzip'
import { createWriteStream } from 'fs'
import { Readable } from 'node:stream'
import { join } from 'path'
import { ZipFile } from 'yauzl'
import { Entry, ZipFile } from 'yauzl'
import { pipeline } from '../util/fs'
import { HMCLModpack, HMCLServerManagedModpack, HMCLVersion } from '/@shared/entities/hmclModpack'

/**
* https://github.com/huanghongxun/HMCL/wiki/HMCL-%E6%95%B4%E5%90%88%E5%8C%85%E8%A7%84%E8%8C%83
* The modpack.json
* Read the metadata of the modpack
* @param zip The modpack zip
* @returns The HMCL modpack metadata
*/
export interface HMCLModpack {
name: string
author: string
/**
* 整合包版本
*/
version: string
/**
* 整合包的Minecraft版本,建议填写,在HMCL 2.9以后生效
*/
gameVersion: string
/**
* The description. It can be html or markdown.
*/
description: string
export async function readHMCLModpackMetadata(zip: ZipFile) {
for await (const entry of walkEntriesGenerator(zip)) {
if (entry.fileName === 'server-manifest.json') {
return readEntry(zip, entry).then(b => JSON.parse(b.toString()) as HMCLServerManagedModpack)
}
}
throw new Error()
}

export interface HMCLServerManagedModpack extends HMCLModpack {
/**
* Your modpack websites. This is displayed to user.
*/
url?: string[] | string
/**
* If this modpack allow the server update this pack.
* Then this should be modpack root url for each file.
*
* For example, once the modpack is deployed, a file `overrides/config/forge.cfg` is deployed.
* If the fileApi is `https://some.website.com/modpack`, then the file should be avaiable on `https://some.website.com/modpack/overrides/config/forge.cfg`
*/
fileApi: string
/**
* The update rule
* - `full`, if user modifiess the modpack, then it will be overwrited once the modpack is updated. User added files will be removed!
* - `normal`, user is allowed to modify the modpack in this mode. The modified file won't be overwrited.
* @default full
*/
update: 'full' | 'normal'
addons: Addon[]
/**
* The library need to install. This is optional
*/
libraries?: Library[]
/**
* Contains the files need to be downloaded from internet
*/
files: FileInfo[]
export function resolveHMCLVersion(version: HMCLVersion) {
}

/**
* Represnet a file need to be downloaded from internet
* This task will install HMCL files into the destination directory.
*
* It will not handle the HMCL version or auto-update function.
*/
export interface FileInfo {
/**
* The relative path of the file in minecraft
*/
path: string
/**
* The sha1 of the file
*/
hash: string
/**
* The file download url
*/
url?: string
}
export class InstallHMCLModpackTask extends TaskBase<HMCLServerManagedModpack> {
private zip: ZipFile | undefined
private entries: (Entry[]) | undefined = []
private openedStreams: Readable[] = []

export interface Library {
/**
* The library need to be installed.
*
* For example: "cn.skinme.skinme-loader"
*/
name: string
/**
* The name of the library
*/
filename: string
hint: 'local'
}

/**
* An addon will install multiple (thrid party) library to the game.
*/
export interface Addon {
/**
* "game" for minecraft. "forge" for minecraft forge
*/
id: string
/**
* The version of the addon. For forge, it has to match BMCLAPI's version
*/
version: string
}
constructor(path: string, destination: string) {
super()
this._from = path
this._to = destination
}

export interface PatchedVersion {
protected async ensureZip() {
if (this.zip && this.entries) {
return [this.zip, this.entries] as const
}
this.zip = await open(this._from!)
this.entries = await readAllEntries(this.zip)

}
return [this.zip, this.entries] as const
}

export interface PackJson extends Version {
id: string
jar: string
root: boolean
hidden: boolean
patches: PatchedVersion[]
}
protected async track(readStream: Readable) {
readStream.on('data', (b) => { this.update(b.length) })
this.openedStreams.push(readStream)
}

function isServerManaged(modpack: HMCLModpack): modpack is HMCLServerManagedModpack {
return 'fileApi' in modpack
}
protected async run(): Promise<HMCLServerManagedModpack> {
const [zip, entries] = await this.ensureZip()
const destination = this._to!
if (entries.find(e => e.fileName === 'server-manifest.json')) {
const promises = [] as Array<Promise<void>>
const fileEntries = entries.filter(e => e.fileName.startsWith('overrides'))
this._total = fileEntries.map(e => e.uncompressedSize).reduce((a, b) => a + b, 0)
for (const e of fileEntries) {
const fileName = join(destination, e.fileName.substring('overrides/'.length, e.fileName.length))
const readStream = await openEntryReadStream(zip, e)
this.track(readStream)
promises.push(pipeline(readStream, createWriteStream(fileName)))
}
await Promise.all(promises)
const metadata = await readEntry(zip, entries.find(e => e.fileName === 'server-manifest.json')!).then(b => JSON.parse(b.toString()) as HMCLServerManagedModpack)
return metadata
}
throw new Error('Malformed HMCL Modpack!')
}

export async function readHMCLModpackMetadata(zip: ZipFile) {
for await (const entry of walkEntriesGenerator(zip)) {
if (entry.fileName === 'modpack.json') {
return readEntry(zip, entry).then(b => JSON.parse(b.toString()) as HMCLModpack)
protected async performCancel(): Promise<void> {
for (const stream of this.openedStreams) {
stream.destroy(new CancelledError(undefined))
}
if (entry.fileName === 'server-manifest.json') {
return readEntry(zip, entry).then(b => JSON.parse(b.toString()) as HMCLServerManagedModpack)
}

protected async performPause(): Promise<void> {
for (const stream of this.openedStreams) {
stream.pause()
}
}
throw new Error()
}

export async function installHMCLModpack(zip: ZipFile, destination: string) {
const entries = await readAllEntries(zip)
if (entries.find(e => e.fileName === 'modpack.json')) {
const promises = [] as Array<Promise<void>>
for (const e of entries.filter(e => e.fileName.startsWith('minecraft'))) {
const fileName = join(destination, e.fileName)
if (e.fileName === 'minecraft/pack.json') {
const pack: PackJson = await readEntry(zip, e).then(b => JSON.parse(b.toString()))
} else {
promises.push(pipeline(await openEntryReadStream(zip, e), createWriteStream(fileName)))
}
protected performResume(): void {
for (const stream of this.openedStreams) {
stream.resume()
}
await Promise.all(promises)
}
}
2 changes: 1 addition & 1 deletion src/main/entities/java.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function parseTsingHuaAdpotOpenJDKHotspotArchive (pageText: string, baseU
.filter(isNonnull)
.map((a) => a[1]) ?? []

const target = targets.find((target) => target.indexOf('hotspot') !== -1 && target.endsWith('.zip') || target.endsWith('.tar.gz'))
const target = targets.find((target) => (target.indexOf('hotspot') !== -1 && target.endsWith('.zip')) || target.endsWith('.tar.gz'))
if (target) {
return {
fileName: target,
Expand Down
1 change: 0 additions & 1 deletion src/main/service/BaseService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { copy, copyFile, ensureDir, readJson, remove, unlink, writeJson } from 'fs-extra'
import { join } from 'path'
import 'reflect-metadata'
import LauncherApp from '../app/LauncherApp'
import { MappedFile } from '../util/persistance'
import { BufferJsonSerializer } from '../util/serialize'
Expand Down
Loading

0 comments on commit 03cf878

Please sign in to comment.