Updated Distribution Index spec and impl.
Added distromanager.js to represent distro elements. Moved all distro refresh code to distromanager.js. Overhauled assetexec.js. Overhauled handling of assetexec.js output in landing.js. Overhauled events emitted by assetguard.js. Improved doenload processing in assetguard. Updated discord-rpc to v3.0.0. Replaced westeroscraft.json with distribution.json. Use npm in travis for windows + linux. Remove file extension from imports. Added liteloader + macromod + shaders to distribution.json.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,38 +1,27 @@
|
||||
const {AssetGuard} = require('./assetguard.js')
|
||||
const { AssetGuard } = require('./assetguard')
|
||||
|
||||
const tracker = new AssetGuard(process.argv[2], process.argv[3], process.argv[4], process.argv[5])
|
||||
const tracker = new AssetGuard(process.argv[2], process.argv[3])
|
||||
console.log('AssetExec Started')
|
||||
|
||||
// Temporary for debug purposes.
|
||||
process.on('unhandledRejection', r => console.log(r))
|
||||
|
||||
tracker.on('assetVal', (data) => {
|
||||
process.send({task: 0, total: data.total, value: data.acc, content: 'validateAssets'})
|
||||
tracker.on('validate', (data) => {
|
||||
process.send({context: 'validate', data})
|
||||
})
|
||||
|
||||
tracker.on('totaldlprogress', (data) => {
|
||||
process.send({task: 0, total: data.total, value: data.acc, percent: parseInt((data.acc/data.total)*100), content: 'dl'})
|
||||
tracker.on('progress', (data, acc, total) => {
|
||||
process.send({context: 'progress', data, value: acc, total, percent: parseInt((acc/total)*100)})
|
||||
})
|
||||
|
||||
tracker.on('extracting', () => {
|
||||
process.send({task: 0.7, content: 'dl'})
|
||||
tracker.on('complete', (data, ...args) => {
|
||||
process.send({context: 'complete', data, args})
|
||||
})
|
||||
|
||||
tracker.on('dlcomplete', () => {
|
||||
process.send({task: 1, content: 'dl'})
|
||||
})
|
||||
|
||||
tracker.on('jExtracted', (jPath) => {
|
||||
process.send({task: 2, content: 'dl', jPath})
|
||||
})
|
||||
|
||||
tracker.on('dlerror', (err) => {
|
||||
process.send({task: 0.9, content: 'dl', err})
|
||||
tracker.on('error', (data, error) => {
|
||||
process.send({context: 'error', data, error})
|
||||
})
|
||||
|
||||
process.on('message', (msg) => {
|
||||
if(msg.task === 0){
|
||||
const func = msg.content
|
||||
if(msg.task === 'execute'){
|
||||
const func = msg.function
|
||||
let nS = tracker[func]
|
||||
let iS = AssetGuard[func]
|
||||
if(typeof nS === 'function' || typeof iS === 'function'){
|
||||
@@ -40,12 +29,12 @@ process.on('message', (msg) => {
|
||||
const res = f.apply(f === nS ? tracker : null, msg.argsArr)
|
||||
if(res instanceof Promise){
|
||||
res.then((v) => {
|
||||
process.send({result: v, content: msg.content})
|
||||
process.send({result: v, context: func})
|
||||
}).catch((err) => {
|
||||
process.send({result: err, content: msg.content})
|
||||
process.send({result: err, context: func})
|
||||
})
|
||||
} else {
|
||||
process.send({result: res, content: msg.content})
|
||||
process.send({result: res, context: func})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,6 @@
|
||||
* assigned as the value of the identifier in the AssetGuard object. These download
|
||||
* trackers will remain idle until an async process is started to process them.
|
||||
*
|
||||
* Once the async process is started, any enqueued assets will be downloaded. The AssetGuard
|
||||
* object will emit events throughout the download whose name correspond to the identifier
|
||||
* being processed. For example, if the 'assets' identifier was being processed, whenever
|
||||
* the download stream recieves data, the event 'assetsdlprogress' will be emitted off of
|
||||
* the AssetGuard instance. This can be listened to by external modules allowing for
|
||||
* categorical tracking of the downloading process.
|
||||
*
|
||||
* @module assetguard
|
||||
*/
|
||||
// Requirements
|
||||
@@ -36,6 +29,9 @@ const request = require('request')
|
||||
const tar = require('tar-fs')
|
||||
const zlib = require('zlib')
|
||||
|
||||
const ConfigManager = require('./configmanager')
|
||||
const DistroManager = require('./distromanager')
|
||||
|
||||
// Constants
|
||||
const PLATFORM_MAP = {
|
||||
win32: '-windows-x64.tar.gz',
|
||||
@@ -161,9 +157,6 @@ class DLTracker {
|
||||
|
||||
}
|
||||
|
||||
let distributionData = null
|
||||
let launchWithLocal = false
|
||||
|
||||
/**
|
||||
* Central object class used for control flow. This object stores data about
|
||||
* categories of downloads. Each category is assigned an identifier with a
|
||||
@@ -180,12 +173,10 @@ class AssetGuard extends EventEmitter {
|
||||
* values. Each identifier is resolved to an empty DLTracker.
|
||||
*
|
||||
* @param {string} commonPath The common path for shared game files.
|
||||
* @param {string} launcherPath The root launcher directory.
|
||||
* @param {string} javaexec The path to a java executable which will be used
|
||||
* to finalize installation.
|
||||
* @param {string} instancePath The path to the instances directory.
|
||||
*/
|
||||
constructor(commonPath, launcherPath, javaexec, instancePath){
|
||||
constructor(commonPath, javaexec){
|
||||
super()
|
||||
this.totaldlsize = 0
|
||||
this.progress = 0
|
||||
@@ -196,73 +187,12 @@ class AssetGuard extends EventEmitter {
|
||||
this.java = new DLTracker([], 0)
|
||||
this.extractQueue = []
|
||||
this.commonPath = commonPath
|
||||
this.launcherPath = launcherPath
|
||||
this.javaexec = javaexec
|
||||
this.instancePath = instancePath
|
||||
}
|
||||
|
||||
// Static Utility Functions
|
||||
// #region
|
||||
|
||||
// Static General Resolve Functions
|
||||
// #region
|
||||
|
||||
/**
|
||||
* Resolve an artifact id into a path. For example, on windows
|
||||
* 'net.minecraftforge:forge:1.11.2-13.20.0.2282', '.jar' becomes
|
||||
* net\minecraftforge\forge\1.11.2-13.20.0.2282\forge-1.11.2-13.20.0.2282.jar
|
||||
*
|
||||
* @param {string} artifactid The artifact id string.
|
||||
* @param {string} extension The extension of the file at the resolved path.
|
||||
* @returns {string} The resolved relative path from the artifact id.
|
||||
*/
|
||||
static _resolvePath(artifactid, extension){
|
||||
let ps = artifactid.split(':')
|
||||
let cs = ps[0].split('.')
|
||||
|
||||
cs.push(ps[1])
|
||||
cs.push(ps[2])
|
||||
cs.push(ps[1].concat('-').concat(ps[2]).concat(extension))
|
||||
|
||||
return path.join.apply(path, cs)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an artifact id into a URL. For example,
|
||||
* 'net.minecraftforge:forge:1.11.2-13.20.0.2282', '.jar' becomes
|
||||
* net/minecraftforge/forge/1.11.2-13.20.0.2282/forge-1.11.2-13.20.0.2282.jar
|
||||
*
|
||||
* @param {string} artifactid The artifact id string.
|
||||
* @param {string} extension The extension of the file at the resolved url.
|
||||
* @returns {string} The resolved relative URL from the artifact id.
|
||||
*/
|
||||
static _resolveURL(artifactid, extension){
|
||||
let ps = artifactid.split(':')
|
||||
let cs = ps[0].split('.')
|
||||
|
||||
cs.push(ps[1])
|
||||
cs.push(ps[2])
|
||||
cs.push(ps[1].concat('-').concat(ps[2]).concat(extension))
|
||||
|
||||
return cs.join('/')
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves an artiface id without the version. For example,
|
||||
* 'net.minecraftforge:forge:1.11.2-13.20.0.2282' becomes
|
||||
* 'net.minecraftforge:forge'.
|
||||
*
|
||||
* @param {string} artifactid The artifact id string.
|
||||
* @returns {string} The resolved identifier without the version.
|
||||
*/
|
||||
static _resolveWithoutVersion(artifactid){
|
||||
let ps = artifactid.split(':')
|
||||
|
||||
return ps[0] + ':' + ps[1]
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// Static Hash Validation Functions
|
||||
// #region
|
||||
|
||||
@@ -386,145 +316,6 @@ class AssetGuard extends EventEmitter {
|
||||
|
||||
// #endregion
|
||||
|
||||
// Static Distribution Index Functions
|
||||
// #region
|
||||
|
||||
/**
|
||||
* Retrieve a new copy of the distribution index from our servers.
|
||||
*
|
||||
* @param {string} launcherPath The root launcher directory.
|
||||
* @returns {Promise.<Object>} A promise which resolves to the distribution data object.
|
||||
*/
|
||||
static refreshDistributionDataRemote(launcherPath){
|
||||
return new Promise((resolve, reject) => {
|
||||
const distroURL = 'http://mc.westeroscraft.com/WesterosCraftLauncher/westeroscraft.json'
|
||||
const opts = {
|
||||
url: distroURL,
|
||||
timeout: 2500
|
||||
}
|
||||
const distroDest = path.join(launcherPath, 'westeroscraft.json')
|
||||
request(opts, (error, resp, body) => {
|
||||
if(!error){
|
||||
distributionData = JSON.parse(body)
|
||||
|
||||
fs.writeFile(distroDest, body, 'utf-8', (err) => {
|
||||
if(!err){
|
||||
resolve(distributionData)
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a local copy of the distribution index asynchronously.
|
||||
*
|
||||
* @param {string} launcherPath The root launcher directory.
|
||||
* @returns {Promise.<Object>} A promise which resolves to the distribution data object.
|
||||
*/
|
||||
static refreshDistributionDataLocal(launcherPath){
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(path.join(launcherPath, 'westeroscraft.json'), 'utf-8', (err, data) => {
|
||||
if(!err){
|
||||
distributionData = JSON.parse(data)
|
||||
resolve(distributionData)
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a local copy of the distribution index synchronously.
|
||||
*
|
||||
* @param {string} launcherPath The root launcher directory.
|
||||
* @returns {Object} The distribution data object.
|
||||
*/
|
||||
static refreshDistributionDataLocalSync(launcherPath){
|
||||
distributionData = JSON.parse(fs.readFileSync(path.join(launcherPath, 'westeroscraft.json'), 'utf-8'))
|
||||
return distributionData
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cached copy of the distribution index.
|
||||
*/
|
||||
static getDistributionData(){
|
||||
return distributionData
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default selected server from the distribution index.
|
||||
*
|
||||
* @returns {Object} An object resolving to the default selected server.
|
||||
*/
|
||||
static resolveSelectedServer(){
|
||||
const distro = AssetGuard.getDistributionData()
|
||||
const servers = distro.servers
|
||||
for(let i=0; i<servers.length; i++){
|
||||
if(servers[i].default_selected){
|
||||
return servers[i]
|
||||
}
|
||||
}
|
||||
// If no server declares default_selected, default to the first one declared.
|
||||
return (servers.length > 0) ? servers[0] : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a server from the distro index which maches the provided ID.
|
||||
* Returns null if the ID could not be found or the distro index has
|
||||
* not yet been loaded.
|
||||
*
|
||||
* @param {string} serverID The id of the server to retrieve.
|
||||
* @returns {Object} The server object whose id matches the parameter.
|
||||
*/
|
||||
static getServerById(serverID){
|
||||
const distro = AssetGuard.getDistributionData()
|
||||
const servers = distro.servers
|
||||
let serv = null
|
||||
for(let i=0; i<servers.length; i++){
|
||||
if(servers[i].id === serverID){
|
||||
serv = servers[i]
|
||||
}
|
||||
}
|
||||
return serv
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not we should launch with a local copy of the distribution
|
||||
* index. This is useful for testing experimental changes to the distribution index.
|
||||
*
|
||||
* @param {boolean} value True if we should launch with a local copy. Otherwise false.
|
||||
*/
|
||||
static launchWithLocal(value, silent = false){
|
||||
if(!silent){
|
||||
if(value){
|
||||
console.log('%c[AssetGuard]', 'color: #a02d2a; font-weight: bold', 'Will now launch using a local copy of the distribution index.')
|
||||
console.log('%c[AssetGuard]', 'color: #a02d2a; font-weight: bold', 'Unless you are a developer, revert this change immediately.')
|
||||
} else {
|
||||
console.log('%c[AssetGuard]', 'color: #a02d2a; font-weight: bold', 'Will now retrieve a fresh copy of the distribution index on launch.')
|
||||
}
|
||||
}
|
||||
launchWithLocal = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if AssetGuard is configured to launch with a local copy
|
||||
* of the distribution index.
|
||||
*
|
||||
* @returns {boolean} True if launching with local, otherwise false.
|
||||
*/
|
||||
static isLocalLaunch(){
|
||||
return launchWithLocal
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// Miscellaneous Static Functions
|
||||
// #region
|
||||
|
||||
@@ -538,8 +329,6 @@ class AssetGuard extends EventEmitter {
|
||||
console.log('[PackXZExtract] Starting')
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
|
||||
|
||||
let libPath
|
||||
if(isDev){
|
||||
libPath = path.join(process.cwd(), 'libraries', 'java', 'PackXZExtract.jar')
|
||||
@@ -1320,11 +1109,11 @@ class AssetGuard extends EventEmitter {
|
||||
//const objKeys = Object.keys(data.objects)
|
||||
async.forEachOfLimit(indexData.objects, 10, (value, key, cb) => {
|
||||
acc++
|
||||
self.emit('assetVal', {acc, total})
|
||||
self.emit('progress', 'assets', acc, total)
|
||||
const hash = value.hash
|
||||
const assetName = path.join(hash.substring(0, 2), hash)
|
||||
const urlName = hash.substring(0, 2) + "/" + hash
|
||||
const ast = new Asset(key, hash, String(value.size), resourceURL + urlName, path.join(objectPath, assetName))
|
||||
const ast = new Asset(key, hash, value.size, resourceURL + urlName, path.join(objectPath, assetName))
|
||||
if(!AssetGuard._validateLocal(ast.to, 'sha1', ast.hash)){
|
||||
dlSize += (ast.size*1)
|
||||
assetDlQueue.push(ast)
|
||||
@@ -1461,30 +1250,22 @@ class AssetGuard extends EventEmitter {
|
||||
/**
|
||||
* Validate the distribution.
|
||||
*
|
||||
* @param {string} serverpackid The id of the server to validate.
|
||||
* @param {Server} server The Server to validate.
|
||||
* @returns {Promise.<Object>} A promise which resolves to the server distribution object.
|
||||
*/
|
||||
validateDistribution(serverpackid){
|
||||
validateDistribution(server){
|
||||
const self = this
|
||||
return new Promise((resolve, reject) => {
|
||||
AssetGuard.refreshDistributionDataLocal(self.launcherPath).then((v) => {
|
||||
const serv = AssetGuard.getServerById(serverpackid)
|
||||
|
||||
if(serv == null) {
|
||||
console.error('Invalid server pack id:', serverpackid)
|
||||
self.forge = self._parseDistroModules(server.getModules(), server.getMinecraftVersion(), server.getID())
|
||||
// Correct our workaround here.
|
||||
let decompressqueue = self.forge.callback
|
||||
self.extractQueue = decompressqueue
|
||||
self.forge.callback = (asset, self) => {
|
||||
if(asset.type === DistroManager.Types.ForgeHosted || asset.type === DistroManager.Types.Forge){
|
||||
AssetGuard._finalizeForgeAsset(asset, self.commonPath).catch(err => console.log(err))
|
||||
}
|
||||
|
||||
self.forge = self._parseDistroModules(serv.modules, serv.mc_version, serv.id)
|
||||
// Correct our workaround here.
|
||||
let decompressqueue = self.forge.callback
|
||||
self.extractQueue = decompressqueue
|
||||
self.forge.callback = (asset, self) => {
|
||||
if(asset.type === 'forge-hosted' || asset.type === 'forge'){
|
||||
AssetGuard._finalizeForgeAsset(asset, self.commonPath)
|
||||
}
|
||||
}
|
||||
resolve(serv)
|
||||
})
|
||||
}
|
||||
resolve(server)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1492,27 +1273,11 @@ class AssetGuard extends EventEmitter {
|
||||
let alist = []
|
||||
let asize = 0;
|
||||
let decompressqueue = []
|
||||
for(let i=0; i<modules.length; i++){
|
||||
let ob = modules[i]
|
||||
let obType = ob.type
|
||||
let obArtifact = ob.artifact
|
||||
let obPath = obArtifact.path == null ? AssetGuard._resolvePath(ob.id, obArtifact.extension) : obArtifact.path
|
||||
switch(obType){
|
||||
case 'forge-hosted':
|
||||
case 'forge':
|
||||
case 'liteloader':
|
||||
case 'library':
|
||||
obPath = path.join(this.commonPath, 'libraries', obPath)
|
||||
break
|
||||
case 'forgemod':
|
||||
case 'litemod':
|
||||
obPath = path.join(this.commonPath, 'modstore', obPath)
|
||||
break
|
||||
case 'file':
|
||||
default:
|
||||
obPath = path.join(this.instancePath, servid, obPath)
|
||||
}
|
||||
let artifact = new DistroModule(ob.id, obArtifact.MD5, obArtifact.size, obArtifact.url, obPath, obType)
|
||||
for(let ob of modules){
|
||||
let obType = ob.getType
|
||||
let obArtifact = ob.getArtifact()
|
||||
let obPath = obArtifact.getPath()
|
||||
let artifact = new DistroModule(ob.getIdentifier(), obArtifact.getHash(), obArtifact.getSize(), obArtifact.getURL(), obPath, obType)
|
||||
const validationPath = obPath.toLowerCase().endsWith('.pack.xz') ? obPath.substring(0, obPath.toLowerCase().lastIndexOf('.pack.xz')) : obPath
|
||||
if(!AssetGuard._validateLocal(validationPath, 'MD5', artifact.hash)){
|
||||
asize += artifact.size*1
|
||||
@@ -1520,8 +1285,8 @@ class AssetGuard extends EventEmitter {
|
||||
if(validationPath !== obPath) decompressqueue.push(obPath)
|
||||
}
|
||||
//Recursively process the submodules then combine the results.
|
||||
if(ob.sub_modules != null){
|
||||
let dltrack = this._parseDistroModules(ob.sub_modules, version, servid)
|
||||
if(ob.getSubModules() != null){
|
||||
let dltrack = this._parseDistroModules(ob.getSubModules(), version, servid)
|
||||
asize += dltrack.dlsize*1
|
||||
alist = alist.concat(dltrack.dlqueue)
|
||||
decompressqueue = decompressqueue.concat(dltrack.callback)
|
||||
@@ -1535,30 +1300,19 @@ class AssetGuard extends EventEmitter {
|
||||
/**
|
||||
* Loads Forge's version.json data into memory for the specified server id.
|
||||
*
|
||||
* @param {string} serverpack The id of the server to load Forge data for.
|
||||
* @param {string} server The Server to load Forge data for.
|
||||
* @returns {Promise.<Object>} A promise which resolves to Forge's version.json data.
|
||||
*/
|
||||
loadForgeData(serverpack){
|
||||
loadForgeData(server){
|
||||
const self = this
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let distro = AssetGuard.getDistributionData()
|
||||
|
||||
const servers = distro.servers
|
||||
let serv = null
|
||||
for(let i=0; i<servers.length; i++){
|
||||
if(servers[i].id === serverpack){
|
||||
serv = servers[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const modules = serv.modules
|
||||
for(let i=0; i<modules.length; i++){
|
||||
const ob = modules[i]
|
||||
if(ob.type === 'forge-hosted' || ob.type === 'forge'){
|
||||
let obArtifact = ob.artifact
|
||||
let obPath = obArtifact.path == null ? path.join(self.commonPath, 'libraries', AssetGuard._resolvePath(ob.id, obArtifact.extension)) : obArtifact.path
|
||||
let asset = new DistroModule(ob.id, obArtifact.MD5, obArtifact.size, obArtifact.url, obPath, ob.type)
|
||||
const modules = server.getModules()
|
||||
for(let ob of modules){
|
||||
const type = ob.getType()
|
||||
if(type === DistroManager.Types.ForgeHosted || type === DistroManager.Types.Forge){
|
||||
let obArtifact = ob.getArtifact()
|
||||
let obPath = obArtifact.getPath()
|
||||
let asset = new DistroModule(ob.getIdentifier(), obArtifact.getHash(), obArtifact.getSize(), obArtifact.getURL(), obPath, type)
|
||||
let forgeData = await AssetGuard._finalizeForgeAsset(asset, self.commonPath)
|
||||
resolve(forgeData)
|
||||
return
|
||||
@@ -1602,7 +1356,7 @@ class AssetGuard extends EventEmitter {
|
||||
dataDir = path.join(dataDir, 'runtime', 'x64')
|
||||
const name = combined.substring(combined.lastIndexOf('/')+1)
|
||||
const fDir = path.join(dataDir, name)
|
||||
const jre = new Asset(name, null, resp.headers['content-length'], opts, fDir)
|
||||
const jre = new Asset(name, null, parseInt(resp.headers['content-length']), opts, fDir)
|
||||
this.java = new DLTracker([jre], jre.size, (a, self) => {
|
||||
let h = null
|
||||
fs.createReadStream(a.to)
|
||||
@@ -1626,7 +1380,7 @@ class AssetGuard extends EventEmitter {
|
||||
h = h.substring(0, h.indexOf('/'))
|
||||
}
|
||||
const pos = path.join(dataDir, h)
|
||||
self.emit('jExtracted', AssetGuard.javaExecFromRoot(pos))
|
||||
self.emit('complete', 'java', AssetGuard.javaExecFromRoot(pos))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1696,74 +1450,109 @@ class AssetGuard extends EventEmitter {
|
||||
* @returns {boolean} True if the process began, otherwise false.
|
||||
*/
|
||||
startAsyncProcess(identifier, limit = 5){
|
||||
|
||||
const self = this
|
||||
let acc = 0
|
||||
const concurrentDlTracker = this[identifier]
|
||||
const concurrentDlQueue = concurrentDlTracker.dlqueue.slice(0)
|
||||
if(concurrentDlQueue.length === 0){
|
||||
return false
|
||||
} else {
|
||||
console.log('DLQueue', concurrentDlQueue)
|
||||
async.eachLimit(concurrentDlQueue, limit, (asset, cb) => {
|
||||
let count = 0;
|
||||
mkpath.sync(path.join(asset.to, ".."))
|
||||
const dlTracker = this[identifier]
|
||||
const dlQueue = dlTracker.dlqueue
|
||||
|
||||
if(dlQueue.length > 0){
|
||||
console.log('DLQueue', dlQueue)
|
||||
|
||||
async.eachLimit(dlQueue, limit, (asset, cb) => {
|
||||
|
||||
mkpath.sync(path.join(asset.to, '..'))
|
||||
|
||||
let req = request(asset.from)
|
||||
req.pause()
|
||||
|
||||
req.on('response', (resp) => {
|
||||
|
||||
if(resp.statusCode === 200){
|
||||
|
||||
let doHashCheck = false
|
||||
const contentLength = parseInt(resp.headers['content-length'])
|
||||
|
||||
if(contentLength !== asset.size){
|
||||
console.log(`WARN: Got ${contentLength} bytes for ${asset.id}: Expected ${asset.size}`)
|
||||
doHashCheck = true
|
||||
|
||||
// Adjust download
|
||||
this.totaldlsize -= asset.size
|
||||
this.totaldlsize += contentLength
|
||||
}
|
||||
|
||||
let writeStream = fs.createWriteStream(asset.to)
|
||||
writeStream.on('close', () => {
|
||||
//console.log('DLResults ' + asset.size + ' ' + count + ' ', asset.size === count)
|
||||
if(concurrentDlTracker.callback != null){
|
||||
concurrentDlTracker.callback.apply(concurrentDlTracker, [asset, self])
|
||||
if(dlTracker.callback != null){
|
||||
dlTracker.callback.apply(dlTracker, [asset, self])
|
||||
}
|
||||
|
||||
if(doHashCheck){
|
||||
const v = AssetGuard._validateLocal(asset.to, asset.type != null ? 'md5' : 'sha1', asset.hash)
|
||||
if(v){
|
||||
console.log(`Hashes match for ${asset.id}, byte mismatch is an issue in the distro index.`)
|
||||
} else {
|
||||
console.error(`Hashes do not match, ${asset.id} may be corrupted.`)
|
||||
}
|
||||
}
|
||||
|
||||
cb()
|
||||
})
|
||||
req.pipe(writeStream)
|
||||
req.resume()
|
||||
|
||||
} else {
|
||||
|
||||
req.abort()
|
||||
const realFrom = typeof asset.from === 'object' ? asset.from.url : asset.from
|
||||
console.log('Failed to download ' + realFrom + '. Response code', resp.statusCode)
|
||||
console.log(`Failed to download ${asset.id}(${typeof asset.from === 'object' ? asset.from.url : asset.from}). Response code ${resp.statusCode}`)
|
||||
self.progress += asset.size*1
|
||||
self.emit('totaldlprogress', {acc: self.progress, total: self.totaldlsize})
|
||||
self.emit('progress', 'download', self.progress, self.totaldlsize)
|
||||
cb()
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
req.on('error', (err) => {
|
||||
self.emit('dlerror', err)
|
||||
self.emit('error', 'download', err)
|
||||
})
|
||||
|
||||
req.on('data', (chunk) => {
|
||||
count += chunk.length
|
||||
self.progress += chunk.length
|
||||
acc += chunk.length
|
||||
self.emit(identifier + 'dlprogress', acc)
|
||||
self.emit('totaldlprogress', {acc: self.progress, total: self.totaldlsize})
|
||||
self.emit('progress', 'download', self.progress, self.totaldlsize)
|
||||
})
|
||||
|
||||
}, (err) => {
|
||||
|
||||
if(err){
|
||||
self.emit(identifier + 'dlerror')
|
||||
console.log('An item in ' + identifier + ' failed to process');
|
||||
} else {
|
||||
self.emit(identifier + 'dlcomplete')
|
||||
console.log('All ' + identifier + ' have been processed successfully')
|
||||
}
|
||||
self.totaldlsize -= self[identifier].dlsize
|
||||
self.progress -= self[identifier].dlsize
|
||||
|
||||
//self.totaldlsize -= dlTracker.dlsize
|
||||
//self.progress -= dlTracker.dlsize
|
||||
self[identifier] = new DLTracker([], 0)
|
||||
if(self.totaldlsize === 0) {
|
||||
|
||||
if(self.progress >= self.totaldlsize) {
|
||||
if(self.extractQueue.length > 0){
|
||||
self.emit('extracting')
|
||||
self.emit('progress', 'extract', 1, 1)
|
||||
//self.emit('extracting')
|
||||
AssetGuard._extractPackXZ(self.extractQueue, self.javaexec).then(() => {
|
||||
self.extractQueue = []
|
||||
self.emit('dlcomplete')
|
||||
self.emit('complete', 'download')
|
||||
})
|
||||
} else {
|
||||
self.emit('dlcomplete')
|
||||
self.emit('complete', 'download')
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return true
|
||||
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1772,32 +1561,69 @@ class AssetGuard extends EventEmitter {
|
||||
* given, all identifiers will be initiated. Note that in order for files to be processed you need to run
|
||||
* the processing function corresponding to that identifier. If you run this function without processing
|
||||
* the files, it is likely nothing will be enqueued in the object and processing will complete
|
||||
* immediately. Once all downloads are complete, this function will fire the 'dlcomplete' event on the
|
||||
* immediately. Once all downloads are complete, this function will fire the 'complete' event on the
|
||||
* global object instance.
|
||||
*
|
||||
* @param {Array.<{id: string, limit: number}>} identifiers Optional. The identifiers to process and corresponding parallel async task limit.
|
||||
*/
|
||||
processDlQueues(identifiers = [{id:'assets', limit:20}, {id:'libraries', limit:5}, {id:'files', limit:5}, {id:'forge', limit:5}]){
|
||||
this.progress = 0;
|
||||
return new Promise((resolve, reject) => {
|
||||
let shouldFire = true
|
||||
|
||||
let shouldFire = true
|
||||
// Assign dltracking variables.
|
||||
this.totaldlsize = 0
|
||||
this.progress = 0
|
||||
|
||||
// Assign dltracking variables.
|
||||
this.totaldlsize = 0
|
||||
this.progress = 0
|
||||
for(let i=0; i<identifiers.length; i++){
|
||||
this.totaldlsize += this[identifiers[i].id].dlsize
|
||||
for(let iden of identifiers){
|
||||
this.totaldlsize += this[iden.id].dlsize
|
||||
}
|
||||
|
||||
this.once('complete', (data) => {
|
||||
resolve()
|
||||
})
|
||||
|
||||
for(let iden of identifiers){
|
||||
let r = this.startAsyncProcess(iden.id, iden.limit)
|
||||
if(r) shouldFire = false
|
||||
}
|
||||
|
||||
if(shouldFire){
|
||||
this.emit('complete', 'download')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async validateEverything(serverid, dev = false){
|
||||
|
||||
if(!ConfigManager.isLoaded()){
|
||||
ConfigManager.load()
|
||||
}
|
||||
DistroManager.setDevMode(dev)
|
||||
const dI = await DistroManager.pullLocal()
|
||||
|
||||
const server = dI.getServer(serverid)
|
||||
|
||||
// Validate Everything
|
||||
|
||||
await this.validateDistribution(server)
|
||||
this.emit('validate', 'distribution')
|
||||
const versionData = await this.loadVersionData(server.getMinecraftVersion())
|
||||
this.emit('validate', 'version')
|
||||
await this.validateAssets(versionData)
|
||||
this.emit('validate', 'assets')
|
||||
await this.validateLibraries(versionData)
|
||||
this.emit('validate', 'libraries')
|
||||
await this.validateMiscellaneous(versionData)
|
||||
this.emit('validate', 'files')
|
||||
await this.processDlQueues()
|
||||
//this.emit('complete', 'download')
|
||||
const forgeData = await this.loadForgeData(server)
|
||||
|
||||
return {
|
||||
versionData,
|
||||
forgeData
|
||||
}
|
||||
|
||||
for(let i=0; i<identifiers.length; i++){
|
||||
let iden = identifiers[i]
|
||||
let r = this.startAsyncProcess(iden.id, iden.limit)
|
||||
if(r) shouldFire = false
|
||||
}
|
||||
|
||||
if(shouldFire){
|
||||
this.emit('dlcomplete')
|
||||
}
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
* @module authmanager
|
||||
*/
|
||||
// Requirements
|
||||
const ConfigManager = require('./configmanager.js')
|
||||
const Mojang = require('./mojang.js')
|
||||
const ConfigManager = require('./configmanager')
|
||||
const Mojang = require('./mojang')
|
||||
|
||||
// Functions
|
||||
|
||||
|
||||
@@ -108,6 +108,13 @@ exports.load = function(){
|
||||
console.log('%c[ConfigManager]', 'color: #a02d2a; font-weight: bold', 'Successfully Loaded')
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} Whether or not the manager has been loaded.
|
||||
*/
|
||||
exports.isLoaded = function(){
|
||||
return config != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the destination object has at least every field
|
||||
* present in the source object. Assign a default value otherwise.
|
||||
|
||||
@@ -1,49 +1,47 @@
|
||||
// Work in progress
|
||||
const {Client} = require('discord-rpc')
|
||||
const ConfigManager = require('./configmanager.js')
|
||||
const ConfigManager = require('./configmanager')
|
||||
|
||||
let rpc
|
||||
let client
|
||||
let activity
|
||||
|
||||
exports.initRPC = function(genSettings, servSettings, initialDetails = 'Waiting for Client..'){
|
||||
rpc = new Client({ transport: 'ipc' })
|
||||
client = new Client({ transport: 'ipc' })
|
||||
|
||||
rpc.on('ready', () => {
|
||||
activity = {
|
||||
details: initialDetails,
|
||||
state: 'Server: ' + servSettings.shortId,
|
||||
largeImageKey: servSettings.largeImageKey,
|
||||
largeImageText: servSettings.largeImageText,
|
||||
smallImageKey: genSettings.smallImageKey,
|
||||
smallImageText: genSettings.smallImageText,
|
||||
startTimestamp: new Date().getTime() / 1000,
|
||||
instance: false
|
||||
}
|
||||
|
||||
rpc.setActivity(activity)
|
||||
activity = {
|
||||
details: initialDetails,
|
||||
state: 'Server: ' + servSettings.shortId,
|
||||
largeImageKey: servSettings.largeImageKey,
|
||||
largeImageText: servSettings.largeImageText,
|
||||
smallImageKey: genSettings.smallImageKey,
|
||||
smallImageText: genSettings.smallImageText,
|
||||
startTimestamp: new Date().getTime() / 1000,
|
||||
instance: false
|
||||
}
|
||||
|
||||
client.on('ready', () => {
|
||||
console.log('%c[Discord Wrapper]', 'color: #a02d2a; font-weight: bold', 'Discord RPC Connected')
|
||||
client.setActivity(activity)
|
||||
})
|
||||
|
||||
rpc.login(genSettings.clientID).catch(error => {
|
||||
|
||||
client.login({clientId: genSettings.clientId}).catch(error => {
|
||||
if(error.message.includes('ENOENT')) {
|
||||
console.log('Unable to initialize Discord Rich Presence, no client detected.')
|
||||
console.log('%c[Discord Wrapper]', 'color: #a02d2a; font-weight: bold', 'Unable to initialize Discord Rich Presence, no client detected.')
|
||||
} else {
|
||||
console.log('Unable to initialize Discord Rich Presence: ' + error.message, error)
|
||||
console.log('%c[Discord Wrapper]', 'color: #a02d2a; font-weight: bold', 'Unable to initialize Discord Rich Presence: ' + error.message, error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
exports.updateDetails = function(details){
|
||||
if(activity == null){
|
||||
console.error('Discord RPC is not initialized and therefore cannot be updated.')
|
||||
}
|
||||
activity.details = details
|
||||
rpc.setActivity(activity)
|
||||
client.setActivity(activity)
|
||||
}
|
||||
|
||||
exports.shutdownRPC = function(){
|
||||
if(!rpc) return
|
||||
rpc.clearActivity()
|
||||
rpc.destroy()
|
||||
rpc = null
|
||||
if(!client) return
|
||||
client.clearActivity()
|
||||
client.destroy()
|
||||
client = null
|
||||
activity = null
|
||||
}
|
||||
584
app/assets/js/distromanager.js
Normal file
584
app/assets/js/distromanager.js
Normal file
@@ -0,0 +1,584 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const request = require('request')
|
||||
|
||||
const ConfigManager = require('./configmanager')
|
||||
|
||||
/**
|
||||
* Represents the download information
|
||||
* for a specific module.
|
||||
*/
|
||||
class Artifact {
|
||||
|
||||
/**
|
||||
* Parse a JSON object into an Artifact.
|
||||
*
|
||||
* @param {Object} json A JSON object representing an Artifact.
|
||||
*
|
||||
* @returns {Artifact} The parsed Artifact.
|
||||
*/
|
||||
static fromJSON(json){
|
||||
return Object.assign(new Artifact(), json)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MD5 hash of the artifact. This value may
|
||||
* be undefined for artifacts which are not to be
|
||||
* validated and updated.
|
||||
*
|
||||
* @returns {string} The MD5 hash of the Artifact or undefined.
|
||||
*/
|
||||
getHash(){
|
||||
return this.MD5
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number} The download size of the artifact.
|
||||
*/
|
||||
getSize(){
|
||||
return this.size
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The download url of the artifact.
|
||||
*/
|
||||
getURL(){
|
||||
return this.url
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The artifact's destination path.
|
||||
*/
|
||||
getPath(){
|
||||
return this.path
|
||||
}
|
||||
|
||||
}
|
||||
exports.Artifact
|
||||
|
||||
/**
|
||||
* Represents a the requirement status
|
||||
* of a module.
|
||||
*/
|
||||
class Required {
|
||||
|
||||
/**
|
||||
* Parse a JSON object into a Required object.
|
||||
*
|
||||
* @param {Object} json A JSON object representing a Required object.
|
||||
*
|
||||
* @returns {Required} The parsed Required object.
|
||||
*/
|
||||
static fromJSON(json){
|
||||
if(json == null){
|
||||
return new Required(true, true)
|
||||
} else {
|
||||
return new Required(json.value == null ? true : json.value, json.def == null ? true : json.def)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(value, def){
|
||||
this.value = value
|
||||
this.default = def
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default value for a required object. If a module
|
||||
* is not required, this value determines whether or not
|
||||
* it is enabled by default.
|
||||
*
|
||||
* @returns {boolean} The default enabled value.
|
||||
*/
|
||||
isDefault(){
|
||||
return this.default
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} Whether or not the module is required.
|
||||
*/
|
||||
isRequired(){
|
||||
return this.value
|
||||
}
|
||||
|
||||
}
|
||||
exports.Required
|
||||
|
||||
/**
|
||||
* Represents a module.
|
||||
*/
|
||||
class Module {
|
||||
|
||||
/**
|
||||
* Parse a JSON object into a Module.
|
||||
*
|
||||
* @param {Object} json A JSON object representing a Module.
|
||||
* @param {string} serverid The ID of the server to which this module belongs.
|
||||
*
|
||||
* @returns {Module} The parsed Module.
|
||||
*/
|
||||
static fromJSON(json, serverid){
|
||||
return new Module(json.id, json.name, json.type, json.required, json.artifact, json.subModules, serverid)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default extension for a specific module type.
|
||||
*
|
||||
* @param {string} type The type of the module.
|
||||
*
|
||||
* @return {string} The default extension for the given type.
|
||||
*/
|
||||
static _resolveDefaultExtension(type){
|
||||
switch (type) {
|
||||
case exports.Types.Library:
|
||||
case exports.Types.ForgeHosted:
|
||||
case exports.Types.LiteLoader:
|
||||
case exports.Types.ForgeMod:
|
||||
return 'jar'
|
||||
case exports.Types.LiteMod:
|
||||
return 'litemod'
|
||||
case exports.Types.File:
|
||||
default:
|
||||
return 'jar' // There is no default extension really.
|
||||
}
|
||||
}
|
||||
|
||||
constructor(id, name, type, required, artifact, subModules, serverid) {
|
||||
this.identifier = id
|
||||
this.type = type
|
||||
this._resolveMetaData()
|
||||
this.name = name
|
||||
this.required = Required.fromJSON(required)
|
||||
this.artifact = Artifact.fromJSON(artifact)
|
||||
this._resolveArtifactPath(artifact.path, serverid)
|
||||
this._resolveSubModules(subModules, serverid)
|
||||
}
|
||||
|
||||
_resolveMetaData(){
|
||||
try {
|
||||
|
||||
const m0 = this.identifier.split('@')
|
||||
|
||||
this.artifactExt = m0[1] || Module._resolveDefaultExtension(this.type)
|
||||
|
||||
const m1 = m0[0].split(':')
|
||||
|
||||
this.artifactVersion = m1[2] || '???'
|
||||
this.artifactID = m1[1] || '???'
|
||||
this.artifactGroup = m1[0] || '???'
|
||||
|
||||
} catch (err) {
|
||||
// Improper identifier
|
||||
console.error('Improper ID for module', this.identifier, err)
|
||||
}
|
||||
}
|
||||
|
||||
_resolveArtifactPath(artifactPath, serverid){
|
||||
const pth = artifactPath == null ? path.join(...this.getGroup().split('.'), this.getID(), this.getVersion(), `${this.getID()}-${this.getVersion()}.${this.getExtension()}`) : artifactPath
|
||||
|
||||
switch (this.type){
|
||||
case exports.Types.Library:
|
||||
case exports.Types.ForgeHosted:
|
||||
case exports.Types.LiteLoader:
|
||||
this.artifact.path = path.join(ConfigManager.getCommonDirectory(), 'libraries', pth)
|
||||
break
|
||||
case exports.Types.ForgeMod:
|
||||
case exports.Types.LiteMod:
|
||||
this.artifact.path = path.join(ConfigManager.getCommonDirectory(), 'modstore', pth)
|
||||
break
|
||||
case exports.Types.File:
|
||||
default:
|
||||
this.artifact.path = path.join(ConfigManager.getInstanceDirectory(), serverid, pth)
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_resolveSubModules(json, serverid){
|
||||
const arr = []
|
||||
if(json != null){
|
||||
for(let sm of json){
|
||||
arr.push(Module.fromJSON(sm, serverid))
|
||||
}
|
||||
}
|
||||
this.subModules = arr.length > 0 ? arr : null
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The full, unparsed module identifier.
|
||||
*/
|
||||
getIdentifier(){
|
||||
return this.identifier
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The name of the module.
|
||||
*/
|
||||
getName(){
|
||||
return this.name
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Required} The required object declared by this module.
|
||||
*/
|
||||
getRequired(){
|
||||
return this.required
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Artifact} The artifact declared by this module.
|
||||
*/
|
||||
getArtifact(){
|
||||
return this.artifact
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The maven identifier of this module's artifact.
|
||||
*/
|
||||
getID(){
|
||||
return this.artifactID
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The maven group of this module's artifact.
|
||||
*/
|
||||
getGroup(){
|
||||
return this.artifactGroup
|
||||
}
|
||||
|
||||
getVersionlessID(){
|
||||
return this.getGroup() + ':' + this.getID()
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The version of this module's artifact.
|
||||
*/
|
||||
getVersion(){
|
||||
return this.artifactVersion
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The extension of this module's artifact.
|
||||
*/
|
||||
getExtension(){
|
||||
return this.artifactExt
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} Whether or not this module has sub modules.
|
||||
*/
|
||||
hasSubModules(){
|
||||
return this.subModules != null
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Array.<Module>} An array of sub modules.
|
||||
*/
|
||||
getSubModules(){
|
||||
return this.subModules
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The type of the module.
|
||||
*/
|
||||
getType(){
|
||||
return this.type
|
||||
}
|
||||
|
||||
}
|
||||
exports.Module
|
||||
|
||||
/**
|
||||
* Represents a server configuration.
|
||||
*/
|
||||
class Server {
|
||||
|
||||
/**
|
||||
* Parse a JSON object into a Server.
|
||||
*
|
||||
* @param {Object} json A JSON object representing a Server.
|
||||
*
|
||||
* @returns {Server} The parsed Server object.
|
||||
*/
|
||||
static fromJSON(json){
|
||||
|
||||
const mdls = json.modules
|
||||
json.modules = []
|
||||
|
||||
const serv = Object.assign(new Server(), json)
|
||||
serv._resolveModules(mdls)
|
||||
|
||||
return serv
|
||||
}
|
||||
|
||||
_resolveModules(json){
|
||||
const arr = []
|
||||
for(let m of json){
|
||||
arr.push(Module.fromJSON(m, this.getID()))
|
||||
}
|
||||
this.modules = arr
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The ID of the server.
|
||||
*/
|
||||
getID(){
|
||||
return this.id
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The name of the server.
|
||||
*/
|
||||
getName(){
|
||||
return this.name
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The description of the server.
|
||||
*/
|
||||
getDescription(){
|
||||
return this.description
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The URL of the server's icon.
|
||||
*/
|
||||
getIcon(){
|
||||
return this.icon
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The version of the server configuration.
|
||||
*/
|
||||
getVersion(){
|
||||
return this.version
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The IP address of the server.
|
||||
*/
|
||||
getAddress(){
|
||||
return this.address
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The minecraft version of the server.
|
||||
*/
|
||||
getMinecraftVersion(){
|
||||
return this.minecraftVersion
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} Whether or not this server is the main
|
||||
* server. The main server is selected by the launcher when
|
||||
* no valid server is selected.
|
||||
*/
|
||||
isMainServer(){
|
||||
return this.mainServer
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} Whether or not the server is autoconnect.
|
||||
* by default.
|
||||
*/
|
||||
isAutoConnect(){
|
||||
return this.autoconnect
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Array.<Module>} An array of modules for this server.
|
||||
*/
|
||||
getModules(){
|
||||
return this.modules
|
||||
}
|
||||
|
||||
}
|
||||
exports.Server
|
||||
|
||||
/**
|
||||
* Represents the Distribution Index.
|
||||
*/
|
||||
class DistroIndex {
|
||||
|
||||
/**
|
||||
* Parse a JSON object into a DistroIndex.
|
||||
*
|
||||
* @param {Object} json A JSON object representing a DistroIndex.
|
||||
*
|
||||
* @returns {DistroIndex} The parsed Server object.
|
||||
*/
|
||||
static fromJSON(json){
|
||||
|
||||
const servers = json.servers
|
||||
json.servers = []
|
||||
|
||||
const distro = Object.assign(new DistroIndex(), json)
|
||||
distro._resolveServers(servers)
|
||||
distro._resolveMainServer()
|
||||
|
||||
return distro
|
||||
}
|
||||
|
||||
_resolveServers(json){
|
||||
const arr = []
|
||||
for(let s of json){
|
||||
arr.push(Server.fromJSON(s))
|
||||
}
|
||||
this.servers = arr
|
||||
}
|
||||
|
||||
_resolveMainServer(){
|
||||
|
||||
for(let serv of this.servers){
|
||||
if(serv.mainServer){
|
||||
this.mainServer = serv.id
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If no server declares default_selected, default to the first one declared.
|
||||
this.mainServer = (this.servers.length > 0) ? this.servers[0].getID() : null
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The version of the distribution index.
|
||||
*/
|
||||
getVersion(){
|
||||
return this.version
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The URL to the news RSS feed.
|
||||
*/
|
||||
getRSS(){
|
||||
return this.rss
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Array.<Server>} An array of declared server configurations.
|
||||
*/
|
||||
getServers(){
|
||||
return this.servers
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a server configuration by its ID. If it does not
|
||||
* exist, null will be returned.
|
||||
*
|
||||
* @param {string} id The ID of the server.
|
||||
*
|
||||
* @returns {Server} The server configuration with the given ID or null.
|
||||
*/
|
||||
getServer(id){
|
||||
for(let serv of this.servers){
|
||||
if(serv.id === id){
|
||||
return serv
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main server.
|
||||
*
|
||||
* @returns {Server} The main server.
|
||||
*/
|
||||
getMainServer(){
|
||||
return getServer(this.mainServer)
|
||||
}
|
||||
|
||||
}
|
||||
exports.DistroIndex
|
||||
|
||||
exports.Types = {
|
||||
Library: 'Library',
|
||||
ForgeHosted: 'ForgeHosted',
|
||||
Forge: 'Forge', // Unimplemented
|
||||
LiteLoader: 'LiteLoader',
|
||||
ForgeMod: 'ForgeMod',
|
||||
LiteMod: 'LiteMod',
|
||||
File: 'File'
|
||||
}
|
||||
|
||||
let DEV_MODE = false
|
||||
|
||||
const DISTRO_PATH = path.join(ConfigManager.getLauncherDirectory(), 'distribution.json')
|
||||
const DEV_PATH = path.join(ConfigManager.getLauncherDirectory(), 'dev_distribution.json')
|
||||
|
||||
let data = null
|
||||
|
||||
/**
|
||||
* @returns {Promise.<DistroIndex>}
|
||||
*/
|
||||
exports.pullRemote = function(){
|
||||
if(DEV_MODE){
|
||||
return exports.pullLocal()
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const distroURL = 'http://mc.westeroscraft.com/WesterosCraftLauncher/distribution.json'
|
||||
//const distroURL = 'https://gist.githubusercontent.com/dscalzi/53b1ba7a11d26a5c353f9d5ae484b71b/raw/'
|
||||
const opts = {
|
||||
url: distroURL,
|
||||
timeout: 2500
|
||||
}
|
||||
const distroDest = path.join(ConfigManager.getLauncherDirectory(), 'distribution.json')
|
||||
request(opts, (error, resp, body) => {
|
||||
if(!error){
|
||||
data = DistroIndex.fromJSON(JSON.parse(body))
|
||||
|
||||
fs.writeFile(distroDest, body, 'utf-8', (err) => {
|
||||
if(!err){
|
||||
resolve(data)
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise.<DistroIndex>}
|
||||
*/
|
||||
exports.pullLocal = function(){
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(DEV_MODE ? DEV_PATH : DISTRO_PATH, 'utf-8', (err, d) => {
|
||||
if(!err){
|
||||
data = DistroIndex.fromJSON(JSON.parse(d))
|
||||
resolve(data)
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
exports.setDevMode = function(value){
|
||||
if(value){
|
||||
console.log('%c[DistroManager]', 'color: #a02d2a; font-weight: bold', 'Developer mode enabled.')
|
||||
console.log('%c[DistroManager]', 'color: #a02d2a; font-weight: bold', 'If you don\'t know what that means, revert immediately.')
|
||||
} else {
|
||||
console.log('%c[DistroManager]', 'color: #a02d2a; font-weight: bold', 'Developer mode disabled.')
|
||||
}
|
||||
DEV_MODE = value
|
||||
}
|
||||
|
||||
exports.isDevMode = function(){
|
||||
return DEV_MODE
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {DistroIndex}
|
||||
*/
|
||||
exports.getDistribution = function(){
|
||||
return data
|
||||
}
|
||||
|
||||
/*async function debug(){
|
||||
const d = await exports.pullRemote()
|
||||
console.log(d)
|
||||
}
|
||||
debug()*/
|
||||
//console.log(DistroIndex.fromJSON(JSON.parse(require('fs').readFileSync('../distribution.json', 'utf-8'))))
|
||||
@@ -1,84 +0,0 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"servers": [
|
||||
{
|
||||
"id": "WesterosCraft-1.11.2",
|
||||
"name": "WesterosCraft Production Client",
|
||||
"news-feed": "http://www.westeroscraft.com/api/rss.php?preset_id=12700544",
|
||||
"icon-url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/server-prod.png",
|
||||
"revision": "0.0.1",
|
||||
"server-ip": "mc.westeroscraft.com",
|
||||
"mc-version": "1.11.2",
|
||||
"modules": [
|
||||
{
|
||||
"id": "MODNAME",
|
||||
"name": "Mod Name version 1.11.2",
|
||||
"type": "forgemod",
|
||||
"_comment": "If no required is given, it will default to true. If a def(ault) is not give, it will default to true. If required is present it always expects a value.",
|
||||
"required": {
|
||||
"value": false,
|
||||
"def": false
|
||||
},
|
||||
"artifact": {
|
||||
"size": 1234,
|
||||
"MD5": "e71e88c744588fdad48d3b3beb4935fc",
|
||||
"path": "forgemod path is appended to {basepath}/mods",
|
||||
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.11.2/somemod.jar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"_comment": "Forge is a special instance of library.",
|
||||
"id": "net.minecraftforge.forge.forge-universal:1.11.2-13.20.0.2228",
|
||||
"name": "Minecraft Forge 1.11.2-13.20.0.2228",
|
||||
"type": "forge",
|
||||
"artifact": {
|
||||
"size": 4123353,
|
||||
"MD5": "5b9105f1a8552beac0c8228203d994ae",
|
||||
"path": "net/minecraftforge/forge/1.11.2-13.20.0.2228/forge-1.11.2-13.20.0.2228-universal.jar",
|
||||
"url": "http://files.minecraftforge.net/maven/net/minecraftforge/forge/1.11.2-13.20.0.2228/forge-1.11.2-13.20.0.2228-universal.jar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"_comment": "library path is appended to {basepath}/libraries",
|
||||
"id": "net.optifine.optifine:1.11.2_HD_U_B8",
|
||||
"name": "Optifine 1.11.2 HD U B8",
|
||||
"type": "library",
|
||||
"artifact": {
|
||||
"size": 2050307,
|
||||
"MD5": "c18c80f8bfa2a440cc5af4ab8816bc4b",
|
||||
"path": "optifine/OptiFine/1.11.2_HD_U_B8/OptiFine-1.11.2_HD_U_B8.jar",
|
||||
"url": "http://optifine.net/download.php?f=OptiFine_1.11.2_HD_U_B8.jar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "chatbubbles",
|
||||
"name": "Chat Bubbles 1.11.2",
|
||||
"type": "litemod",
|
||||
"required": {
|
||||
"value": false
|
||||
},
|
||||
"artifact": {
|
||||
"size": 37838,
|
||||
"MD5": "0497a93e5429b43082282e9d9119fcba",
|
||||
"path": "litemod path is appended to {basepath}/mods/{mc-version}",
|
||||
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.11.2/mod_chatBubbles-1.0.1_for_1.11.2.litemod"
|
||||
},
|
||||
"_comment": "Any module can declare submodules, even submodules.",
|
||||
"sub-modules": [
|
||||
{
|
||||
"id": "customRegexes",
|
||||
"name": "Custom Regexes for Chat Bubbles",
|
||||
"type": "file",
|
||||
"artifact": {
|
||||
"size": 331,
|
||||
"MD5": "f21b4b325f09238a3d6b2103d54351ef",
|
||||
"path": "file path is appended to {basepath}",
|
||||
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.11.2/customRegexes.txt"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
const {AssetGuard} = require('./assetguard.js')
|
||||
const ConfigManager = require('./configmanager.js')
|
||||
const {ipcRenderer} = require('electron')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const rimraf = require('rimraf')
|
||||
|
||||
const ConfigManager = require('./configmanager')
|
||||
const DistroManager = require('./distromanager')
|
||||
|
||||
console.log('%c[Preloader]', 'color: #a02d2a; font-weight: bold', 'Loading..')
|
||||
|
||||
// Load ConfigManager
|
||||
@@ -14,17 +15,17 @@ function onDistroLoad(data){
|
||||
if(data != null){
|
||||
|
||||
// Resolve the selected server if its value has yet to be set.
|
||||
if(ConfigManager.getSelectedServer() == null || AssetGuard.getServerById(ConfigManager.getSelectedServer()) == null){
|
||||
if(ConfigManager.getSelectedServer() == null || data.getServer(ConfigManager.getSelectedServer()) == null){
|
||||
console.log('%c[Preloader]', 'color: #a02d2a; font-weight: bold', 'Determining default selected server..')
|
||||
ConfigManager.setSelectedServer(AssetGuard.resolveSelectedServer().id)
|
||||
ConfigManager.setSelectedServer(data.getMainServer().getID())
|
||||
ConfigManager.save()
|
||||
}
|
||||
}
|
||||
ipcRenderer.send('distributionIndexDone', data)
|
||||
ipcRenderer.send('distributionIndexDone', data != null)
|
||||
}
|
||||
|
||||
// Ensure Distribution is downloaded and cached.
|
||||
AssetGuard.refreshDistributionDataRemote(ConfigManager.getLauncherDirectory()).then((data) => {
|
||||
DistroManager.pullRemote().then((data) => {
|
||||
console.log('%c[Preloader]', 'color: #a02d2a; font-weight: bold', 'Loaded distribution index.')
|
||||
|
||||
onDistroLoad(data)
|
||||
@@ -35,7 +36,7 @@ AssetGuard.refreshDistributionDataRemote(ConfigManager.getLauncherDirectory()).t
|
||||
|
||||
console.log('%c[Preloader]', 'color: #a02d2a; font-weight: bold', 'Attempting to load an older version of the distribution index.')
|
||||
// Try getting a local copy, better than nothing.
|
||||
AssetGuard.refreshDistributionDataLocal(ConfigManager.getLauncherDirectory()).then((data) => {
|
||||
DistroManager.pullLocal().then((data) => {
|
||||
console.log('%c[Preloader]', 'color: #a02d2a; font-weight: bold', 'Successfully loaded an older version of the distribution index.')
|
||||
|
||||
onDistroLoad(data)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
const AdmZip = require('adm-zip')
|
||||
const {AssetGuard, Library} = require('./assetguard.js')
|
||||
const child_process = require('child_process')
|
||||
const ConfigManager = require('./configmanager.js')
|
||||
const crypto = require('crypto')
|
||||
const fs = require('fs')
|
||||
const mkpath = require('mkdirp')
|
||||
@@ -10,10 +8,14 @@ const path = require('path')
|
||||
const rimraf = require('rimraf')
|
||||
const {URL} = require('url')
|
||||
|
||||
const { Library } = require('./assetguard')
|
||||
const ConfigManager = require('./configmanager')
|
||||
const DistroManager = require('./distromanager')
|
||||
|
||||
class ProcessBuilder {
|
||||
|
||||
constructor(distroServer, versionData, forgeData, authUser){
|
||||
this.gameDir = path.join(ConfigManager.getInstanceDirectory(), distroServer.id)
|
||||
this.gameDir = path.join(ConfigManager.getInstanceDirectory(), distroServer.getID())
|
||||
this.commonDir = ConfigManager.getCommonDirectory()
|
||||
this.server = distroServer
|
||||
this.versionData = versionData
|
||||
@@ -35,7 +37,9 @@ class ProcessBuilder {
|
||||
const tempNativePath = path.join(os.tmpdir(), ConfigManager.getTempNativeFolder(), crypto.pseudoRandomBytes(16).toString('hex'))
|
||||
process.throwDeprecation = true
|
||||
this.setupLiteLoader()
|
||||
const modObj = this.resolveModConfiguration(ConfigManager.getModConfiguration(this.server.id).mods, this.server.modules)
|
||||
console.log('using liteloader', this.usingLiteLoader)
|
||||
const modObj = this.resolveModConfiguration(ConfigManager.getModConfiguration(this.server.getID()).mods, this.server.getModules())
|
||||
console.log(modObj)
|
||||
this.constructModList('forge', modObj.fMods, true)
|
||||
if(this.usingLiteLoader){
|
||||
this.constructModList('liteloader', modObj.lMods, true)
|
||||
@@ -92,20 +96,7 @@ class ProcessBuilder {
|
||||
* @returns {boolean} True if the mod is enabled, false otherwise.
|
||||
*/
|
||||
static isModEnabled(modCfg, required = null){
|
||||
return modCfg != null ? ((typeof modCfg === 'boolean' && modCfg) || (typeof modCfg === 'object' && modCfg.value)) : required != null && required.def != null ? required.def : true
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a mod is optional.
|
||||
*
|
||||
* A mod is optional if its required object is not null and its 'value'
|
||||
* property is false.
|
||||
*
|
||||
* @param {Object} mdl The mod distro module.
|
||||
* @returns {boolean} True if the mod is optional, otherwise false.
|
||||
*/
|
||||
static isModOptional(mdl){
|
||||
return mdl.required != null && mdl.required.value != null && mdl.required.value === false
|
||||
return modCfg != null ? ((typeof modCfg === 'boolean' && modCfg) || (typeof modCfg === 'object' && modCfg.value)) : required != null ? required.isDefault() : true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,19 +106,21 @@ class ProcessBuilder {
|
||||
* mod. It must not be declared as a submodule.
|
||||
*/
|
||||
setupLiteLoader(){
|
||||
const mdls = this.server.modules
|
||||
for(let i=0; i<mdls.length; i++){
|
||||
if(mdls[i].type === 'liteloader'){
|
||||
const ll = mdls[i]
|
||||
if(ProcessBuilder.isModOptional(ll)){
|
||||
const modCfg = ConfigManager.getModConfiguration(this.server.id).mods
|
||||
if(ProcessBuilder.isModEnabled(modCfg[AssetGuard._resolveWithoutVersion(ll.id)], ll.required)){
|
||||
this.usingLiteLoader = true
|
||||
this.llPath = path.join(this.libPath, ll.artifact.path == null ? AssetGuard._resolvePath(ll.id, ll.artifact.extension) : ll.artifact.path)
|
||||
for(let ll of this.server.getModules()){
|
||||
if(ll.getType() === DistroManager.Types.LiteLoader){
|
||||
if(!ll.getRequired().isRequired()){
|
||||
const modCfg = ConfigManager.getModConfiguration(this.server.getID()).mods
|
||||
if(ProcessBuilder.isModEnabled(modCfg[ll.getVersionlessID()], ll.getRequired())){
|
||||
if(fs.existsSync(ll.getArtifact().getPath())){
|
||||
this.usingLiteLoader = true
|
||||
this.llPath = ll.getArtifact().getPath()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.usingLiteLoader = true
|
||||
this.llPath = path.join(this.libPath, ll.artifact.path == null ? AssetGuard._resolvePath(ll.id, ll.artifact.extension) : ll.artifact.path)
|
||||
if(fs.existsSync(ll.getArtifact().getPath())){
|
||||
this.usingLiteLoader = true
|
||||
this.llPath = ll.getArtifact().getPath()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,21 +139,21 @@ class ProcessBuilder {
|
||||
let fMods = []
|
||||
let lMods = []
|
||||
|
||||
for(let i=0; i<mdls.length; i++){
|
||||
const mdl = mdls[i]
|
||||
if(mdl.type != null && (mdl.type === 'forgemod' || mdl.type === 'litemod' || mdl.type === 'liteloader')){
|
||||
const o = ProcessBuilder.isModOptional(mdl)
|
||||
const e = ProcessBuilder.isModEnabled(modCfg[AssetGuard._resolveWithoutVersion(mdl.id)], mdl.required)
|
||||
for(let mdl of mdls){
|
||||
const type = mdl.getType()
|
||||
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
|
||||
const o = !mdl.getRequired().isRequired()
|
||||
const e = ProcessBuilder.isModEnabled(modCfg[mdl.getVersionlessID()], mdl.getRequired())
|
||||
if(!o || (o && e)){
|
||||
if(mdl.sub_modules != null){
|
||||
const v = this.resolveModConfiguration(modCfg[AssetGuard._resolveWithoutVersion(mdl.id)].mods, mdl.sub_modules)
|
||||
if(mdl.hasSubModules()){
|
||||
const v = this.resolveModConfiguration(modCfg[mdl.getVersionlessID()].mods, mdl.getSubModules())
|
||||
fMods = fMods.concat(v.fMods)
|
||||
lMods = lMods.concat(v.lMods)
|
||||
if(mdl.type === 'liteloader'){
|
||||
if(mdl.type === DistroManager.Types.LiteLoader){
|
||||
continue
|
||||
}
|
||||
}
|
||||
if(mdl.type === 'forgemod'){
|
||||
if(mdl.type === DistroManager.Types.ForgeMod){
|
||||
fMods.push(mdl)
|
||||
} else {
|
||||
lMods.push(mdl)
|
||||
@@ -186,15 +179,15 @@ class ProcessBuilder {
|
||||
const modList = {
|
||||
repositoryRoot: path.join(this.commonDir, 'modstore')
|
||||
}
|
||||
|
||||
|
||||
const ids = []
|
||||
if(type === 'forge'){
|
||||
for(let i=0; i<mods.length; ++i){
|
||||
ids.push(mods[i].id)
|
||||
for(let mod of mods){
|
||||
ids.push(mod.getIdentifier())
|
||||
}
|
||||
} else {
|
||||
for(let i=0; i<mods.length; ++i){
|
||||
ids.push(mods[i].id + '@' + (mods[i].artifact.extension != null ? mods[i].artifact.extension.substring(1) : 'jar'))
|
||||
for(let mod of mods){
|
||||
ids.push(mod.getIdentifier() + '@' + mod.getExtension())
|
||||
}
|
||||
}
|
||||
modList.modRef = ids
|
||||
@@ -255,7 +248,7 @@ class ProcessBuilder {
|
||||
break
|
||||
case 'version_name':
|
||||
//val = versionData.id
|
||||
val = this.server.id
|
||||
val = this.server.getID()
|
||||
break
|
||||
case 'game_directory':
|
||||
val = this.gameDir
|
||||
@@ -306,8 +299,8 @@ class ProcessBuilder {
|
||||
}
|
||||
|
||||
// Prepare autoconnect
|
||||
if(ConfigManager.getAutoConnect() && this.server.autoconnect){
|
||||
const serverURL = new URL('my://' + this.server.server_ip)
|
||||
if(ConfigManager.getAutoConnect() && this.server.isAutoConnect()){
|
||||
const serverURL = new URL('my://' + this.server.getAddress())
|
||||
mcArgs.unshift(serverURL.hostname)
|
||||
mcArgs.unshift('--server')
|
||||
if(serverURL.port){
|
||||
@@ -428,16 +421,16 @@ class ProcessBuilder {
|
||||
* @returns {Array.<string>} An array containing the paths of each library this server requires.
|
||||
*/
|
||||
_resolveServerLibraries(mods){
|
||||
const mdles = this.server.modules
|
||||
const mdls = this.server.getModules()
|
||||
let libs = []
|
||||
|
||||
// Locate Forge/Libraries
|
||||
for(let i=0; i<mdles.length; i++){
|
||||
if(mdles[i].type != null && (mdles[i].type === 'forge-hosted' || mdles[i].type === 'library')){
|
||||
let lib = mdles[i]
|
||||
libs.push(path.join(this.libPath, lib.artifact.path == null ? AssetGuard._resolvePath(lib.id, lib.artifact.extension) : lib.artifact.path))
|
||||
if(lib.sub_modules != null){
|
||||
const res = this._resolveModuleLibraries(lib)
|
||||
for(let mdl of mdls){
|
||||
const type = mdl.getType()
|
||||
if(type === DistroManager.Types.ForgeHosted || type === DistroManager.Types.Library){
|
||||
libs.push(mdl.getArtifact().getPath())
|
||||
if(mdl.hasSubModules()){
|
||||
const res = this._resolveModuleLibraries(mdl)
|
||||
if(res.length > 0){
|
||||
libs = libs.concat(res)
|
||||
}
|
||||
@@ -461,22 +454,21 @@ class ProcessBuilder {
|
||||
/**
|
||||
* Recursively resolve the path of each library required by this module.
|
||||
*
|
||||
* @param {Object} mdle A module object from the server distro index.
|
||||
* @param {Object} mdl 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){
|
||||
_resolveModuleLibraries(mdl){
|
||||
if(!mdl.hasSubModules()){
|
||||
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 ? AssetGuard._resolvePath(sm.id, sm.artifact.extension) : sm.artifact.path))
|
||||
for(let sm of mdl.getSubModules()){
|
||||
if(sm.getType() === DistroManager.Types.Library){
|
||||
libs.push(sm.getArtifact().getPath())
|
||||
}
|
||||
// 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){
|
||||
if(mdl.hasSubModules()){
|
||||
const res = this._resolveModuleLibraries(sm)
|
||||
if(res.length > 0){
|
||||
libs = libs.concat(res)
|
||||
|
||||
@@ -7,10 +7,10 @@ const crypto = require('crypto')
|
||||
const {URL} = require('url')
|
||||
|
||||
// Internal Requirements
|
||||
const DiscordWrapper = require('./assets/js/discordwrapper.js')
|
||||
const Mojang = require('./assets/js/mojang.js')
|
||||
const ProcessBuilder = require('./assets/js/processbuilder.js')
|
||||
const ServerStatus = require('./assets/js/serverstatus.js')
|
||||
const DiscordWrapper = require('./assets/js/discordwrapper')
|
||||
const Mojang = require('./assets/js/mojang')
|
||||
const ProcessBuilder = require('./assets/js/processbuilder')
|
||||
const ServerStatus = require('./assets/js/serverstatus')
|
||||
|
||||
// Launch Elements
|
||||
const launch_content = document.getElementById('launch_content')
|
||||
@@ -208,13 +208,13 @@ const refreshMojangStatuses = async function(){
|
||||
|
||||
const refreshServerStatus = async function(fade = false){
|
||||
console.log('Refreshing Server Status')
|
||||
const serv = AssetGuard.getServerById(ConfigManager.getSelectedServer())
|
||||
const serv = DistroManager.getDistribution().getServer(ConfigManager.getSelectedServer())
|
||||
|
||||
let pLabel = 'SERVER'
|
||||
let pVal = 'OFFLINE'
|
||||
|
||||
try {
|
||||
const serverURL = new URL('my://' + serv.server_ip)
|
||||
const serverURL = new URL('my://' + serv.getAddress())
|
||||
const servStat = await ServerStatus.getStatus(serverURL.hostname, serverURL.port)
|
||||
if(servStat.online){
|
||||
pLabel = 'PLAYERS'
|
||||
@@ -261,9 +261,7 @@ function asyncSystemScan(launchAfter = true){
|
||||
// Fork a process to run validations.
|
||||
sysAEx = cp.fork(path.join(__dirname, 'assets', 'js', 'assetexec.js'), [
|
||||
ConfigManager.getCommonDirectory(),
|
||||
ConfigManager.getLauncherDirectory(),
|
||||
ConfigManager.getJavaExecutable(),
|
||||
ConfigManager.getInstanceDirectory()
|
||||
ConfigManager.getJavaExecutable()
|
||||
], {
|
||||
stdio: 'pipe'
|
||||
})
|
||||
@@ -277,8 +275,8 @@ function asyncSystemScan(launchAfter = true){
|
||||
})
|
||||
|
||||
sysAEx.on('message', (m) => {
|
||||
if(m.content === 'validateJava'){
|
||||
|
||||
if(m.context === 'validateJava'){
|
||||
if(m.result == null){
|
||||
// If the result is null, no valid Java installation was found.
|
||||
// Show this information to the user.
|
||||
@@ -290,7 +288,7 @@ function asyncSystemScan(launchAfter = true){
|
||||
)
|
||||
setOverlayHandler(() => {
|
||||
setLaunchDetails('Preparing Java Download..')
|
||||
sysAEx.send({task: 0, content: '_enqueueOracleJRE', argsArr: [ConfigManager.getLauncherDirectory()]})
|
||||
sysAEx.send({task: 'execute', function: '_enqueueOracleJRE', argsArr: [ConfigManager.getLauncherDirectory()]})
|
||||
toggleOverlay(false)
|
||||
})
|
||||
setDismissHandler(() => {
|
||||
@@ -330,14 +328,13 @@ function asyncSystemScan(launchAfter = true){
|
||||
}
|
||||
sysAEx.disconnect()
|
||||
}
|
||||
|
||||
} else if(m.content === '_enqueueOracleJRE'){
|
||||
} else if(m.context === '_enqueueOracleJRE'){
|
||||
|
||||
if(m.result === true){
|
||||
|
||||
// Oracle JRE enqueued successfully, begin download.
|
||||
setLaunchDetails('Downloading Java..')
|
||||
sysAEx.send({task: 0, content: 'processDlQueues', argsArr: [[{id:'java', limit:1}]]})
|
||||
sysAEx.send({task: 'execute', function: 'processDlQueues', argsArr: [[{id:'java', limit:1}]]})
|
||||
|
||||
} else {
|
||||
|
||||
@@ -357,58 +354,64 @@ function asyncSystemScan(launchAfter = true){
|
||||
|
||||
}
|
||||
|
||||
} else if(m.content === 'dl'){
|
||||
} else if(m.context === 'progress'){
|
||||
|
||||
if(m.task === 0){
|
||||
// Downloading..
|
||||
setDownloadPercentage(m.value, m.total, m.percent)
|
||||
} else if(m.task === 1){
|
||||
// Show installing progress bar.
|
||||
remote.getCurrentWindow().setProgressBar(2)
|
||||
|
||||
// Wait for extration to complete.
|
||||
const eLStr = 'Extracting'
|
||||
let dotStr = ''
|
||||
setLaunchDetails(eLStr)
|
||||
extractListener = setInterval(() => {
|
||||
if(dotStr.length >= 3){
|
||||
dotStr = ''
|
||||
} else {
|
||||
dotStr += '.'
|
||||
}
|
||||
setLaunchDetails(eLStr + dotStr)
|
||||
}, 750)
|
||||
|
||||
} else if(m.task === 2){
|
||||
|
||||
// Download & extraction complete, remove the loading from the OS progress bar.
|
||||
remote.getCurrentWindow().setProgressBar(-1)
|
||||
|
||||
// Extraction completed successfully.
|
||||
ConfigManager.setJavaExecutable(m.jPath)
|
||||
ConfigManager.save()
|
||||
|
||||
if(extractListener != null){
|
||||
clearInterval(extractListener)
|
||||
extractListener = null
|
||||
}
|
||||
|
||||
setLaunchDetails('Java Installed!')
|
||||
|
||||
if(launchAfter){
|
||||
dlAsync()
|
||||
}
|
||||
|
||||
sysAEx.disconnect()
|
||||
} else {
|
||||
console.error('Unknown download data type.', m)
|
||||
switch(m.data){
|
||||
case 'download':
|
||||
// Downloading..
|
||||
setDownloadPercentage(m.value, m.total, m.percent)
|
||||
break
|
||||
}
|
||||
|
||||
} else if(m.context === 'complete'){
|
||||
|
||||
switch(m.data){
|
||||
case 'download':
|
||||
// Show installing progress bar.
|
||||
remote.getCurrentWindow().setProgressBar(2)
|
||||
|
||||
// Wait for extration to complete.
|
||||
const eLStr = 'Extracting'
|
||||
let dotStr = ''
|
||||
setLaunchDetails(eLStr)
|
||||
extractListener = setInterval(() => {
|
||||
if(dotStr.length >= 3){
|
||||
dotStr = ''
|
||||
} else {
|
||||
dotStr += '.'
|
||||
}
|
||||
setLaunchDetails(eLStr + dotStr)
|
||||
}, 750)
|
||||
break
|
||||
case 'java':
|
||||
// Download & extraction complete, remove the loading from the OS progress bar.
|
||||
remote.getCurrentWindow().setProgressBar(-1)
|
||||
|
||||
// Extraction completed successfully.
|
||||
ConfigManager.setJavaExecutable(m.args[0])
|
||||
ConfigManager.save()
|
||||
|
||||
if(extractListener != null){
|
||||
clearInterval(extractListener)
|
||||
extractListener = null
|
||||
}
|
||||
|
||||
setLaunchDetails('Java Installed!')
|
||||
|
||||
if(launchAfter){
|
||||
dlAsync()
|
||||
}
|
||||
|
||||
sysAEx.disconnect()
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// Begin system Java scan.
|
||||
setLaunchDetails('Checking system info..')
|
||||
sysAEx.send({task: 0, content: 'validateJava', argsArr: [ConfigManager.getLauncherDirectory()]})
|
||||
sysAEx.send({task: 'execute', function: 'validateJava', argsArr: [ConfigManager.getLauncherDirectory()]})
|
||||
|
||||
}
|
||||
|
||||
@@ -448,9 +451,7 @@ function dlAsync(login = true){
|
||||
// Start AssetExec to run validations and downloads in a forked process.
|
||||
aEx = cp.fork(path.join(__dirname, 'assets', 'js', 'assetexec.js'), [
|
||||
ConfigManager.getCommonDirectory(),
|
||||
ConfigManager.getLauncherDirectory(),
|
||||
ConfigManager.getJavaExecutable(),
|
||||
ConfigManager.getInstanceDirectory()
|
||||
ConfigManager.getJavaExecutable()
|
||||
], {
|
||||
stdio: 'pipe'
|
||||
})
|
||||
@@ -465,136 +466,110 @@ function dlAsync(login = true){
|
||||
|
||||
// Establish communications between the AssetExec and current process.
|
||||
aEx.on('message', (m) => {
|
||||
if(m.content === 'validateDistribution'){
|
||||
|
||||
setLaunchPercentage(20, 100)
|
||||
serv = m.result
|
||||
console.log('Validated distibution index.')
|
||||
|
||||
// Begin version load.
|
||||
setLaunchDetails('Loading version information..')
|
||||
aEx.send({task: 0, content: 'loadVersionData', argsArr: [serv.mc_version]})
|
||||
|
||||
} else if(m.content === 'loadVersionData'){
|
||||
|
||||
setLaunchPercentage(40, 100)
|
||||
versionData = m.result
|
||||
console.log('Version data loaded.')
|
||||
|
||||
// Begin asset validation.
|
||||
setLaunchDetails('Validating asset integrity..')
|
||||
aEx.send({task: 0, content: 'validateAssets', argsArr: [versionData]})
|
||||
|
||||
} else if(m.content === 'validateAssets'){
|
||||
|
||||
// Asset validation can *potentially* take longer, so let's track progress.
|
||||
if(m.task === 0){
|
||||
const perc = (m.value/m.total)*20
|
||||
setLaunchPercentage(40+perc, 100, parseInt(40+perc))
|
||||
} else {
|
||||
setLaunchPercentage(60, 100)
|
||||
console.log('Asset Validation Complete')
|
||||
|
||||
// Begin library validation.
|
||||
setLaunchDetails('Validating library integrity..')
|
||||
aEx.send({task: 0, content: 'validateLibraries', argsArr: [versionData]})
|
||||
if(m.context === 'validate'){
|
||||
switch(m.data){
|
||||
case 'distribution':
|
||||
setLaunchPercentage(20, 100)
|
||||
console.log('Validated distibution index.')
|
||||
setLaunchDetails('Loading version information..')
|
||||
break
|
||||
case 'version':
|
||||
setLaunchPercentage(40, 100)
|
||||
console.log('Version data loaded.')
|
||||
setLaunchDetails('Validating asset integrity..')
|
||||
break
|
||||
case 'assets':
|
||||
setLaunchPercentage(60, 100)
|
||||
console.log('Asset Validation Complete')
|
||||
setLaunchDetails('Validating library integrity..')
|
||||
break
|
||||
case 'libraries':
|
||||
setLaunchPercentage(80, 100)
|
||||
console.log('Library validation complete.')
|
||||
setLaunchDetails('Validating miscellaneous file integrity..')
|
||||
break
|
||||
case 'files':
|
||||
setLaunchPercentage(100, 100)
|
||||
console.log('File validation complete.')
|
||||
setLaunchDetails('Downloading files..')
|
||||
break
|
||||
}
|
||||
} else if(m.context === 'progress'){
|
||||
switch(m.data){
|
||||
case 'assets':
|
||||
const perc = (m.value/m.total)*20
|
||||
setLaunchPercentage(40+perc, 100, parseInt(40+perc))
|
||||
break
|
||||
case 'download':
|
||||
setDownloadPercentage(m.value, m.total, m.percent)
|
||||
break
|
||||
case 'extract':
|
||||
// Show installing progress bar.
|
||||
remote.getCurrentWindow().setProgressBar(2)
|
||||
|
||||
} else if(m.content === 'validateLibraries'){
|
||||
|
||||
setLaunchPercentage(80, 100)
|
||||
console.log('Library validation complete.')
|
||||
|
||||
// Begin miscellaneous validation.
|
||||
setLaunchDetails('Validating miscellaneous file integrity..')
|
||||
aEx.send({task: 0, content: 'validateMiscellaneous', argsArr: [versionData]})
|
||||
|
||||
} else if(m.content === 'validateMiscellaneous'){
|
||||
|
||||
setLaunchPercentage(100, 100)
|
||||
console.log('File validation complete.')
|
||||
|
||||
// Download queued files.
|
||||
setLaunchDetails('Downloading files..')
|
||||
aEx.send({task: 0, content: 'processDlQueues'})
|
||||
|
||||
} else if(m.content === 'dl'){
|
||||
|
||||
if(m.task === 0){
|
||||
|
||||
setDownloadPercentage(m.value, m.total, m.percent)
|
||||
|
||||
} else if(m.task === 0.7){
|
||||
|
||||
// Show installing progress bar.
|
||||
remote.getCurrentWindow().setProgressBar(2)
|
||||
|
||||
// Download done, extracting.
|
||||
const eLStr = 'Extracting libraries'
|
||||
let dotStr = ''
|
||||
setLaunchDetails(eLStr)
|
||||
progressListener = setInterval(() => {
|
||||
if(dotStr.length >= 3){
|
||||
dotStr = ''
|
||||
} else {
|
||||
dotStr += '.'
|
||||
// Download done, extracting.
|
||||
const eLStr = 'Extracting libraries'
|
||||
let dotStr = ''
|
||||
setLaunchDetails(eLStr)
|
||||
progressListener = setInterval(() => {
|
||||
if(dotStr.length >= 3){
|
||||
dotStr = ''
|
||||
} else {
|
||||
dotStr += '.'
|
||||
}
|
||||
setLaunchDetails(eLStr + dotStr)
|
||||
}, 750)
|
||||
break
|
||||
}
|
||||
} else if(m.context === 'complete'){
|
||||
switch(m.data){
|
||||
case 'download':
|
||||
// Download and extraction complete, remove the loading from the OS progress bar.
|
||||
remote.getCurrentWindow().setProgressBar(-1)
|
||||
if(progressListener != null){
|
||||
clearInterval(progressListener)
|
||||
progressListener = null
|
||||
}
|
||||
setLaunchDetails(eLStr + dotStr)
|
||||
}, 750)
|
||||
|
||||
} else if(m.task === 0.9) {
|
||||
|
||||
console.error(m.err)
|
||||
|
||||
if(m.err.code === 'ENOENT'){
|
||||
setOverlayContent(
|
||||
'Download Error',
|
||||
'Could not connect to the file server. Ensure that you are connected to the internet and try again.',
|
||||
'Okay'
|
||||
)
|
||||
setOverlayHandler(null)
|
||||
} else {
|
||||
setOverlayContent(
|
||||
'Download Error',
|
||||
'Check the console for more details. Please try again.',
|
||||
'Okay'
|
||||
)
|
||||
setOverlayHandler(null)
|
||||
}
|
||||
|
||||
remote.getCurrentWindow().setProgressBar(-1)
|
||||
toggleOverlay(true)
|
||||
toggleLaunchArea(false)
|
||||
|
||||
// Disconnect from AssetExec
|
||||
aEx.disconnect()
|
||||
|
||||
} else if(m.task === 1){
|
||||
|
||||
// Download and extraction complete, remove the loading from the OS progress bar.
|
||||
remote.getCurrentWindow().setProgressBar(-1)
|
||||
if(progressListener != null){
|
||||
clearInterval(progressListener)
|
||||
progressListener = null
|
||||
}
|
||||
|
||||
setLaunchDetails('Preparing to launch..')
|
||||
aEx.send({task: 0, content: 'loadForgeData', argsArr: [serv.id]})
|
||||
|
||||
} else {
|
||||
|
||||
console.error('Unknown download data type.', m)
|
||||
|
||||
setLaunchDetails('Preparing to launch..')
|
||||
break
|
||||
}
|
||||
} else if(m.context === 'error'){
|
||||
switch(m.data){
|
||||
case 'download':
|
||||
console.error(m.error)
|
||||
|
||||
if(m.error.code === 'ENOENT'){
|
||||
setOverlayContent(
|
||||
'Download Error',
|
||||
'Could not connect to the file server. Ensure that you are connected to the internet and try again.',
|
||||
'Okay'
|
||||
)
|
||||
setOverlayHandler(null)
|
||||
} else {
|
||||
setOverlayContent(
|
||||
'Download Error',
|
||||
'Check the console for more details. Please try again.',
|
||||
'Okay'
|
||||
)
|
||||
setOverlayHandler(null)
|
||||
}
|
||||
|
||||
} else if(m.content === 'loadForgeData'){
|
||||
remote.getCurrentWindow().setProgressBar(-1)
|
||||
toggleOverlay(true)
|
||||
toggleLaunchArea(false)
|
||||
|
||||
forgeData = m.result
|
||||
// Disconnect from AssetExec
|
||||
aEx.disconnect()
|
||||
break
|
||||
}
|
||||
} else if(m.context === 'validateEverything'){
|
||||
|
||||
forgeData = m.result.forgeData
|
||||
versionData = m.result.versionData
|
||||
|
||||
if(login) {
|
||||
//if(!(await AuthManager.validateSelected())){
|
||||
//
|
||||
//}
|
||||
const authUser = ConfigManager.getSelectedAccount()
|
||||
console.log('authu', authUser)
|
||||
let pb = new ProcessBuilder(serv, versionData, forgeData, authUser)
|
||||
@@ -623,7 +598,7 @@ function dlAsync(login = true){
|
||||
if(servJoined.test(data)){
|
||||
DiscordWrapper.updateDetails('Exploring the Realm!')
|
||||
} else if(gameJoined.test(data)){
|
||||
DiscordWrapper.updateDetails('Idling on Main Menu')
|
||||
DiscordWrapper.updateDetails('Sailing to Westeros!')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,7 +607,7 @@ function dlAsync(login = true){
|
||||
proc.stdout.on('data', gameStateChange)
|
||||
|
||||
// Init Discord Hook
|
||||
const distro = AssetGuard.getDistributionData()
|
||||
const distro = DistroManager.getDistribution()
|
||||
if(distro.discord != null && serv.discord != null){
|
||||
DiscordWrapper.initRPC(distro.discord, serv.discord)
|
||||
hasRPC = true
|
||||
@@ -670,14 +645,19 @@ function dlAsync(login = true){
|
||||
// Validate Forge files.
|
||||
setLaunchDetails('Loading server information..')
|
||||
|
||||
if(AssetGuard.isLocalLaunch()){
|
||||
|
||||
refreshDistributionIndex(true, (data) => {
|
||||
onDistroRefresh(data)
|
||||
serv = data.getServer(ConfigManager.getSelectedServer())
|
||||
aEx.send({task: 'execute', function: 'validateEverything', argsArr: [ConfigManager.getSelectedServer(), DistroManager.isDevMode()]})
|
||||
}, (err) => {
|
||||
console.log(err)
|
||||
refreshDistributionIndex(false, (data) => {
|
||||
onDistroRefresh(data)
|
||||
aEx.send({task: 0, content: 'validateDistribution', argsArr: [ConfigManager.getSelectedServer()]})
|
||||
serv = data.getServer(ConfigManager.getSelectedServer())
|
||||
aEx.send({task: 'execute', function: 'validateEverything', argsArr: [ConfigManager.getSelectedServer(), DistroManager.isDevMode()]})
|
||||
}, (err) => {
|
||||
console.error('Unable to refresh distribution index.', err)
|
||||
if(AssetGuard.getDistributionData() == null){
|
||||
if(DistroManager.getDistribution() == null){
|
||||
setOverlayContent(
|
||||
'Fatal Error',
|
||||
'Could not load a copy of the distribution index. See the console for more details.',
|
||||
@@ -691,40 +671,11 @@ function dlAsync(login = true){
|
||||
// Disconnect from AssetExec
|
||||
aEx.disconnect()
|
||||
} else {
|
||||
aEx.send({task: 0, content: 'validateDistribution', argsArr: [ConfigManager.getSelectedServer()]})
|
||||
serv = data.getServer(ConfigManager.getSelectedServer())
|
||||
aEx.send({task: 'execute', function: 'validateEverything', argsArr: [ConfigManager.getSelectedServer(), DistroManager.isDevMode()]})
|
||||
}
|
||||
})
|
||||
|
||||
} else {
|
||||
|
||||
refreshDistributionIndex(true, (data) => {
|
||||
onDistroRefresh(data)
|
||||
aEx.send({task: 0, content: 'validateDistribution', argsArr: [ConfigManager.getSelectedServer()]})
|
||||
}, (err) => {
|
||||
refreshDistributionIndex(false, (data) => {
|
||||
onDistroRefresh(data)
|
||||
}, (err) => {
|
||||
console.error('Unable to refresh distribution index.', err)
|
||||
if(AssetGuard.getDistributionData() == null){
|
||||
setOverlayContent(
|
||||
'Fatal Error',
|
||||
'Could not load a copy of the distribution index. See the console for more details.',
|
||||
'Okay'
|
||||
)
|
||||
setOverlayHandler(null)
|
||||
|
||||
toggleOverlay(true)
|
||||
toggleLaunchArea(false)
|
||||
|
||||
// Disconnect from AssetExec
|
||||
aEx.disconnect()
|
||||
} else {
|
||||
aEx.send({task: 0, content: 'validateDistribution', argsArr: [ConfigManager.getSelectedServer()]})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1046,8 +997,8 @@ function displayArticle(articleObject, index){
|
||||
*/
|
||||
function loadNews(){
|
||||
return new Promise((resolve, reject) => {
|
||||
const distroData = AssetGuard.getDistributionData()
|
||||
const newsFeed = distroData['news_feed']
|
||||
const distroData = DistroManager.getDistribution()
|
||||
const newsFeed = distroData.getRSS()
|
||||
const newsHost = new URL(newsFeed).origin + '/'
|
||||
$.ajax(
|
||||
{
|
||||
|
||||
@@ -138,10 +138,10 @@ document.getElementById('serverSelectConfirm').addEventListener('click', () => {
|
||||
const listings = document.getElementsByClassName('serverListing')
|
||||
for(let i=0; i<listings.length; i++){
|
||||
if(listings[i].hasAttribute('selected')){
|
||||
const serv = AssetGuard.getServerById(listings[i].getAttribute('servid'))
|
||||
ConfigManager.setSelectedServer(serv != null ? serv.id : null)
|
||||
const serv = DistroManager.getDistribution().getServer(listings[i].getAttribute('servid'))
|
||||
ConfigManager.setSelectedServer(serv != null ? serv.getID() : null)
|
||||
ConfigManager.save()
|
||||
updateSelectedServer(serv != null ? serv.name : null)
|
||||
updateSelectedServer(serv != null ? serv.getName() : null)
|
||||
setLaunchEnabled(serv != null)
|
||||
refreshServerStatus(true)
|
||||
toggleOverlay(false)
|
||||
@@ -229,20 +229,20 @@ function setAccountListingHandlers(){
|
||||
}
|
||||
|
||||
function populateServerListings(){
|
||||
const distro = AssetGuard.getDistributionData()
|
||||
const distro = DistroManager.getDistribution()
|
||||
const giaSel = ConfigManager.getSelectedServer()
|
||||
const servers = distro.servers
|
||||
const servers = distro.getServers()
|
||||
let htmlString = ``
|
||||
for(let i=0; i<servers.length; i++){
|
||||
htmlString += `<button class="serverListing" servid="${servers[i].id}" ${servers[i].id === giaSel ? `selected` : ``}>
|
||||
<img class="serverListingImg" src="${servers[i].icon_url}"/>
|
||||
for(const serv of servers){
|
||||
htmlString += `<button class="serverListing" servid="${serv.getID()}" ${serv.getID() === giaSel ? `selected` : ``}>
|
||||
<img class="serverListingImg" src="${serv.getIcon()}"/>
|
||||
<div class="serverListingDetails">
|
||||
<span class="serverListingName">${servers[i].name}</span>
|
||||
<span class="serverListingDescription">${servers[i].description}</span>
|
||||
<span class="serverListingName">${serv.getName()}</span>
|
||||
<span class="serverListingDescription">${serv.getDescription()}</span>
|
||||
<div class="serverListingInfo">
|
||||
<div class="serverListingVersion">${servers[i].mc_version}</div>
|
||||
<div class="serverListingRevision">${servers[i].revision}</div>
|
||||
${servers[i].default_selected ? `<div class="serverListingStarWrapper">
|
||||
<div class="serverListingVersion">${serv.getMinecraftVersion()}</div>
|
||||
<div class="serverListingRevision">${serv.getVersion()}</div>
|
||||
${serv.isMainServer() ? `<div class="serverListingStarWrapper">
|
||||
<svg id="Layer_1" viewBox="0 0 107.45 104.74" width="20px" height="20px">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#fff;}.cls-2{fill:none;stroke:#fff;stroke-miterlimit:10;}</style>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
const os = require('os')
|
||||
const semver = require('semver')
|
||||
|
||||
const { AssetGuard } = require('./assets/js/assetguard')
|
||||
|
||||
const settingsState = {
|
||||
invalid: new Set()
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
*/
|
||||
// Requirements
|
||||
const path = require('path')
|
||||
const AuthManager = require('./assets/js/authmanager.js')
|
||||
const {AssetGuard} = require('./assets/js/assetguard.js')
|
||||
const ConfigManager = require('./assets/js/configmanager.js')
|
||||
|
||||
const AuthManager = require('./assets/js/authmanager')
|
||||
const ConfigManager = require('./assets/js/configmanager')
|
||||
const DistroManager = require('./assets/js/distromanager')
|
||||
|
||||
let rscShouldLoad = false
|
||||
let fatalStartupError = false
|
||||
@@ -53,14 +54,14 @@ function getCurrentView(){
|
||||
return currentView
|
||||
}
|
||||
|
||||
function showMainUI(){
|
||||
function showMainUI(data){
|
||||
|
||||
if(!isDev){
|
||||
console.log('%c[AutoUpdater]', 'color: #a02d2a; font-weight: bold', 'Initializing..')
|
||||
ipcRenderer.send('autoUpdateAction', 'initAutoUpdater', ConfigManager.getAllowPrerelease())
|
||||
}
|
||||
|
||||
updateSelectedServer(AssetGuard.getServerById(ConfigManager.getSelectedServer()).name)
|
||||
updateSelectedServer(data.getServer(ConfigManager.getSelectedServer()).getName())
|
||||
refreshServerStatus()
|
||||
setTimeout(() => {
|
||||
document.getElementById('frameBar').style.backgroundColor = 'rgba(0, 0, 0, 0.5)'
|
||||
@@ -131,7 +132,7 @@ function showFatalStartupError(){
|
||||
* @param {Object} data The distro index object.
|
||||
*/
|
||||
function onDistroRefresh(data){
|
||||
updateSelectedServer(AssetGuard.getServerById(ConfigManager.getSelectedServer()).name)
|
||||
updateSelectedServer(data.getServer(ConfigManager.getSelectedServer()).getName())
|
||||
refreshServerStatus()
|
||||
initNews()
|
||||
syncModConfigurations(data)
|
||||
@@ -146,28 +147,27 @@ function syncModConfigurations(data){
|
||||
|
||||
const syncedCfgs = []
|
||||
|
||||
const servers = data.servers
|
||||
for(let serv of data.getServers()){
|
||||
|
||||
for(let i=0; i<servers.length; i++){
|
||||
|
||||
const id = servers[i].id
|
||||
const mdls = servers[i].modules
|
||||
const cfg = ConfigManager.getModConfiguration(servers[i].id)
|
||||
const id = serv.getID()
|
||||
const mdls = serv.getModules()
|
||||
const cfg = ConfigManager.getModConfiguration(id)
|
||||
|
||||
if(cfg != null){
|
||||
|
||||
const modsOld = cfg.mods
|
||||
const mods = {}
|
||||
|
||||
for(let j=0; j<mdls.length; j++){
|
||||
const mdl = mdls[j]
|
||||
if(mdl.type === 'forgemod' || mdl.type === 'litemod' || mdl.type === 'liteloader'){
|
||||
if(mdl.required != null && mdl.required.value != null && mdl.required.value === false){
|
||||
const mdlID = AssetGuard._resolveWithoutVersion(mdl.id)
|
||||
for(let mdl of mdls){
|
||||
const type = mdl.getType()
|
||||
|
||||
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
|
||||
if(!mdl.getRequired().isRequired()){
|
||||
const mdlID = mdl.getVersionlessID()
|
||||
if(modsOld[mdlID] == null){
|
||||
mods[mdlID] = scanOptionalSubModules(mdl.sub_modules, mdl)
|
||||
mods[mdlID] = scanOptionalSubModules(mdl.getSubModules(), mdl)
|
||||
} else {
|
||||
mods[mdlID] = mergeModConfiguration(modsOld[mdlID], scanOptionalSubModules(mdl.sub_modules, mdl))
|
||||
mods[mdlID] = mergeModConfiguration(modsOld[mdlID], scanOptionalSubModules(mdl.getSubModules(), mdl))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,11 +182,11 @@ function syncModConfigurations(data){
|
||||
|
||||
const mods = {}
|
||||
|
||||
for(let j=0; j<mdls.length; j++){
|
||||
const mdl = mdls[j]
|
||||
if(mdl.type === 'forgemod' || mdl.type === 'litemod' || mdl.type === 'liteloader'){
|
||||
if(mdl.required != null && mdl.required.value != null && mdl.required.value === false){
|
||||
mods[AssetGuard._resolveWithoutVersion(mdl.id)] = scanOptionalSubModules(mdl.sub_modules, mdl)
|
||||
for(let mdl of mdls){
|
||||
const type = mdl.getType()
|
||||
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
|
||||
if(!mdl.getRequired().isRequired()){
|
||||
mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,25 +214,25 @@ function scanOptionalSubModules(mdls, origin){
|
||||
if(mdls != null){
|
||||
const mods = {}
|
||||
|
||||
for(let i=0; i<mdls.length; i++){
|
||||
const mdl = mdls[i]
|
||||
for(let mdl of mdls){
|
||||
const type = mdl.getType()
|
||||
// Optional types.
|
||||
if(mdl.type === 'forgemod' || mdl.type === 'litemod' || mdl.type === 'liteloader'){
|
||||
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
|
||||
// It is optional.
|
||||
if(mdl.required != null && mdl.required.value != null && mdl.required.value === false){
|
||||
mods[AssetGuard._resolveWithoutVersion(mdl.id)] = scanOptionalSubModules(mdl.sub_modules, mdl)
|
||||
if(!mdl.getRequired().isRequired()){
|
||||
mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(Object.keys(mods).length > 0){
|
||||
return {
|
||||
value: origin.required != null && origin.required.def != null ? origin.required.def : true,
|
||||
value: origin.getRequired().isDefault(),
|
||||
mods
|
||||
}
|
||||
}
|
||||
}
|
||||
return origin.required != null && origin.required.def != null ? origin.required.def : true
|
||||
return origin.getRequired().isDefault()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,11 +274,11 @@ function mergeModConfiguration(o, n){
|
||||
|
||||
function refreshDistributionIndex(remote, onSuccess, onError){
|
||||
if(remote){
|
||||
AssetGuard.refreshDistributionDataRemote(ConfigManager.getLauncherDirectory())
|
||||
DistroManager.pullRemote()
|
||||
.then(onSuccess)
|
||||
.catch(onError)
|
||||
} else {
|
||||
AssetGuard.refreshDistributionDataLocal(ConfigManager.getLauncherDirectory())
|
||||
DistroManager.pullLocal()
|
||||
.then(onSuccess)
|
||||
.catch(onError)
|
||||
}
|
||||
@@ -347,7 +347,8 @@ document.addEventListener('readystatechange', function(){
|
||||
if (document.readyState === 'complete'){
|
||||
if(rscShouldLoad){
|
||||
if(!fatalStartupError){
|
||||
showMainUI()
|
||||
const data = DistroManager.getDistribution()
|
||||
showMainUI(data)
|
||||
} else {
|
||||
showFatalStartupError()
|
||||
}
|
||||
@@ -362,11 +363,12 @@ document.addEventListener('readystatechange', function(){
|
||||
}, false)
|
||||
|
||||
// Actions that must be performed after the distribution index is downloaded.
|
||||
ipcRenderer.on('distributionIndexDone', (event, data) => {
|
||||
if(data != null) {
|
||||
ipcRenderer.on('distributionIndexDone', (event, res) => {
|
||||
if(res) {
|
||||
const data = DistroManager.getDistribution()
|
||||
syncModConfigurations(data)
|
||||
if(document.readyState === 'complete'){
|
||||
showMainUI()
|
||||
showMainUI(data)
|
||||
} else {
|
||||
rscShouldLoad = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user