pimi-launcher-core/components/handler.js

740 lines
28 KiB
JavaScript
Raw Normal View History

2020-05-29 05:39:13 +03:00
const fs = require('fs')
const path = require('path')
const request = require('request')
const checksum = require('checksum')
const Zip = require('adm-zip')
const child = require('child_process')
let counter = 0
2018-10-30 01:13:58 +03:00
2019-05-23 01:28:48 +03:00
class Handler {
2020-05-29 05:39:13 +03:00
constructor (client) {
this.client = client
this.options = client.options
this.baseRequest = request.defaults({
pool: { maxSockets: this.options.overrides.maxSockets || 2 },
timeout: this.options.timeout || 10000
})
}
checkJava (java) {
return new Promise(resolve => {
child.exec(`"${java}" -version`, (error, stdout, stderr) => {
if (error) {
resolve({
run: false,
message: error
})
} else {
this.client.emit('debug', `[MCLC]: Using Java version ${stderr.match(/"(.*?)"/).pop()} ${stderr.includes('64-Bit') ? '64-bit' : '32-Bit'}`)
resolve({
run: true
})
}
})
})
}
2018-10-30 01:13:58 +03:00
2020-05-29 05:39:13 +03:00
downloadAsync (url, directory, name, retry, type) {
return new Promise(resolve => {
fs.mkdirSync(directory, { recursive: true })
2020-05-29 05:39:13 +03:00
const _request = this.baseRequest(url)
2018-10-30 01:13:58 +03:00
2020-05-29 05:39:13 +03:00
let receivedBytes = 0
let totalBytes = 0
2020-05-29 05:39:13 +03:00
_request.on('response', (data) => {
if (data.statusCode === 404) {
this.client.emit('debug', `[MCLC]: Failed to download ${url} due to: File not found...`)
2022-09-13 19:10:05 +03:00
return resolve(false)
2020-05-29 05:39:13 +03:00
}
totalBytes = parseInt(data.headers['content-length'])
})
_request.on('error', async (error) => {
this.client.emit('debug', `[MCLC]: Failed to download asset to ${path.join(directory, name)} due to\n${error}.` +
` Retrying... ${retry}`)
if (retry) await this.downloadAsync(url, directory, name, false, type)
resolve()
})
_request.on('data', (data) => {
receivedBytes += data.length
this.client.emit('download-status', {
name: name,
type: type,
current: receivedBytes,
total: totalBytes
})
})
const file = fs.createWriteStream(path.join(directory, name))
_request.pipe(file)
file.once('finish', () => {
this.client.emit('download', name)
resolve({
failed: false,
asset: null
})
})
file.on('error', async (e) => {
this.client.emit('debug', `[MCLC]: Failed to download asset to ${path.join(directory, name)} due to\n${e}.` +
` Retrying... ${retry}`)
if (fs.existsSync(path.join(directory, name))) fs.unlinkSync(path.join(directory, name))
2020-05-29 05:39:13 +03:00
if (retry) await this.downloadAsync(url, directory, name, false, type)
resolve()
})
})
}
checkSum (hash, file) {
return new Promise((resolve, reject) => {
checksum.file(file, (err, sum) => {
2020-06-19 19:26:58 +03:00
if (err) {
this.client.emit('debug', `[MCLC]: Failed to check file hash due to ${err}`)
resolve(false)
} else {
resolve(hash === sum)
}
2020-05-29 05:39:13 +03:00
})
})
}
getVersion () {
return new Promise(resolve => {
const versionJsonPath = this.options.overrides.versionJson || path.join(this.options.directory, `${this.options.version.number}.json`)
if (fs.existsSync(versionJsonPath)) {
this.version = JSON.parse(fs.readFileSync(versionJsonPath))
2020-06-19 19:26:58 +03:00
return resolve(this.version)
2020-05-29 05:39:13 +03:00
}
const manifest = `${this.options.overrides.url.meta}/mc/game/version_manifest.json`
const cache = this.options.cache ? `${this.options.cache}/json` : `${this.options.root}/cache/json`
2020-05-29 05:39:13 +03:00
request.get(manifest, (error, response, body) => {
if (error && error.code !== 'ENOTFOUND') return resolve(error)
if (!error) {
if (!fs.existsSync(cache)) {
fs.mkdirSync(cache, { recursive: true })
this.client.emit('debug', '[MCLC]: Cache directory created.')
}
fs.writeFile(path.join(`${cache}/version_manifest.json`), body, (err) => {
if (err) return resolve(err)
this.client.emit('debug', '[MCLC]: Cached version_manifest.json')
})
}
2020-05-29 05:39:13 +03:00
let parsed
if (error && (error.code === 'ENOTFOUND')) {
parsed = JSON.parse(fs.readFileSync(`${cache}/version_manifest.json`))
} else {
parsed = JSON.parse(body)
}
2020-05-29 05:39:13 +03:00
for (const desiredVersion in parsed.versions) {
if (parsed.versions[desiredVersion].id === this.options.version.number) {
request.get(parsed.versions[desiredVersion].url, (error, response, body) => {
if (error && error.code !== 'ENOTFOUND') return resolve(error)
if (!error) {
fs.writeFile(path.join(`${cache}/${this.options.version.number}.json`), body, (err) => {
if (err) return resolve(err)
this.client.emit('debug', `[MCLC]: Cached ${this.options.version.number}.json`)
})
}
2020-05-29 05:39:13 +03:00
this.client.emit('debug', '[MCLC]: Parsed version from version manifest')
if (error && (error.code === 'ENOTFOUND')) {
this.version = JSON.parse(fs.readFileSync(`${cache}/${this.options.version.number}.json`))
} else {
this.version = JSON.parse(body)
}
2020-06-19 19:26:58 +03:00
return resolve(this.version)
2020-05-29 05:39:13 +03:00
})
}
}
})
})
}
2018-10-30 01:13:58 +03:00
2020-05-29 05:39:13 +03:00
async getJar () {
await this.downloadAsync(this.version.downloads.client.url, this.options.directory, `${this.options.version.custom ? this.options.version.custom : this.options.version.number}.jar`, true, 'version-jar')
2018-10-30 01:13:58 +03:00
2020-05-29 05:39:13 +03:00
fs.writeFileSync(path.join(this.options.directory, `${this.options.version.number}.json`), JSON.stringify(this.version, null, 4))
2018-10-30 01:13:58 +03:00
2020-05-29 05:39:13 +03:00
return this.client.emit('debug', '[MCLC]: Downloaded version jar and wrote version json')
}
2018-10-30 01:13:58 +03:00
2020-05-29 05:39:13 +03:00
async getAssets () {
2020-07-23 00:19:48 +03:00
const assetDirectory = path.resolve(this.options.overrides.assetRoot || path.join(this.options.root, 'assets'))
2022-08-29 05:40:57 +03:00
const assetId = this.options.version.custom || this.options.version.number
if (!fs.existsSync(path.join(assetDirectory, 'indexes', `${assetId}.json`))) {
await this.downloadAsync(this.version.assetIndex.url, path.join(assetDirectory, 'indexes'),
2022-08-29 05:40:57 +03:00
`${assetId}.json`, true, 'asset-json')
2019-05-23 01:28:48 +03:00
}
2018-10-30 01:13:58 +03:00
2022-08-29 05:40:57 +03:00
const index = JSON.parse(fs.readFileSync(path.join(assetDirectory, 'indexes', `${assetId}.json`), { encoding: 'utf8' }))
2020-05-29 05:39:13 +03:00
this.client.emit('progress', {
type: 'assets',
task: 0,
total: Object.keys(index.objects).length
})
await Promise.all(Object.keys(index.objects).map(async asset => {
const hash = index.objects[asset].hash
const subhash = hash.substring(0, 2)
const subAsset = path.join(assetDirectory, 'objects', subhash)
if (!fs.existsSync(path.join(subAsset, hash)) || !await this.checkSum(hash, path.join(subAsset, hash))) {
await this.downloadAsync(`${this.options.overrides.url.resource}/${subhash}/${hash}`, subAsset, hash,
true, 'assets')
2020-07-08 06:36:31 +03:00
counter++
2020-05-29 05:39:13 +03:00
this.client.emit('progress', {
type: 'assets',
task: counter,
total: Object.keys(index.objects).length
})
}
}))
counter = 0
// Copy assets to legacy if it's an older Minecraft version.
2020-07-08 06:36:31 +03:00
if (this.isLegacy()) {
2020-10-03 11:16:03 +03:00
if (fs.existsSync(path.join(assetDirectory, 'legacy'))) {
this.client.emit('debug', '[MCLC]: The \'legacy\' directory is no longer used as Minecraft looks ' +
'for the resouces folder regardless of what is passed in the assetDirecotry launch option. I\'d ' +
`recommend removing the directory (${path.join(assetDirectory, 'legacy')})`)
}
const legacyDirectory = path.join(this.options.root, 'resources')
this.client.emit('debug', `[MCLC]: Copying assets over to ${legacyDirectory}`)
2020-05-29 05:39:13 +03:00
this.client.emit('progress', {
type: 'assets-copy',
task: 0,
total: Object.keys(index.objects).length
})
await Promise.all(Object.keys(index.objects).map(async asset => {
const hash = index.objects[asset].hash
const subhash = hash.substring(0, 2)
const subAsset = path.join(assetDirectory, 'objects', subhash)
const legacyAsset = asset.split('/')
legacyAsset.pop()
2020-10-03 11:16:03 +03:00
if (!fs.existsSync(path.join(legacyDirectory, legacyAsset.join('/')))) {
fs.mkdirSync(path.join(legacyDirectory, legacyAsset.join('/')), { recursive: true })
2020-05-29 05:39:13 +03:00
}
2020-10-03 11:16:03 +03:00
if (!fs.existsSync(path.join(legacyDirectory, asset))) {
fs.copyFileSync(path.join(subAsset, hash), path.join(legacyDirectory, asset))
2020-05-29 05:39:13 +03:00
}
2020-07-08 06:36:31 +03:00
counter++
2020-05-29 05:39:13 +03:00
this.client.emit('progress', {
type: 'assets-copy',
task: counter,
total: Object.keys(index.objects).length
})
}))
2019-05-23 01:28:48 +03:00
}
2020-05-29 05:39:13 +03:00
counter = 0
this.client.emit('debug', '[MCLC]: Downloaded assets')
}
2019-05-23 01:28:48 +03:00
2020-05-29 05:39:13 +03:00
parseRule (lib) {
if (lib.rules) {
if (lib.rules.length > 1) {
if (lib.rules[0].action === 'allow' &&
lib.rules[1].action === 'disallow' &&
lib.rules[1].os.name === 'osx') {
2020-05-29 05:39:13 +03:00
return this.getOS() === 'osx'
} else {
2020-05-29 05:39:13 +03:00
return true
}
2020-05-29 05:39:13 +03:00
} else {
2022-06-13 02:03:22 +03:00
if (lib.rules[0].action === 'allow' && lib.rules[0].os) return lib.rules[0].os.name !== this.getOS()
2020-05-29 05:39:13 +03:00
}
} else {
return false
}
2020-05-29 05:39:13 +03:00
}
async getNatives () {
2020-07-23 00:19:48 +03:00
const nativeDirectory = path.resolve(this.options.overrides.natives || path.join(this.options.root, 'natives', this.version.id))
2020-05-29 05:39:13 +03:00
2022-06-13 02:03:22 +03:00
if (parseInt(this.version.id.split('.')[1]) >= 19) return this.options.overrides.cwd || this.options.root
2020-05-29 05:39:13 +03:00
if (!fs.existsSync(nativeDirectory) || !fs.readdirSync(nativeDirectory).length) {
fs.mkdirSync(nativeDirectory, { recursive: true })
2020-05-29 05:39:13 +03:00
const natives = async () => {
const natives = []
await Promise.all(this.version.libraries.map(async (lib) => {
if (!lib.downloads || !lib.downloads.classifiers) return
2020-05-29 05:39:13 +03:00
if (this.parseRule(lib)) return
const native = this.getOS() === 'osx'
? lib.downloads.classifiers['natives-osx'] || lib.downloads.classifiers['natives-macos']
: lib.downloads.classifiers[`natives-${this.getOS()}`]
natives.push(native)
}))
return natives
}
const stat = await natives()
this.client.emit('progress', {
type: 'natives',
task: 0,
total: stat.length
})
await Promise.all(stat.map(async (native) => {
if (!native) return
const name = native.path.split('/').pop()
await this.downloadAsync(native.url, nativeDirectory, name, true, 'natives')
if (!await this.checkSum(native.sha1, path.join(nativeDirectory, name))) {
await this.downloadAsync(native.url, nativeDirectory, name, true, 'natives')
2019-05-23 01:28:48 +03:00
}
try {
2020-05-29 05:39:13 +03:00
new Zip(path.join(nativeDirectory, name)).extractAllTo(nativeDirectory, true)
} catch (e) {
// Only doing a console.warn since a stupid error happens. You can basically ignore this.
// if it says Invalid file name, just means two files were downloaded and both were deleted.
// All is well.
console.warn(e)
}
fs.unlinkSync(path.join(nativeDirectory, name))
2020-07-08 06:36:31 +03:00
counter++
2019-08-28 16:47:04 +03:00
this.client.emit('progress', {
2020-05-29 05:39:13 +03:00
type: 'natives',
task: counter,
total: stat.length
})
}))
this.client.emit('debug', '[MCLC]: Downloaded and extracted natives')
}
counter = 0
this.client.emit('debug', `[MCLC]: Set native path to ${nativeDirectory}`)
2019-02-07 16:14:16 +03:00
2020-05-29 05:39:13 +03:00
return nativeDirectory
}
2019-04-29 00:52:37 +03:00
fwAddArgs () {
const forgeWrapperAgrs = [
`-Dforgewrapper.librariesDir=${path.resolve(this.options.overrides.libraryRoot || path.join(this.options.root, 'libraries'))}`,
`-Dforgewrapper.installer=${this.options.forge}`,
`-Dforgewrapper.minecraft=${this.options.mcPath}`
]
this.options.customArgs
? this.options.customArgs = this.options.customArgs.concat(forgeWrapperAgrs)
: this.options.customArgs = forgeWrapperAgrs
}
2021-01-09 05:07:08 +03:00
isModernForge (json) {
return json.inheritsFrom && json.inheritsFrom.split('.')[1] >= 12 && !(json.inheritsFrom === '1.12.2' && (json.id.split('.')[json.id.split('.').length - 1]) === '2847')
}
async getForgedWrapped () {
let json = null
let installerJson = null
const versionPath = path.join(this.options.root, 'forge', `${this.version.id}`, 'version.json')
// Since we're building a proper "custom" JSON that will work nativly with MCLC, the version JSON will not
// be re-generated on the next run.
if (fs.existsSync(versionPath)) {
try {
json = JSON.parse(fs.readFileSync(versionPath))
2021-08-08 23:05:50 +03:00
if (!json.forgeWrapperVersion || !(json.forgeWrapperVersion === this.options.overrides.fw.version)) {
this.client.emit('debug', '[MCLC]: Old ForgeWrapper has generated this version JSON, re-generating')
} else {
2021-01-09 05:07:08 +03:00
// If forge is modern, add ForgeWrappers launch arguments and set forge to null so MCLC treats it as a custom json.
if (this.isModernForge(json)) {
this.fwAddArgs()
this.options.forge = null
}
return json
}
} catch (e) {
console.warn(e)
this.client.emit('debug', '[MCLC]: Failed to parse Forge version JSON, re-generating')
}
2019-05-23 01:28:48 +03:00
}
2019-02-07 16:14:16 +03:00
this.client.emit('debug', '[MCLC]: Generating a proper version json, this might take a bit')
2020-05-29 05:39:13 +03:00
const zipFile = new Zip(this.options.forge)
json = zipFile.readAsText('version.json')
if (zipFile.getEntry('install_profile.json')) installerJson = zipFile.readAsText('install_profile.json')
2020-05-29 05:39:13 +03:00
try {
json = JSON.parse(json)
if (installerJson) installerJson = JSON.parse(installerJson)
2020-05-29 05:39:13 +03:00
} catch (e) {
this.client.emit('debug', '[MCLC]: Failed to load json files for ForgeWrapper, using Vanilla instead')
2020-05-29 05:39:13 +03:00
return null
2019-05-23 01:28:48 +03:00
}
// Adding the installer libraries as mavenFiles so MCLC downloads them but doesn't add them to the class paths.
if (installerJson) {
json.mavenFiles
? json.mavenFiles = json.mavenFiles.concat(installerJson.libraries)
: json.mavenFiles = installerJson.libraries
}
2018-10-30 01:13:58 +03:00
// Holder for the specifc jar ending which depends on the specifc forge version.
let jarEnding = 'universal'
// We need to handle modern forge differently than legacy.
2021-01-09 05:07:08 +03:00
if (this.isModernForge(json)) {
// If forge is modern and above 1.12.2, we add ForgeWrapper to the libraries so MCLC includes it in the classpaths.
if (json.inheritsFrom !== '1.12.2') {
this.fwAddArgs()
2021-08-08 23:05:50 +03:00
const fwName = `ForgeWrapper-${this.options.overrides.fw.version}.jar`
const fwPathArr = ['io', 'github', 'zekerzhayard', 'ForgeWrapper', this.options.overrides.fw.version]
json.libraries.push({
name: fwPathArr.join(':'),
downloads: {
artifact: {
path: [...fwPathArr, fwName].join('/'),
2021-08-08 23:05:50 +03:00
url: `${this.options.overrides.fw.baseUrl}${this.options.overrides.fw.version}/${fwName}`,
sha1: this.options.overrides.fw.sh1,
size: this.options.overrides.fw.size
}
}
})
json.mainClass = 'io.github.zekerzhayard.forgewrapper.installer.Main'
jarEnding = 'launcher'
// Providing a download URL to the universal jar mavenFile so it can be downloaded properly.
for (const library of json.mavenFiles) {
const lib = library.name.split(':')
if (lib[0] === 'net.minecraftforge' && lib[1].includes('forge')) {
library.downloads.artifact.url = 'https://files.minecraftforge.net/maven/' + library.downloads.artifact.path
break
}
}
} else {
// Remove the forge dependent since we're going to overwrite the first entry anyways.
for (const library in json.mavenFiles) {
const lib = json.mavenFiles[library].name.split(':')
if (lib[0] === 'net.minecraftforge' && lib[1].includes('forge')) {
delete json.mavenFiles[library]
break
}
2020-05-29 05:39:13 +03:00
}
}
} else {
// Modifying legacy library format to play nice with MCLC's downloadToDirectory function.
await Promise.all(json.libraries.map(async library => {
const lib = library.name.split(':')
if (lib[0] === 'net.minecraftforge' && lib[1].includes('forge')) return
2020-05-29 05:39:13 +03:00
let url = this.options.overrides.url.mavenForge
const name = `${lib[1]}-${lib[2]}.jar`
2020-05-29 05:39:13 +03:00
if (!library.url) {
if (library.serverreq || library.clientreq) {
url = this.options.overrides.url.defaultRepoForge
2020-11-27 03:22:41 +03:00
} else {
return
2020-11-27 03:22:41 +03:00
}
}
library.url = url
const downloadLink = `${url}${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}/${name}`
// Checking if the file still exists on Forge's server, if not, replace it with the fallback.
// Not checking for sucess, only if it 404s.
this.baseRequest(downloadLink, (error, response, body) => {
if (error) {
this.client.emit('debug', `[MCLC]: Failed checking request for ${downloadLink}`)
} else {
if (response.statusCode === 404) library.url = this.options.overrides.url.fallbackMaven
}
})
}))
}
// If a downloads property exists, we modify the inital forge entry to include ${jarEnding} so ForgeWrapper can work properly.
// If it doesn't, we simply remove it since we're already providing the universal jar.
if (json.libraries[0].downloads) {
2021-08-22 02:06:21 +03:00
if (json.libraries[0].name.includes('minecraftforge')) {
json.libraries[0].name = json.libraries[0].name + `:${jarEnding}`
json.libraries[0].downloads.artifact.path = json.libraries[0].downloads.artifact.path.replace('.jar', `-${jarEnding}.jar`)
json.libraries[0].downloads.artifact.url = 'https://files.minecraftforge.net/maven/' + json.libraries[0].downloads.artifact.path
}
} else {
delete json.libraries[0]
}
2020-11-27 03:22:41 +03:00
// Removing duplicates and null types
json.libraries = this.cleanUp(json.libraries)
if (json.mavenFiles) json.mavenFiles = this.cleanUp(json.mavenFiles)
2020-11-27 03:22:41 +03:00
2021-08-08 23:13:01 +03:00
json.forgeWrapperVersion = this.options.overrides.fw.version
// Saving file for next run!
if (!fs.existsSync(path.join(this.options.root, 'forge', this.version.id))) {
fs.mkdirSync(path.join(this.options.root, 'forge', this.version.id), { recursive: true })
}
fs.writeFileSync(versionPath, JSON.stringify(json, null, 4))
2020-11-27 03:22:41 +03:00
// Make MCLC treat modern forge as a custom version json rather then legacy forge.
2021-01-09 05:07:08 +03:00
if (this.isModernForge(json)) this.options.forge = null
return json
2020-05-29 05:39:13 +03:00
}
runInstaller (path) {
return new Promise(resolve => {
const installer = child.exec(path)
installer.on('close', (code) => resolve(code))
2020-05-29 05:39:13 +03:00
})
}
async downloadToDirectory (directory, libraries, eventName) {
const libs = []
await Promise.all(libraries.map(async library => {
if (!library) return
if (this.parseRule(library)) return
2020-05-29 05:39:13 +03:00
const lib = library.name.split(':')
let jarPath
let name
if (library.downloads && library.downloads.artifact && library.downloads.artifact.path) {
name = library.downloads.artifact.path.split('/')[library.downloads.artifact.path.split('/').length - 1]
2020-07-23 00:19:48 +03:00
jarPath = path.join(directory, this.popString(library.downloads.artifact.path))
2020-05-29 05:39:13 +03:00
} else {
name = `${lib[1]}-${lib[2]}${lib[3] ? '-' + lib[3] : ''}.jar`
2020-07-23 00:19:48 +03:00
jarPath = path.join(directory, `${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}`)
2020-05-29 05:39:13 +03:00
}
if (!fs.existsSync(path.join(jarPath, name)) || !this.checkSum(library.downloads.artifact.sha1, path.join(jarPath, name))) {
2020-05-29 05:39:13 +03:00
// Simple lib support, forgot which addon needed this but here you go, Mr special.
if (library.url) {
const url = `${library.url}${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}/${name}`
await this.downloadAsync(url, jarPath, name, true, eventName)
} else if (library.downloads && library.downloads.artifact) {
await this.downloadAsync(library.downloads.artifact.url, jarPath, name, true, eventName)
2018-10-30 01:13:58 +03:00
}
2020-05-29 05:39:13 +03:00
}
2020-07-08 06:36:31 +03:00
counter++
2020-05-29 05:39:13 +03:00
this.client.emit('progress', {
type: eventName,
task: counter,
total: libraries.length
})
libs.push(`${jarPath}${path.sep}${name}`)
}))
counter = 0
return libs
}
async getClasses (classJson) {
let libs = []
2020-07-23 00:19:48 +03:00
const libraryDirectory = path.resolve(this.options.overrides.libraryRoot || path.join(this.options.root, 'libraries'))
2020-05-29 05:39:13 +03:00
if (classJson) {
if (classJson.mavenFiles) {
2020-07-23 00:19:48 +03:00
await this.downloadToDirectory(libraryDirectory, classJson.mavenFiles, 'classes-maven-custom')
2020-05-29 05:39:13 +03:00
}
2020-07-23 00:19:48 +03:00
libs = (await this.downloadToDirectory(libraryDirectory, classJson.libraries, 'classes-custom'))
2020-05-29 05:39:13 +03:00
}
const parsed = this.version.libraries.map(lib => {
if (lib.downloads && lib.downloads.artifact && !this.parseRule(lib)) return lib
2020-05-29 05:39:13 +03:00
})
2020-07-23 00:19:48 +03:00
libs = libs.concat((await this.downloadToDirectory(libraryDirectory, parsed, 'classes')))
2020-05-29 05:39:13 +03:00
counter = 0
// Temp Quilt support
if (classJson) libs.sort()
2023-01-30 04:16:27 +03:00
2020-05-29 05:39:13 +03:00
this.client.emit('debug', '[MCLC]: Collected class paths')
return libs
}
popString (path) {
const tempArray = path.split('/')
tempArray.pop()
return tempArray.join('/')
}
2020-07-08 06:36:31 +03:00
cleanUp (array) {
const newArray = []
for (const classPath in array) {
if (newArray.includes(array[classPath]) || array[classPath] === null) continue
2020-07-08 06:36:31 +03:00
newArray.push(array[classPath])
}
return newArray
2020-05-29 05:39:13 +03:00
}
async getLaunchOptions (modification) {
const type = modification || this.version
let args = type.minecraftArguments
? type.minecraftArguments.split(' ')
: type.arguments.game
2020-07-23 00:19:48 +03:00
const assetRoot = path.resolve(this.options.overrides.assetRoot || path.join(this.options.root, 'assets'))
2020-07-08 06:36:31 +03:00
const assetPath = this.isLegacy()
2020-10-03 11:16:03 +03:00
? path.join(this.options.root, 'resources')
2020-05-29 05:39:13 +03:00
: path.join(assetRoot)
2020-07-08 06:36:31 +03:00
const minArgs = this.options.overrides.minArgs || this.isLegacy() ? 5 : 11
2020-05-29 05:39:13 +03:00
if (args.length < minArgs) args = args.concat(this.version.minecraftArguments ? this.version.minecraftArguments.split(' ') : this.version.arguments.game)
if (this.options.customLaunchArgs) args = args.concat(this.options.customLaunchArgs)
2020-05-29 05:39:13 +03:00
this.options.authorization = await Promise.resolve(this.options.authorization)
this.options.authorization.meta = this.options.authorization.meta ? this.options.authorization.meta : { type: 'mojang' }
2020-05-29 05:39:13 +03:00
const fields = {
'${auth_access_token}': this.options.authorization.access_token,
'${auth_session}': this.options.authorization.access_token,
'${auth_player_name}': this.options.authorization.name,
'${auth_uuid}': this.options.authorization.uuid,
'${auth_xuid}': this.options.authorization.meta.xuid || this.options.authorization.access_token,
2020-05-29 05:39:13 +03:00
'${user_properties}': this.options.authorization.user_properties,
'${user_type}': this.options.authorization.meta.type,
2020-05-29 05:39:13 +03:00
'${version_name}': this.options.version.number,
'${assets_index_name}': this.options.version.custom || this.options.version.number,
'${game_directory}': this.options.overrides.gameDirectory || this.options.root,
2020-05-29 05:39:13 +03:00
'${assets_root}': assetPath,
'${game_assets}': assetPath,
'${version_type}': this.options.version.type,
'${clientid}': this.options.authorization.meta.clientId || (this.options.authorization.client_token || this.options.authorization.access_token),
2022-09-13 19:10:05 +03:00
'${resolution_width}': this.options.window ? this.options.window.width : 856,
'${resolution_height}': this.options.window ? this.options.window.height : 482
2020-05-29 05:39:13 +03:00
}
if (this.options.authorization.meta.demo && (this.options.features ? !this.options.features.includes('is_demo_user') : true)) {
args.push('--demo')
}
const replaceArg = (obj, index) => {
if (Array.isArray(obj.value)) {
for (const arg of obj.value) {
args.push(arg)
}
} else {
args.push(obj.value)
}
delete args[index]
}
2020-05-29 05:39:13 +03:00
for (let index = 0; index < args.length; index++) {
if (typeof args[index] === 'object') {
if (args[index].rules) {
if (!this.options.features) continue
const featureFlags = []
for (const rule of args[index].rules) {
featureFlags.push(...Object.keys(rule.features))
}
let hasAllRules = true
for (const feature of this.options.features) {
if (!featureFlags.includes(feature)) {
hasAllRules = false
}
}
if (hasAllRules) replaceArg(args[index], index)
} else {
replaceArg(args[index], index)
}
} else {
if (Object.keys(fields).includes(args[index])) {
args[index] = fields[args[index]]
}
2020-05-29 05:39:13 +03:00
}
}
args = args.filter((value) => {
return typeof value === 'string' || typeof value === 'number'
})
2020-05-29 05:39:13 +03:00
if (this.options.window) {
2022-09-13 19:10:05 +03:00
// eslint-disable-next-line no-unused-expressions
2020-05-29 05:39:13 +03:00
this.options.window.fullscreen
? args.push('--fullscreen')
: () => {
if (this.options.features ? !this.options.features.includes('has_custom_resolution') : true) {
args.push('--width', this.options.window.width, '--height', this.options.window.height)
}
}
2020-05-29 05:39:13 +03:00
}
if (this.options.server) args.push('--server', this.options.server.host, '--port', this.options.server.port || '25565')
if (this.options.proxy) {
args.push(
'--proxyHost',
this.options.proxy.host,
'--proxyPort',
this.options.proxy.port || '8080',
'--proxyUser',
this.options.proxy.username,
'--proxyPass',
this.options.proxy.password
)
}
2020-05-29 05:39:13 +03:00
this.client.emit('debug', '[MCLC]: Set launch options')
return args
}
async getJVM () {
const opts = {
windows: '-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump',
osx: '-XstartOnFirstThread',
linux: '-Xss1M'
}
return opts[this.getOS()]
}
2020-07-08 06:36:31 +03:00
isLegacy () {
return this.version.assets === 'legacy' || this.version.assets === 'pre-1.6'
}
2020-05-29 05:39:13 +03:00
getOS () {
if (this.options.os) {
return this.options.os
} else {
switch (process.platform) {
case 'win32': return 'windows'
case 'darwin': return 'osx'
default: return 'linux'
}
}
2020-05-29 05:39:13 +03:00
}
2020-08-24 06:57:28 +03:00
// To prevent launchers from breaking when they update. Will be reworked with rewrite.
getMemory () {
if (!this.options.memory) {
this.client.emit('debug', '[MCLC]: Memory not set! Setting 1GB as MAX!')
this.options.memory = {
min: 512,
max: 1023
}
}
if (!isNaN(this.options.memory.max) && !isNaN(this.options.memory.min)) {
if (this.options.memory.max < this.options.memory.min) {
this.client.emit('debug', '[MCLC]: MIN memory is higher then MAX! Resetting!')
this.options.memory.max = 1023
this.options.memory.min = 512
}
return [`${this.options.memory.max}M`, `${this.options.memory.min}M`]
} else { return [`${this.options.memory.max}`, `${this.options.memory.min}`] }
}
2020-05-29 05:39:13 +03:00
async extractPackage (options = this.options) {
2020-06-19 19:26:58 +03:00
if (options.clientPackage.startsWith('http')) {
await this.downloadAsync(options.clientPackage, options.root, 'clientPackage.zip', true, 'client-package')
options.clientPackage = path.join(options.root, 'clientPackage.zip')
}
new Zip(options.clientPackage).extractAllTo(options.root, true)
if (options.removePackage) fs.unlinkSync(options.clientPackage)
2020-05-29 05:39:13 +03:00
2020-06-19 19:26:58 +03:00
return this.client.emit('package-extract', true)
2020-05-29 05:39:13 +03:00
}
2019-05-23 01:28:48 +03:00
}
2020-05-29 05:39:13 +03:00
module.exports = Handler