mirror of
https://github.com/dscalzi/HeliosLauncher.git
synced 2024-12-22 11:42:14 -08:00
Massive progress on implementing the new forge modlist format. I have been able to rewrite much of the launch process code to use this new method, and was able to successfully log into the game. Several bugs need to be worked on, however everything is in a good state so far.
This commit is contained in:
parent
abbfb52c1b
commit
dd7ae6bf56
@ -705,6 +705,13 @@ function validateLogConfig(versionData, basePath){
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the distribution.
|
||||
*
|
||||
* @param {String} serverpackid - The id of the server to validate.
|
||||
* @param {String} basePath - the absolute file path which will be prepended to the given relative paths.
|
||||
* @returns {Promise.<Object>} - A promise which resolves to the server distribution object.
|
||||
*/
|
||||
function validateDistribution(serverpackid, basePath){
|
||||
return new Promise(function(fulfill, reject){
|
||||
_chainValidateDistributionIndex(basePath).then((value) => {
|
||||
@ -729,7 +736,7 @@ function validateDistribution(serverpackid, basePath){
|
||||
}
|
||||
}
|
||||
instance.totaldlsize += instance.forge.dlsize*1
|
||||
fulfill()
|
||||
fulfill(serv)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -1,3 +1,8 @@
|
||||
/**
|
||||
* File is officially deprecated in favor of processbuilder.js,
|
||||
* will be removed once the new module is 100% complete and this
|
||||
* is no longer needed for reference.
|
||||
*/
|
||||
const mojang = require('mojang')
|
||||
const uuidV4 = require('uuid/v4')
|
||||
const path = require('path')
|
||||
|
@ -1,104 +0,0 @@
|
||||
/**
|
||||
* Work in progress
|
||||
*/
|
||||
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
class ProcessBuilder {
|
||||
|
||||
constructor(gameDirectory, distroServer, versionData, forgeData, authUser){
|
||||
this.dir = gameDirectory
|
||||
this.server = server
|
||||
this.versionData = versionData
|
||||
this.forgeData = forgeData
|
||||
this.authUser = authUser
|
||||
this.fmlDir = path.join(this.dir, 'versions', this.server.id)
|
||||
}
|
||||
|
||||
static shouldInclude(mdle){
|
||||
//If the module should be included by default
|
||||
return mdle.required == null || mdle.required.value == null || mdle.required.value === true || (mdle.required.value === false && mdle.required.def === true)
|
||||
}
|
||||
|
||||
resolveDefaultMods(options = {type: 'forgemod'}){
|
||||
//Returns array of default forge mods to load.
|
||||
const mods = []
|
||||
const mdles = this.server.modules
|
||||
|
||||
for(let i=0; i<mdles.length; ++i){
|
||||
if(mdles[i].type != null && mdles[i].type === options.type){
|
||||
if(ProcessBuiler._shouldInclude(mdles[i])){
|
||||
mods.push(mdles[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
constructFMLModList(mods, save = false){
|
||||
//untested - save modlist file
|
||||
const modList = {}
|
||||
modList.repositoryRoot = path.join(this.dir, 'modstore')
|
||||
const ids = []
|
||||
for(let i=0; i<mods.length; ++i){
|
||||
ids.push(mods.id)
|
||||
}
|
||||
modList.modRef = ids
|
||||
|
||||
if(save){
|
||||
fs.writeFileSync(this.fmlDir, modList, 'UTF-8')
|
||||
}
|
||||
}
|
||||
|
||||
constructJVMArguments(){
|
||||
//pending changes
|
||||
const args = [];
|
||||
const mcArgs = this.forgeData.minecraftArguments.split(' ')
|
||||
const argDiscovery = /\${*(.*)}/
|
||||
for(let i=0; i<mcArgs.length; ++i){
|
||||
if(argDiscovery.test(mcArgs[i])){
|
||||
const identifier = mcArgs[i]
|
||||
let val = null;
|
||||
switch(identifier){
|
||||
case 'auth_player_name':
|
||||
val = authUser.selectedProfile.name
|
||||
break
|
||||
case 'version_name':
|
||||
//val = versionData.id
|
||||
val = this.server.id
|
||||
break
|
||||
case 'game_directory':
|
||||
val = this.dir
|
||||
break
|
||||
case 'assets_root':
|
||||
val = path.join(this.dir, 'assets')
|
||||
break
|
||||
case 'assets_index_name':
|
||||
val = this.versionData.assets
|
||||
break
|
||||
case 'auth_uuid':
|
||||
val = this.authUser.selectedProfile.id
|
||||
break
|
||||
case 'auth_access_token':
|
||||
val = authUser.accessToken
|
||||
break
|
||||
case 'user_type':
|
||||
val = 'MOJANG'
|
||||
break
|
||||
case 'version_type':
|
||||
val = versionData['type']
|
||||
break
|
||||
}
|
||||
if(val != null){
|
||||
mcArgs[i] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return args.concat(mcArgs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ProcessBuilder
|
327
app/assets/js/processbuilder.js
Normal file
327
app/assets/js/processbuilder.js
Normal file
@ -0,0 +1,327 @@
|
||||
/**
|
||||
* The initial iteration of this file will not support optional submodules.
|
||||
* Support will be added down the line, only top-level modules will recieve optional support.
|
||||
*
|
||||
*
|
||||
* TODO why are logs not working??????
|
||||
*/
|
||||
const AdmZip = require('adm-zip')
|
||||
const ag = require('./assetguard.js')
|
||||
const child_process = require('child_process')
|
||||
const fs = require('fs')
|
||||
const mkpath = require('mkdirp')
|
||||
const path = require('path')
|
||||
|
||||
class ProcessBuilder {
|
||||
|
||||
constructor(gameDirectory, distroServer, versionData, forgeData, authUser){
|
||||
this.dir = gameDirectory
|
||||
this.server = distroServer
|
||||
this.versionData = versionData
|
||||
this.forgeData = forgeData
|
||||
this.authUser = authUser
|
||||
this.fmlDir = path.join(this.dir, 'versions', this.server.id + '.json')
|
||||
this.libPath = path.join(this.dir, 'libraries')
|
||||
}
|
||||
|
||||
static shouldInclude(mdle){
|
||||
//If the module should be included by default
|
||||
return mdle.required == null || mdle.required.value == null || mdle.required.value === true || (mdle.required.value === false && (mdle.required.def == null || mdle.required.def === true))
|
||||
}
|
||||
|
||||
/**
|
||||
* Convienence method to run the functions typically used to build a process.
|
||||
*/
|
||||
build(){
|
||||
const mods = this.resolveDefaultMods()
|
||||
this.constructFMLModList(mods, true)
|
||||
const args = this.constructJVMArguments(mods)
|
||||
|
||||
//console.log(args)
|
||||
|
||||
const child = child_process.spawn('C:\\Program Files\\Java\\jdk1.8.0_152\\bin\\javaw.exe', args)
|
||||
|
||||
child.stdout.on('data', (data) => {
|
||||
console.log('Minecraft:', data.toString('utf8'))
|
||||
})
|
||||
child.stderr.on('data', (data) => {
|
||||
console.log('Minecraft:', data.toString('utf8'))
|
||||
})
|
||||
child.on('close', (code, signal) => {
|
||||
console.log('Exited with code', code)
|
||||
})
|
||||
|
||||
return child
|
||||
}
|
||||
|
||||
resolveDefaultMods(options = {type: 'forgemod'}){
|
||||
//Returns array of default forge mods to load.
|
||||
const mods = []
|
||||
const mdles = this.server.modules
|
||||
|
||||
for(let i=0; i<mdles.length; ++i){
|
||||
if(mdles[i].type != null && mdles[i].type === options.type){
|
||||
if(ProcessBuilder.shouldInclude(mdles[i])){
|
||||
mods.push(mdles[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
constructFMLModList(mods, save = false){
|
||||
const modList = {}
|
||||
modList.repositoryRoot = path.join(this.dir, 'modstore')
|
||||
const ids = []
|
||||
for(let i=0; i<mods.length; ++i){
|
||||
ids.push(mods[i].id)
|
||||
}
|
||||
modList.modRef = ids
|
||||
|
||||
if(save){
|
||||
const json = JSON.stringify(modList, null, 4)
|
||||
fs.writeFileSync(this.fmlDir, json, 'UTF-8')
|
||||
}
|
||||
|
||||
return modList
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the argument array that will be passed to the JVM process.
|
||||
*
|
||||
* @param {Array.<Object>} mods - An array of enabled mods which will be launched with this process.
|
||||
* @returns {Array.<String>} - An array containing the full JVM arguments for this process.
|
||||
*/
|
||||
constructJVMArguments(mods){
|
||||
|
||||
let args = ['-Xmx4G',
|
||||
'-XX:+UseConcMarkSweepGC',
|
||||
'-XX:+CMSIncrementalMode',
|
||||
'-XX:-UseAdaptiveSizePolicy',
|
||||
'-Xmn128M',
|
||||
'-Djava.library.path=' + path.join(this.dir, 'natives'),
|
||||
'-cp',
|
||||
this.classpathArg(mods).join(';'),
|
||||
this.forgeData.mainClass]
|
||||
|
||||
args = args.concat(this._resolveForgeArgs())
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the arguments required by forge.
|
||||
*
|
||||
* @returns {Array.<String>} - An array containing the arguments required by forge.
|
||||
*/
|
||||
_resolveForgeArgs(){
|
||||
const mcArgs = this.forgeData.minecraftArguments.split(' ')
|
||||
const argDiscovery = /\${*(.*)}/
|
||||
|
||||
// Replace the declared variables with their proper values.
|
||||
for(let i=0; i<mcArgs.length; ++i){
|
||||
if(argDiscovery.test(mcArgs[i])){
|
||||
const identifier = mcArgs[i].match(argDiscovery)[1]
|
||||
let val = null;
|
||||
switch(identifier){
|
||||
case 'auth_player_name':
|
||||
val = this.authUser.selectedProfile.name
|
||||
break
|
||||
case 'version_name':
|
||||
//val = versionData.id
|
||||
val = this.server.id
|
||||
break
|
||||
case 'game_directory':
|
||||
val = this.dir
|
||||
break
|
||||
case 'assets_root':
|
||||
val = path.join(this.dir, 'assets')
|
||||
break
|
||||
case 'assets_index_name':
|
||||
val = this.versionData.assets
|
||||
break
|
||||
case 'auth_uuid':
|
||||
val = this.authUser.selectedProfile.id
|
||||
break
|
||||
case 'auth_access_token':
|
||||
val = this.authUser.accessToken
|
||||
break
|
||||
case 'user_type':
|
||||
val = 'MOJANG'
|
||||
break
|
||||
case 'version_type':
|
||||
val = this.versionData.type
|
||||
break
|
||||
}
|
||||
if(val != null){
|
||||
mcArgs[i] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
mcArgs.push('--modListFile')
|
||||
mcArgs.push('absolute:' + this.fmlDir)
|
||||
return mcArgs
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the full classpath argument list for this process. This method will resolve all Mojang-declared
|
||||
* libraries as well as the libraries declared by the server. Since mods are permitted to declare libraries,
|
||||
* this method requires all enabled mods as an input
|
||||
*
|
||||
* @param {Array.<Object>} mods - An array of enabled mods which will be launched with this process.
|
||||
* @returns {Array.<String>} - An array containing the paths of each library required by this process.
|
||||
*/
|
||||
classpathArg(mods){
|
||||
let cpArgs = []
|
||||
|
||||
// Add the version.jar to the classpath.
|
||||
const version = this.versionData.id
|
||||
cpArgs.push(path.join(this.dir, 'versions', version, version + '.jar'))
|
||||
|
||||
// Resolve the Mojang declared libraries.
|
||||
const mojangLibs = this._resolveMojangLibraries()
|
||||
cpArgs = cpArgs.concat(mojangLibs)
|
||||
|
||||
// Resolve the server declared libraries.
|
||||
const servLibs = this._resolveServerLibraries(mods)
|
||||
cpArgs = cpArgs.concat(servLibs)
|
||||
|
||||
return cpArgs
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the libraries defined by Mojang's version data. This method will also extract
|
||||
* native libraries and point to the correct location for its classpath.
|
||||
*
|
||||
* TODO - clean up function
|
||||
*
|
||||
* @returns {Array.<String>} - An array containing the paths of each library mojang declares.
|
||||
*/
|
||||
_resolveMojangLibraries(){
|
||||
const libs = []
|
||||
|
||||
const libArr = this.versionData.libraries
|
||||
const nativePath = path.join(this.dir, 'natives')
|
||||
for(let i=0; i<libArr.length; i++){
|
||||
const lib = libArr[i]
|
||||
if(ag.Library.validateRules(lib.rules)){
|
||||
if(lib.natives == null){
|
||||
const dlInfo = lib.downloads
|
||||
const artifact = dlInfo.artifact
|
||||
const to = path.join(this.libPath, artifact.path)
|
||||
libs.push(to)
|
||||
} else {
|
||||
// Extract the native library.
|
||||
const natives = lib.natives
|
||||
const extractInst = lib.extract
|
||||
const exclusionArr = extractInst.exclude
|
||||
const opSys = ag.Library.mojangFriendlyOS()
|
||||
const indexId = natives[opSys]
|
||||
const dlInfo = lib.downloads
|
||||
const classifiers = dlInfo.classifiers
|
||||
const artifact = classifiers[indexId]
|
||||
|
||||
// Location of native zip.
|
||||
const to = path.join(this.libPath, artifact.path)
|
||||
|
||||
let zip = new AdmZip(to)
|
||||
let zipEntries = zip.getEntries()
|
||||
|
||||
// Unzip the native zip.
|
||||
for(let i=0; i<zipEntries.length; i++){
|
||||
const fileName = zipEntries[i].entryName
|
||||
|
||||
let shouldExclude = false
|
||||
|
||||
// Exclude noted files.
|
||||
exclusionArr.forEach(function(exclusion){
|
||||
if(exclusion.indexOf(fileName) > -1){
|
||||
shouldExclude = true
|
||||
}
|
||||
})
|
||||
|
||||
// Extract the file.
|
||||
if(!shouldExclude){
|
||||
mkpath.sync(path.join(nativePath, fileName, '..'))
|
||||
fs.writeFile(path.join(nativePath, fileName), zipEntries[i].getData())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
libs.push(to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return libs
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the libraries declared by this server in order to add them to the classpath.
|
||||
* This method will also check each enabled mod for libraries, as mods are permitted to
|
||||
* declare libraries.
|
||||
*
|
||||
* @param {Array.<Object>} mods - An array of enabled mods which will be launched with this process.
|
||||
* @returns {Array.<String>} - An array containing the paths of each library this server requires.
|
||||
*/
|
||||
_resolveServerLibraries(mods){
|
||||
const mdles = this.server.modules
|
||||
let libs = []
|
||||
|
||||
// Locate Forge Libraries
|
||||
for(let i=0; i<mdles.length; i++){
|
||||
if(mdles[i].type != null && mdles[i].type === 'forge-hosted'){
|
||||
let forge = mdles[i]
|
||||
libs.push(path.join(this.libPath, forge.artifact.path == null ? ag._resolvePath(forge.id, forge.artifact.extension) : forge.artifact.path))
|
||||
const res = this._resolveModuleLibraries(forge)
|
||||
if(res.length > 0){
|
||||
libs = libs.concat(res)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//Check for any libraries in our mod list.
|
||||
for(let i=0; i<mods.length; i++){
|
||||
if(mods.sub_modules != null){
|
||||
const res = this._resolveModuleLibraries(mods[i])
|
||||
if(res.length > 0){
|
||||
libs = libs.concat(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return libs
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively resolve the path of each library required by this module.
|
||||
*
|
||||
* @param {Object} mdle - A module object from the server distro index.
|
||||
* @returns {Array.<String>} - An array containing the paths of each library this module requires.
|
||||
*/
|
||||
_resolveModuleLibraries(mdle){
|
||||
if(mdle.sub_modules == null){
|
||||
return []
|
||||
}
|
||||
let libs = []
|
||||
for(let i=0; i<mdle.sub_modules.length; i++){
|
||||
const sm = mdle.sub_modules[i]
|
||||
if(sm.type != null && sm.type == 'library'){
|
||||
libs.push(path.join(this.libPath, sm.artifact.path == null ? ag._resolvePath(sm.id, sm.artifact.extension) : sm.artifact.path))
|
||||
}
|
||||
// If this module has submodules, we need to resolve the libraries for those.
|
||||
// To avoid unnecessary recursive calls, base case is checked here.
|
||||
if(mdle.sub_modules != null){
|
||||
const res = this._resolveModuleLibraries(sm)
|
||||
if(res.length > 0){
|
||||
libs = libs.concat(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
return libs
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProcessBuilder
|
@ -4,6 +4,9 @@ const shell = require('electron').shell
|
||||
const path = require('path')
|
||||
const os = require('os');
|
||||
const ag = require(path.join(__dirname, 'assets', 'js', 'assetguard.js'))
|
||||
const ProcessBuilder = require(path.join(__dirname, 'assets', 'js', 'processbuilder.js'))
|
||||
const mojang = require('mojang')
|
||||
const uuidV4 = require('uuid/v4')
|
||||
|
||||
$(document).on('ready', function(){
|
||||
console.log('okay');
|
||||
@ -48,7 +51,7 @@ $(document).on('click', 'a[href^="http"]', function(event) {
|
||||
})
|
||||
|
||||
testdownloads = async function(){
|
||||
const lp = require(path.join(__dirname, 'assets', 'js', 'launchprocess.js'))
|
||||
//const lp = require(path.join(__dirname, 'assets', 'js', 'launchprocess.js'))
|
||||
const basePath = path.join(__dirname, '..', 'target', 'test', 'mcfiles')
|
||||
let versionData = await ag.loadVersionData('1.11.2', basePath)
|
||||
await ag.validateAssets(versionData, basePath)
|
||||
@ -57,12 +60,18 @@ testdownloads = async function(){
|
||||
console.log('libs done')
|
||||
await ag.validateMiscellaneous(versionData, basePath)
|
||||
console.log('files done')
|
||||
await ag.validateDistribution('WesterosCraft-1.11.2', basePath)
|
||||
const serv = await ag.validateDistribution('WesterosCraft-1.11.2', basePath)
|
||||
console.log('forge stuff done')
|
||||
ag.instance.on('dlcomplete', async function(){
|
||||
let forgeData = await ag.loadForgeData('WesterosCraft-1.11.2', basePath)
|
||||
lp.launchMinecraft(versionData, forgeData, basePath)
|
||||
const forgeData = await ag.loadForgeData('WesterosCraft-1.11.2', basePath)
|
||||
const authUser = await mojang.auth('EMAIL', 'PASS', uuidV4(), {
|
||||
name: 'Minecraft',
|
||||
version: 1
|
||||
})
|
||||
//lp.launchMinecraft(versionData, forgeData, basePath)
|
||||
//lp.launchMinecraft(versionData, basePath)
|
||||
let pb = new ProcessBuilder(basePath, serv, versionData, forgeData, authUser)
|
||||
const proc = pb.build()
|
||||
})
|
||||
ag.processDlQueues()
|
||||
}
|
||||
|
@ -313,6 +313,44 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "org.blockartistry:dynsurround:1.11.2-3.4.6.2",
|
||||
"name": "DynamicSurroundings (1.11.2-3.4.6.2)",
|
||||
"type": "forgemod",
|
||||
"required": {
|
||||
"value": false
|
||||
},
|
||||
"artifact": {
|
||||
"size": 21853035,
|
||||
"MD5": "82a6aac5420151960b8dd709deee5423",
|
||||
"extension": ".jar",
|
||||
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.11.2/mods/DynamicSurroundings.jar"
|
||||
},
|
||||
"sub_modules": [
|
||||
{
|
||||
"id": "dsurround.cfg",
|
||||
"name": "DynamicSurroundings General Configuration File",
|
||||
"type": "file",
|
||||
"artifact": {
|
||||
"size": 19736,
|
||||
"MD5": "4c64fc6cbbb83b18012ed4820b0b496e",
|
||||
"path": "/config/dsurround/dsurround.cfg",
|
||||
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.11.2/config/dsurround/dsurround.cfg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "westeros.json",
|
||||
"name": "DynamicSurroundings WesterosCraft Configuration File",
|
||||
"type": "file",
|
||||
"artifact": {
|
||||
"size": 608,
|
||||
"MD5": "44eab112ff24d0bd29974c270de868ba",
|
||||
"path": "/config/dsurround/westeros.json",
|
||||
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.11.2/config/dsurround/westeros.json"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "com.westeroscraft:westerosblocks:3.0.0-beta-6-133",
|
||||
"name": "WesterosBlocks (3.0.0-beta-6-133)",
|
||||
|
Loading…
Reference in New Issue
Block a user