Working on typescript conversion, not functional yet.
@ -52,7 +52,7 @@
|
|||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": [ "app/assets/js/scripts/*.js" ],
|
"files": [ "src/scripts/*.js" ],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-unused-vars": [
|
"no-unused-vars": [
|
||||||
0
|
0
|
||||||
|
3
.gitignore
vendored
@ -3,4 +3,5 @@
|
|||||||
/.vscode/
|
/.vscode/
|
||||||
/target/
|
/target/
|
||||||
/logs/
|
/logs/
|
||||||
/dist/
|
/dist/
|
||||||
|
/out/
|
@ -1,688 +0,0 @@
|
|||||||
const fs = require('fs-extra')
|
|
||||||
const os = require('os')
|
|
||||||
const path = require('path')
|
|
||||||
|
|
||||||
const logger = require('./loggerutil')('%c[ConfigManager]', 'color: #a02d2a; font-weight: bold')
|
|
||||||
|
|
||||||
const sysRoot = process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Application Support' : process.env.HOME)
|
|
||||||
// TODO change
|
|
||||||
const dataPath = path.join(sysRoot, '.westeroscraft')
|
|
||||||
|
|
||||||
// Forked processes do not have access to electron, so we have this workaround.
|
|
||||||
const launcherDir = process.env.CONFIG_DIRECT_PATH || require('electron').remote.app.getPath('userData')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the absolute path of the launcher directory.
|
|
||||||
*
|
|
||||||
* @returns {string} The absolute path of the launcher directory.
|
|
||||||
*/
|
|
||||||
exports.getLauncherDirectory = function(){
|
|
||||||
return launcherDir
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the launcher's data directory. This is where all files related
|
|
||||||
* to game launch are installed (common, instances, java, etc).
|
|
||||||
*
|
|
||||||
* @returns {string} The absolute path of the launcher's data directory.
|
|
||||||
*/
|
|
||||||
exports.getDataDirectory = function(def = false){
|
|
||||||
return !def ? config.settings.launcher.dataDirectory : DEFAULT_CONFIG.settings.launcher.dataDirectory
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the new data directory.
|
|
||||||
*
|
|
||||||
* @param {string} dataDirectory The new data directory.
|
|
||||||
*/
|
|
||||||
exports.setDataDirectory = function(dataDirectory){
|
|
||||||
config.settings.launcher.dataDirectory = dataDirectory
|
|
||||||
}
|
|
||||||
|
|
||||||
const configPath = path.join(exports.getLauncherDirectory(), 'config.json')
|
|
||||||
const configPathLEGACY = path.join(dataPath, 'config.json')
|
|
||||||
const firstLaunch = !fs.existsSync(configPath) && !fs.existsSync(configPathLEGACY)
|
|
||||||
|
|
||||||
exports.getAbsoluteMinRAM = function(){
|
|
||||||
const mem = os.totalmem()
|
|
||||||
return mem >= 6000000000 ? 3 : 2
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getAbsoluteMaxRAM = function(){
|
|
||||||
const mem = os.totalmem()
|
|
||||||
const gT16 = mem-16000000000
|
|
||||||
return Math.floor((mem-1000000000-(gT16 > 0 ? (Number.parseInt(gT16/8) + 16000000000/4) : mem/4))/1000000000)
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveMaxRAM(){
|
|
||||||
const mem = os.totalmem()
|
|
||||||
return mem >= 8000000000 ? '4G' : (mem >= 6000000000 ? '3G' : '2G')
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveMinRAM(){
|
|
||||||
return resolveMaxRAM()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Three types of values:
|
|
||||||
* Static = Explicitly declared.
|
|
||||||
* Dynamic = Calculated by a private function.
|
|
||||||
* Resolved = Resolved externally, defaults to null.
|
|
||||||
*/
|
|
||||||
const DEFAULT_CONFIG = {
|
|
||||||
settings: {
|
|
||||||
java: {
|
|
||||||
minRAM: resolveMinRAM(),
|
|
||||||
maxRAM: resolveMaxRAM(), // Dynamic
|
|
||||||
executable: null,
|
|
||||||
jvmOptions: [
|
|
||||||
'-XX:+UseConcMarkSweepGC',
|
|
||||||
'-XX:+CMSIncrementalMode',
|
|
||||||
'-XX:-UseAdaptiveSizePolicy',
|
|
||||||
'-Xmn128M'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
game: {
|
|
||||||
resWidth: 1280,
|
|
||||||
resHeight: 720,
|
|
||||||
fullscreen: false,
|
|
||||||
autoConnect: true,
|
|
||||||
launchDetached: true
|
|
||||||
},
|
|
||||||
launcher: {
|
|
||||||
allowPrerelease: false,
|
|
||||||
dataDirectory: dataPath
|
|
||||||
}
|
|
||||||
},
|
|
||||||
newsCache: {
|
|
||||||
date: null,
|
|
||||||
content: null,
|
|
||||||
dismissed: false
|
|
||||||
},
|
|
||||||
clientToken: null,
|
|
||||||
selectedServer: null, // Resolved
|
|
||||||
selectedAccount: null,
|
|
||||||
authenticationDatabase: {},
|
|
||||||
modConfigurations: []
|
|
||||||
}
|
|
||||||
|
|
||||||
let config = null
|
|
||||||
|
|
||||||
// Persistance Utility Functions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the current configuration to a file.
|
|
||||||
*/
|
|
||||||
exports.save = function(){
|
|
||||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 4), 'UTF-8')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the configuration into memory. If a configuration file exists,
|
|
||||||
* that will be read and saved. Otherwise, a default configuration will
|
|
||||||
* be generated. Note that "resolved" values default to null and will
|
|
||||||
* need to be externally assigned.
|
|
||||||
*/
|
|
||||||
exports.load = function(){
|
|
||||||
let doLoad = true
|
|
||||||
|
|
||||||
if(!fs.existsSync(configPath)){
|
|
||||||
// Create all parent directories.
|
|
||||||
fs.ensureDirSync(path.join(configPath, '..'))
|
|
||||||
if(fs.existsSync(configPathLEGACY)){
|
|
||||||
fs.moveSync(configPathLEGACY, configPath)
|
|
||||||
} else {
|
|
||||||
doLoad = false
|
|
||||||
config = DEFAULT_CONFIG
|
|
||||||
exports.save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(doLoad){
|
|
||||||
let doValidate = false
|
|
||||||
try {
|
|
||||||
config = JSON.parse(fs.readFileSync(configPath, 'UTF-8'))
|
|
||||||
doValidate = true
|
|
||||||
} catch (err){
|
|
||||||
logger.error(err)
|
|
||||||
logger.log('Configuration file contains malformed JSON or is corrupt.')
|
|
||||||
logger.log('Generating a new configuration file.')
|
|
||||||
fs.ensureDirSync(path.join(configPath, '..'))
|
|
||||||
config = DEFAULT_CONFIG
|
|
||||||
exports.save()
|
|
||||||
}
|
|
||||||
if(doValidate){
|
|
||||||
config = validateKeySet(DEFAULT_CONFIG, config)
|
|
||||||
exports.save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.log('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.
|
|
||||||
*
|
|
||||||
* @param {Object} srcObj The source object to reference against.
|
|
||||||
* @param {Object} destObj The destination object.
|
|
||||||
* @returns {Object} A validated destination object.
|
|
||||||
*/
|
|
||||||
function validateKeySet(srcObj, destObj){
|
|
||||||
if(srcObj == null){
|
|
||||||
srcObj = {}
|
|
||||||
}
|
|
||||||
const validationBlacklist = ['authenticationDatabase']
|
|
||||||
const keys = Object.keys(srcObj)
|
|
||||||
for(let i=0; i<keys.length; i++){
|
|
||||||
if(typeof destObj[keys[i]] === 'undefined'){
|
|
||||||
destObj[keys[i]] = srcObj[keys[i]]
|
|
||||||
} else if(typeof srcObj[keys[i]] === 'object' && srcObj[keys[i]] != null && !(srcObj[keys[i]] instanceof Array) && validationBlacklist.indexOf(keys[i]) === -1){
|
|
||||||
destObj[keys[i]] = validateKeySet(srcObj[keys[i]], destObj[keys[i]])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return destObj
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to see if this is the first time the user has launched the
|
|
||||||
* application. This is determined by the existance of the data path.
|
|
||||||
*
|
|
||||||
* @returns {boolean} True if this is the first launch, otherwise false.
|
|
||||||
*/
|
|
||||||
exports.isFirstLaunch = function(){
|
|
||||||
return firstLaunch
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of the folder in the OS temp directory which we
|
|
||||||
* will use to extract and store native dependencies for game launch.
|
|
||||||
*
|
|
||||||
* @returns {string} The name of the folder.
|
|
||||||
*/
|
|
||||||
exports.getTempNativeFolder = function(){
|
|
||||||
return 'WCNatives'
|
|
||||||
}
|
|
||||||
|
|
||||||
// System Settings (Unconfigurable on UI)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the news cache to determine
|
|
||||||
* whether or not there is newer news.
|
|
||||||
*
|
|
||||||
* @returns {Object} The news cache object.
|
|
||||||
*/
|
|
||||||
exports.getNewsCache = function(){
|
|
||||||
return config.newsCache
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the new news cache object.
|
|
||||||
*
|
|
||||||
* @param {Object} newsCache The new news cache object.
|
|
||||||
*/
|
|
||||||
exports.setNewsCache = function(newsCache){
|
|
||||||
config.newsCache = newsCache
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set whether or not the news has been dismissed (checked)
|
|
||||||
*
|
|
||||||
* @param {boolean} dismissed Whether or not the news has been dismissed (checked).
|
|
||||||
*/
|
|
||||||
exports.setNewsCacheDismissed = function(dismissed){
|
|
||||||
config.newsCache.dismissed = dismissed
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the common directory for shared
|
|
||||||
* game files (assets, libraries, etc).
|
|
||||||
*
|
|
||||||
* @returns {string} The launcher's common directory.
|
|
||||||
*/
|
|
||||||
exports.getCommonDirectory = function(){
|
|
||||||
return path.join(exports.getDataDirectory(), 'common')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the instance directory for the per
|
|
||||||
* server game directories.
|
|
||||||
*
|
|
||||||
* @returns {string} The launcher's instance directory.
|
|
||||||
*/
|
|
||||||
exports.getInstanceDirectory = function(){
|
|
||||||
return path.join(exports.getDataDirectory(), 'instances')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the launcher's Client Token.
|
|
||||||
* There is no default client token.
|
|
||||||
*
|
|
||||||
* @returns {string} The launcher's Client Token.
|
|
||||||
*/
|
|
||||||
exports.getClientToken = function(){
|
|
||||||
return config.clientToken
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the launcher's Client Token.
|
|
||||||
*
|
|
||||||
* @param {string} clientToken The launcher's new Client Token.
|
|
||||||
*/
|
|
||||||
exports.setClientToken = function(clientToken){
|
|
||||||
config.clientToken = clientToken
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the ID of the selected serverpack.
|
|
||||||
*
|
|
||||||
* @param {boolean} def Optional. If true, the default value will be returned.
|
|
||||||
* @returns {string} The ID of the selected serverpack.
|
|
||||||
*/
|
|
||||||
exports.getSelectedServer = function(def = false){
|
|
||||||
return !def ? config.selectedServer : DEFAULT_CONFIG.clientToken
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the ID of the selected serverpack.
|
|
||||||
*
|
|
||||||
* @param {string} serverID The ID of the new selected serverpack.
|
|
||||||
*/
|
|
||||||
exports.setSelectedServer = function(serverID){
|
|
||||||
config.selectedServer = serverID
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an array of each account currently authenticated by the launcher.
|
|
||||||
*
|
|
||||||
* @returns {Array.<Object>} An array of each stored authenticated account.
|
|
||||||
*/
|
|
||||||
exports.getAuthAccounts = function(){
|
|
||||||
return config.authenticationDatabase
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the authenticated account with the given uuid. Value may
|
|
||||||
* be null.
|
|
||||||
*
|
|
||||||
* @param {string} uuid The uuid of the authenticated account.
|
|
||||||
* @returns {Object} The authenticated account with the given uuid.
|
|
||||||
*/
|
|
||||||
exports.getAuthAccount = function(uuid){
|
|
||||||
return config.authenticationDatabase[uuid]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the access token of an authenticated account.
|
|
||||||
*
|
|
||||||
* @param {string} uuid The uuid of the authenticated account.
|
|
||||||
* @param {string} accessToken The new Access Token.
|
|
||||||
*
|
|
||||||
* @returns {Object} The authenticated account object created by this action.
|
|
||||||
*/
|
|
||||||
exports.updateAuthAccount = function(uuid, accessToken){
|
|
||||||
config.authenticationDatabase[uuid].accessToken = accessToken
|
|
||||||
return config.authenticationDatabase[uuid]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an authenticated account to the database to be stored.
|
|
||||||
*
|
|
||||||
* @param {string} uuid The uuid of the authenticated account.
|
|
||||||
* @param {string} accessToken The accessToken of the authenticated account.
|
|
||||||
* @param {string} username The username (usually email) of the authenticated account.
|
|
||||||
* @param {string} displayName The in game name of the authenticated account.
|
|
||||||
*
|
|
||||||
* @returns {Object} The authenticated account object created by this action.
|
|
||||||
*/
|
|
||||||
exports.addAuthAccount = function(uuid, accessToken, username, displayName){
|
|
||||||
config.selectedAccount = uuid
|
|
||||||
config.authenticationDatabase[uuid] = {
|
|
||||||
accessToken,
|
|
||||||
username: username.trim(),
|
|
||||||
uuid: uuid.trim(),
|
|
||||||
displayName: displayName.trim()
|
|
||||||
}
|
|
||||||
return config.authenticationDatabase[uuid]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove an authenticated account from the database. If the account
|
|
||||||
* was also the selected account, a new one will be selected. If there
|
|
||||||
* are no accounts, the selected account will be null.
|
|
||||||
*
|
|
||||||
* @param {string} uuid The uuid of the authenticated account.
|
|
||||||
*
|
|
||||||
* @returns {boolean} True if the account was removed, false if it never existed.
|
|
||||||
*/
|
|
||||||
exports.removeAuthAccount = function(uuid){
|
|
||||||
if(config.authenticationDatabase[uuid] != null){
|
|
||||||
delete config.authenticationDatabase[uuid]
|
|
||||||
if(config.selectedAccount === uuid){
|
|
||||||
const keys = Object.keys(config.authenticationDatabase)
|
|
||||||
if(keys.length > 0){
|
|
||||||
config.selectedAccount = keys[0]
|
|
||||||
} else {
|
|
||||||
config.selectedAccount = null
|
|
||||||
config.clientToken = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the currently selected authenticated account.
|
|
||||||
*
|
|
||||||
* @returns {Object} The selected authenticated account.
|
|
||||||
*/
|
|
||||||
exports.getSelectedAccount = function(){
|
|
||||||
return config.authenticationDatabase[config.selectedAccount]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the selected authenticated account.
|
|
||||||
*
|
|
||||||
* @param {string} uuid The UUID of the account which is to be set
|
|
||||||
* as the selected account.
|
|
||||||
*
|
|
||||||
* @returns {Object} The selected authenticated account.
|
|
||||||
*/
|
|
||||||
exports.setSelectedAccount = function(uuid){
|
|
||||||
const authAcc = config.authenticationDatabase[uuid]
|
|
||||||
if(authAcc != null) {
|
|
||||||
config.selectedAccount = uuid
|
|
||||||
}
|
|
||||||
return authAcc
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an array of each mod configuration currently stored.
|
|
||||||
*
|
|
||||||
* @returns {Array.<Object>} An array of each stored mod configuration.
|
|
||||||
*/
|
|
||||||
exports.getModConfigurations = function(){
|
|
||||||
return config.modConfigurations
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the array of stored mod configurations.
|
|
||||||
*
|
|
||||||
* @param {Array.<Object>} configurations An array of mod configurations.
|
|
||||||
*/
|
|
||||||
exports.setModConfigurations = function(configurations){
|
|
||||||
config.modConfigurations = configurations
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mod configuration for a specific server.
|
|
||||||
*
|
|
||||||
* @param {string} serverid The id of the server.
|
|
||||||
* @returns {Object} The mod configuration for the given server.
|
|
||||||
*/
|
|
||||||
exports.getModConfiguration = function(serverid){
|
|
||||||
const cfgs = config.modConfigurations
|
|
||||||
for(let i=0; i<cfgs.length; i++){
|
|
||||||
if(cfgs[i].id === serverid){
|
|
||||||
return cfgs[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the mod configuration for a specific server. This overrides any existing value.
|
|
||||||
*
|
|
||||||
* @param {string} serverid The id of the server for the given mod configuration.
|
|
||||||
* @param {Object} configuration The mod configuration for the given server.
|
|
||||||
*/
|
|
||||||
exports.setModConfiguration = function(serverid, configuration){
|
|
||||||
const cfgs = config.modConfigurations
|
|
||||||
for(let i=0; i<cfgs.length; i++){
|
|
||||||
if(cfgs[i].id === serverid){
|
|
||||||
cfgs[i] = configuration
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cfgs.push(configuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
// User Configurable Settings
|
|
||||||
|
|
||||||
// Java Settings
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the minimum amount of memory for JVM initialization. This value
|
|
||||||
* contains the units of memory. For example, '5G' = 5 GigaBytes, '1024M' =
|
|
||||||
* 1024 MegaBytes, etc.
|
|
||||||
*
|
|
||||||
* @param {boolean} def Optional. If true, the default value will be returned.
|
|
||||||
* @returns {string} The minimum amount of memory for JVM initialization.
|
|
||||||
*/
|
|
||||||
exports.getMinRAM = function(def = false){
|
|
||||||
return !def ? config.settings.java.minRAM : DEFAULT_CONFIG.settings.java.minRAM
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the minimum amount of memory for JVM initialization. This value should
|
|
||||||
* contain the units of memory. For example, '5G' = 5 GigaBytes, '1024M' =
|
|
||||||
* 1024 MegaBytes, etc.
|
|
||||||
*
|
|
||||||
* @param {string} minRAM The new minimum amount of memory for JVM initialization.
|
|
||||||
*/
|
|
||||||
exports.setMinRAM = function(minRAM){
|
|
||||||
config.settings.java.minRAM = minRAM
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the maximum amount of memory for JVM initialization. This value
|
|
||||||
* contains the units of memory. For example, '5G' = 5 GigaBytes, '1024M' =
|
|
||||||
* 1024 MegaBytes, etc.
|
|
||||||
*
|
|
||||||
* @param {boolean} def Optional. If true, the default value will be returned.
|
|
||||||
* @returns {string} The maximum amount of memory for JVM initialization.
|
|
||||||
*/
|
|
||||||
exports.getMaxRAM = function(def = false){
|
|
||||||
return !def ? config.settings.java.maxRAM : resolveMaxRAM()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the maximum amount of memory for JVM initialization. This value should
|
|
||||||
* contain the units of memory. For example, '5G' = 5 GigaBytes, '1024M' =
|
|
||||||
* 1024 MegaBytes, etc.
|
|
||||||
*
|
|
||||||
* @param {string} maxRAM The new maximum amount of memory for JVM initialization.
|
|
||||||
*/
|
|
||||||
exports.setMaxRAM = function(maxRAM){
|
|
||||||
config.settings.java.maxRAM = maxRAM
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the path of the Java Executable.
|
|
||||||
*
|
|
||||||
* This is a resolved configuration value and defaults to null until externally assigned.
|
|
||||||
*
|
|
||||||
* @returns {string} The path of the Java Executable.
|
|
||||||
*/
|
|
||||||
exports.getJavaExecutable = function(){
|
|
||||||
return config.settings.java.executable
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the path of the Java Executable.
|
|
||||||
*
|
|
||||||
* @param {string} executable The new path of the Java Executable.
|
|
||||||
*/
|
|
||||||
exports.setJavaExecutable = function(executable){
|
|
||||||
config.settings.java.executable = executable
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the additional arguments for JVM initialization. Required arguments,
|
|
||||||
* such as memory allocation, will be dynamically resolved and will not be included
|
|
||||||
* in this value.
|
|
||||||
*
|
|
||||||
* @param {boolean} def Optional. If true, the default value will be returned.
|
|
||||||
* @returns {Array.<string>} An array of the additional arguments for JVM initialization.
|
|
||||||
*/
|
|
||||||
exports.getJVMOptions = function(def = false){
|
|
||||||
return !def ? config.settings.java.jvmOptions : DEFAULT_CONFIG.settings.java.jvmOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the additional arguments for JVM initialization. Required arguments,
|
|
||||||
* such as memory allocation, will be dynamically resolved and should not be
|
|
||||||
* included in this value.
|
|
||||||
*
|
|
||||||
* @param {Array.<string>} jvmOptions An array of the new additional arguments for JVM
|
|
||||||
* initialization.
|
|
||||||
*/
|
|
||||||
exports.setJVMOptions = function(jvmOptions){
|
|
||||||
config.settings.java.jvmOptions = jvmOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// Game Settings
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the width of the game window.
|
|
||||||
*
|
|
||||||
* @param {boolean} def Optional. If true, the default value will be returned.
|
|
||||||
* @returns {number} The width of the game window.
|
|
||||||
*/
|
|
||||||
exports.getGameWidth = function(def = false){
|
|
||||||
return !def ? config.settings.game.resWidth : DEFAULT_CONFIG.settings.game.resWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the width of the game window.
|
|
||||||
*
|
|
||||||
* @param {number} resWidth The new width of the game window.
|
|
||||||
*/
|
|
||||||
exports.setGameWidth = function(resWidth){
|
|
||||||
config.settings.game.resWidth = Number.parseInt(resWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate a potential new width value.
|
|
||||||
*
|
|
||||||
* @param {number} resWidth The width value to validate.
|
|
||||||
* @returns {boolean} Whether or not the value is valid.
|
|
||||||
*/
|
|
||||||
exports.validateGameWidth = function(resWidth){
|
|
||||||
const nVal = Number.parseInt(resWidth)
|
|
||||||
return Number.isInteger(nVal) && nVal >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the height of the game window.
|
|
||||||
*
|
|
||||||
* @param {boolean} def Optional. If true, the default value will be returned.
|
|
||||||
* @returns {number} The height of the game window.
|
|
||||||
*/
|
|
||||||
exports.getGameHeight = function(def = false){
|
|
||||||
return !def ? config.settings.game.resHeight : DEFAULT_CONFIG.settings.game.resHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the height of the game window.
|
|
||||||
*
|
|
||||||
* @param {number} resHeight The new height of the game window.
|
|
||||||
*/
|
|
||||||
exports.setGameHeight = function(resHeight){
|
|
||||||
config.settings.game.resHeight = Number.parseInt(resHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate a potential new height value.
|
|
||||||
*
|
|
||||||
* @param {number} resHeight The height value to validate.
|
|
||||||
* @returns {boolean} Whether or not the value is valid.
|
|
||||||
*/
|
|
||||||
exports.validateGameHeight = function(resHeight){
|
|
||||||
const nVal = Number.parseInt(resHeight)
|
|
||||||
return Number.isInteger(nVal) && nVal >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the game should be launched in fullscreen mode.
|
|
||||||
*
|
|
||||||
* @param {boolean} def Optional. If true, the default value will be returned.
|
|
||||||
* @returns {boolean} Whether or not the game is set to launch in fullscreen mode.
|
|
||||||
*/
|
|
||||||
exports.getFullscreen = function(def = false){
|
|
||||||
return !def ? config.settings.game.fullscreen : DEFAULT_CONFIG.settings.game.fullscreen
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the status of if the game should be launched in fullscreen mode.
|
|
||||||
*
|
|
||||||
* @param {boolean} fullscreen Whether or not the game should launch in fullscreen mode.
|
|
||||||
*/
|
|
||||||
exports.setFullscreen = function(fullscreen){
|
|
||||||
config.settings.game.fullscreen = fullscreen
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the game should auto connect to servers.
|
|
||||||
*
|
|
||||||
* @param {boolean} def Optional. If true, the default value will be returned.
|
|
||||||
* @returns {boolean} Whether or not the game should auto connect to servers.
|
|
||||||
*/
|
|
||||||
exports.getAutoConnect = function(def = false){
|
|
||||||
return !def ? config.settings.game.autoConnect : DEFAULT_CONFIG.settings.game.autoConnect
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the status of whether or not the game should auto connect to servers.
|
|
||||||
*
|
|
||||||
* @param {boolean} autoConnect Whether or not the game should auto connect to servers.
|
|
||||||
*/
|
|
||||||
exports.setAutoConnect = function(autoConnect){
|
|
||||||
config.settings.game.autoConnect = autoConnect
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the game should launch as a detached process.
|
|
||||||
*
|
|
||||||
* @param {boolean} def Optional. If true, the default value will be returned.
|
|
||||||
* @returns {boolean} Whether or not the game will launch as a detached process.
|
|
||||||
*/
|
|
||||||
exports.getLaunchDetached = function(def = false){
|
|
||||||
return !def ? config.settings.game.launchDetached : DEFAULT_CONFIG.settings.game.launchDetached
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the status of whether or not the game should launch as a detached process.
|
|
||||||
*
|
|
||||||
* @param {boolean} launchDetached Whether or not the game should launch as a detached process.
|
|
||||||
*/
|
|
||||||
exports.setLaunchDetached = function(launchDetached){
|
|
||||||
config.settings.game.launchDetached = launchDetached
|
|
||||||
}
|
|
||||||
|
|
||||||
// Launcher Settings
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the launcher should download prerelease versions.
|
|
||||||
*
|
|
||||||
* @param {boolean} def Optional. If true, the default value will be returned.
|
|
||||||
* @returns {boolean} Whether or not the launcher should download prerelease versions.
|
|
||||||
*/
|
|
||||||
exports.getAllowPrerelease = function(def = false){
|
|
||||||
return !def ? config.settings.launcher.allowPrerelease : DEFAULT_CONFIG.settings.launcher.allowPrerelease
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the status of Whether or not the launcher should download prerelease versions.
|
|
||||||
*
|
|
||||||
* @param {boolean} launchDetached Whether or not the launcher should download prerelease versions.
|
|
||||||
*/
|
|
||||||
exports.setAllowPrerelease = function(allowPrerelease){
|
|
||||||
config.settings.launcher.allowPrerelease = allowPrerelease
|
|
||||||
}
|
|
@ -1,605 +0,0 @@
|
|||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const request = require('request')
|
|
||||||
|
|
||||||
const ConfigManager = require('./configmanager')
|
|
||||||
const logger = require('./loggerutil')('%c[DistroManager]', 'color: #a02d2a; font-weight: bold')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.artifactClassifier = m1[3] || undefined
|
|
||||||
this.artifactVersion = m1[2] || '???'
|
|
||||||
this.artifactID = m1[1] || '???'
|
|
||||||
this.artifactGroup = m1[0] || '???'
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
// Improper identifier
|
|
||||||
logger.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.artifactClassifier != undefined ? `-${this.artifactClassifier}` : ''}.${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.VersionManifest:
|
|
||||||
this.artifact.path = path.join(ConfigManager.getCommonDirectory(), 'versions', this.getIdentifier(), `${this.getIdentifier()}.json`)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string} The identifier without he version or extension.
|
|
||||||
*/
|
|
||||||
getVersionlessID(){
|
|
||||||
return this.getGroup() + ':' + this.getID()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string} The identifier without the extension.
|
|
||||||
*/
|
|
||||||
getExtensionlessID(){
|
|
||||||
return this.getIdentifier().split('@')[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string} The version of this module's artifact.
|
|
||||||
*/
|
|
||||||
getVersion(){
|
|
||||||
return this.artifactVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string} The classifier of this module's artifact
|
|
||||||
*/
|
|
||||||
getClassifier(){
|
|
||||||
return this.artifactClassifier
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 this.mainServer != null ? this.getServer(this.mainServer) : null
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
exports.DistroIndex
|
|
||||||
|
|
||||||
exports.Types = {
|
|
||||||
Library: 'Library',
|
|
||||||
ForgeHosted: 'ForgeHosted',
|
|
||||||
Forge: 'Forge', // Unimplemented
|
|
||||||
LiteLoader: 'LiteLoader',
|
|
||||||
ForgeMod: 'ForgeMod',
|
|
||||||
LiteMod: 'LiteMod',
|
|
||||||
File: 'File',
|
|
||||||
VersionManifest: 'VersionManifest'
|
|
||||||
}
|
|
||||||
|
|
||||||
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){
|
|
||||||
|
|
||||||
try {
|
|
||||||
data = DistroIndex.fromJSON(JSON.parse(body))
|
|
||||||
} catch (e) {
|
|
||||||
reject(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
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){
|
|
||||||
logger.log('Developer mode enabled.')
|
|
||||||
logger.log('If you don\'t know what that means, revert immediately.')
|
|
||||||
} else {
|
|
||||||
logger.log('Developer mode disabled.')
|
|
||||||
}
|
|
||||||
DEV_MODE = value
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.isDevMode = function(){
|
|
||||||
return DEV_MODE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {DistroIndex}
|
|
||||||
*/
|
|
||||||
exports.getDistribution = function(){
|
|
||||||
return data
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
const getFromEnv = parseInt(process.env.ELECTRON_IS_DEV, 10) === 1
|
|
||||||
const isEnvSet = 'ELECTRON_IS_DEV' in process.env
|
|
||||||
|
|
||||||
module.exports = isEnvSet ? getFromEnv : (process.defaultApp || /node_modules[\\/]electron[\\/]/.test(process.execPath))
|
|
@ -1,21 +0,0 @@
|
|||||||
const fs = require('fs-extra')
|
|
||||||
const path = require('path')
|
|
||||||
|
|
||||||
let lang
|
|
||||||
|
|
||||||
exports.loadLanguage = function(id){
|
|
||||||
lang = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'lang', `${id}.json`))) || {}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.query = function(id){
|
|
||||||
let query = id.split('.')
|
|
||||||
let res = lang
|
|
||||||
for(let q of query){
|
|
||||||
res = res[q]
|
|
||||||
}
|
|
||||||
return res === lang ? {} : res
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.queryJS = function(id){
|
|
||||||
return exports.query(`js.${id}`)
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
class LoggerUtil {
|
|
||||||
|
|
||||||
constructor(prefix, style){
|
|
||||||
this.prefix = prefix
|
|
||||||
this.style = style
|
|
||||||
}
|
|
||||||
|
|
||||||
log(){
|
|
||||||
console.log.apply(null, [this.prefix, this.style, ...arguments])
|
|
||||||
}
|
|
||||||
|
|
||||||
info(){
|
|
||||||
console.info.apply(null, [this.prefix, this.style, ...arguments])
|
|
||||||
}
|
|
||||||
|
|
||||||
warn(){
|
|
||||||
console.warn.apply(null, [this.prefix, this.style, ...arguments])
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(){
|
|
||||||
console.debug.apply(null, [this.prefix, this.style, ...arguments])
|
|
||||||
}
|
|
||||||
|
|
||||||
error(){
|
|
||||||
console.error.apply(null, [this.prefix, this.style, ...arguments])
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function (prefix, style){
|
|
||||||
return new LoggerUtil(prefix, style)
|
|
||||||
}
|
|
@ -1,271 +0,0 @@
|
|||||||
/**
|
|
||||||
* Mojang
|
|
||||||
*
|
|
||||||
* This module serves as a minimal wrapper for Mojang's REST api.
|
|
||||||
*
|
|
||||||
* @module mojang
|
|
||||||
*/
|
|
||||||
// Requirements
|
|
||||||
const request = require('request')
|
|
||||||
const logger = require('./loggerutil')('%c[Mojang]', 'color: #a02d2a; font-weight: bold')
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
const minecraftAgent = {
|
|
||||||
name: 'Minecraft',
|
|
||||||
version: 1
|
|
||||||
}
|
|
||||||
const authpath = 'https://authserver.mojang.com'
|
|
||||||
const statuses = [
|
|
||||||
{
|
|
||||||
service: 'sessionserver.mojang.com',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Multiplayer Session Service',
|
|
||||||
essential: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: 'authserver.mojang.com',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Authentication Service',
|
|
||||||
essential: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: 'textures.minecraft.net',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Minecraft Skins',
|
|
||||||
essential: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: 'api.mojang.com',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Public API',
|
|
||||||
essential: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: 'minecraft.net',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Minecraft.net',
|
|
||||||
essential: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: 'account.mojang.com',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Mojang Accounts Website',
|
|
||||||
essential: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// Functions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a Mojang status color to a hex value. Valid statuses
|
|
||||||
* are 'green', 'yellow', 'red', and 'grey'. Grey is a custom status
|
|
||||||
* to our project which represents an unknown status.
|
|
||||||
*
|
|
||||||
* @param {string} status A valid status code.
|
|
||||||
* @returns {string} The hex color of the status code.
|
|
||||||
*/
|
|
||||||
exports.statusToHex = function(status){
|
|
||||||
switch(status.toLowerCase()){
|
|
||||||
case 'green':
|
|
||||||
return '#a5c325'
|
|
||||||
case 'yellow':
|
|
||||||
return '#eac918'
|
|
||||||
case 'red':
|
|
||||||
return '#c32625'
|
|
||||||
case 'grey':
|
|
||||||
default:
|
|
||||||
return '#848484'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the status of Mojang's services.
|
|
||||||
* The response is condensed into a single object. Each service is
|
|
||||||
* a key, where the value is an object containing a status and name
|
|
||||||
* property.
|
|
||||||
*
|
|
||||||
* @see http://wiki.vg/Mojang_API#API_Status
|
|
||||||
*/
|
|
||||||
exports.status = function(){
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
request.get('https://status.mojang.com/check',
|
|
||||||
{
|
|
||||||
json: true,
|
|
||||||
timeout: 2500
|
|
||||||
},
|
|
||||||
function(error, response, body){
|
|
||||||
|
|
||||||
if(error || response.statusCode !== 200){
|
|
||||||
logger.warn('Unable to retrieve Mojang status.')
|
|
||||||
logger.debug('Error while retrieving Mojang statuses:', error)
|
|
||||||
//reject(error || response.statusCode)
|
|
||||||
for(let i=0; i<statuses.length; i++){
|
|
||||||
statuses[i].status = 'grey'
|
|
||||||
}
|
|
||||||
resolve(statuses)
|
|
||||||
} else {
|
|
||||||
for(let i=0; i<body.length; i++){
|
|
||||||
const key = Object.keys(body[i])[0]
|
|
||||||
inner:
|
|
||||||
for(let j=0; j<statuses.length; j++){
|
|
||||||
if(statuses[j].service === key) {
|
|
||||||
statuses[j].status = body[i][key]
|
|
||||||
break inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve(statuses)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Authenticate a user with their Mojang credentials.
|
|
||||||
*
|
|
||||||
* @param {string} username The user's username, this is often an email.
|
|
||||||
* @param {string} password The user's password.
|
|
||||||
* @param {string} clientToken The launcher's Client Token.
|
|
||||||
* @param {boolean} requestUser Optional. Adds user object to the reponse.
|
|
||||||
* @param {Object} agent Optional. Provided by default. Adds user info to the response.
|
|
||||||
*
|
|
||||||
* @see http://wiki.vg/Authentication#Authenticate
|
|
||||||
*/
|
|
||||||
exports.authenticate = function(username, password, clientToken, requestUser = true, agent = minecraftAgent){
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
agent,
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
requestUser
|
|
||||||
}
|
|
||||||
if(clientToken != null){
|
|
||||||
body.clientToken = clientToken
|
|
||||||
}
|
|
||||||
|
|
||||||
request.post(authpath + '/authenticate',
|
|
||||||
{
|
|
||||||
json: true,
|
|
||||||
body
|
|
||||||
},
|
|
||||||
function(error, response, body){
|
|
||||||
if(error){
|
|
||||||
logger.error('Error during authentication.', error)
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
if(response.statusCode === 200){
|
|
||||||
resolve(body)
|
|
||||||
} else {
|
|
||||||
reject(body || {code: 'ENOTFOUND'})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate an access token. This should always be done before launching.
|
|
||||||
* The client token should match the one used to create the access token.
|
|
||||||
*
|
|
||||||
* @param {string} accessToken The access token to validate.
|
|
||||||
* @param {string} clientToken The launcher's client token.
|
|
||||||
*
|
|
||||||
* @see http://wiki.vg/Authentication#Validate
|
|
||||||
*/
|
|
||||||
exports.validate = function(accessToken, clientToken){
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
request.post(authpath + '/validate',
|
|
||||||
{
|
|
||||||
json: true,
|
|
||||||
body: {
|
|
||||||
accessToken,
|
|
||||||
clientToken
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(error, response, body){
|
|
||||||
if(error){
|
|
||||||
logger.error('Error during validation.', error)
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
if(response.statusCode === 403){
|
|
||||||
resolve(false)
|
|
||||||
} else {
|
|
||||||
// 204 if valid
|
|
||||||
resolve(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates an access token. The clientToken must match the
|
|
||||||
* token used to create the provided accessToken.
|
|
||||||
*
|
|
||||||
* @param {string} accessToken The access token to invalidate.
|
|
||||||
* @param {string} clientToken The launcher's client token.
|
|
||||||
*
|
|
||||||
* @see http://wiki.vg/Authentication#Invalidate
|
|
||||||
*/
|
|
||||||
exports.invalidate = function(accessToken, clientToken){
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
request.post(authpath + '/invalidate',
|
|
||||||
{
|
|
||||||
json: true,
|
|
||||||
body: {
|
|
||||||
accessToken,
|
|
||||||
clientToken
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(error, response, body){
|
|
||||||
if(error){
|
|
||||||
logger.error('Error during invalidation.', error)
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
if(response.statusCode === 204){
|
|
||||||
resolve()
|
|
||||||
} else {
|
|
||||||
reject(body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh a user's authentication. This should be used to keep a user logged
|
|
||||||
* in without asking them for their credentials again. A new access token will
|
|
||||||
* be generated using a recent invalid access token. See Wiki for more info.
|
|
||||||
*
|
|
||||||
* @param {string} accessToken The old access token.
|
|
||||||
* @param {string} clientToken The launcher's client token.
|
|
||||||
* @param {boolean} requestUser Optional. Adds user object to the reponse.
|
|
||||||
*
|
|
||||||
* @see http://wiki.vg/Authentication#Refresh
|
|
||||||
*/
|
|
||||||
exports.refresh = function(accessToken, clientToken, requestUser = true){
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
request.post(authpath + '/refresh',
|
|
||||||
{
|
|
||||||
json: true,
|
|
||||||
body: {
|
|
||||||
accessToken,
|
|
||||||
clientToken,
|
|
||||||
requestUser
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(error, response, body){
|
|
||||||
if(error){
|
|
||||||
logger.error('Error during refresh.', error)
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
if(response.statusCode === 200){
|
|
||||||
resolve(body)
|
|
||||||
} else {
|
|
||||||
reject(body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/* Github Code Highlighting. */
|
/* Github Code Highlighting. */
|
||||||
@import "../../../node_modules/github-syntax-dark/lib/github-dark.css";
|
@import "../../node_modules/github-syntax-dark/lib/github-dark.css";
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 244 KiB |
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 181 KiB |
Before Width: | Height: | Size: 502 KiB After Width: | Height: | Size: 502 KiB |
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 268 KiB |
Before Width: | Height: | Size: 456 KiB After Width: | Height: | Size: 456 KiB |
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 2.6 MiB |
Before Width: | Height: | Size: 5.0 MiB After Width: | Height: | Size: 5.0 MiB |
Before Width: | Height: | Size: 298 B After Width: | Height: | Size: 298 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 875 B After Width: | Height: | Size: 875 B |
Before Width: | Height: | Size: 756 B After Width: | Height: | Size: 756 B |
Before Width: | Height: | Size: 959 B After Width: | Height: | Size: 959 B |
Before Width: | Height: | Size: 602 B After Width: | Height: | Size: 602 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 809 B After Width: | Height: | Size: 809 B |
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 932 B |
Before Width: | Height: | Size: 822 B After Width: | Height: | Size: 822 B |
Before Width: | Height: | Size: 1018 B After Width: | Height: | Size: 1018 B |
Before Width: | Height: | Size: 907 B After Width: | Height: | Size: 907 B |
Before Width: | Height: | Size: 700 B After Width: | Height: | Size: 700 B |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 654 B After Width: | Height: | Size: 654 B |
@ -2,9 +2,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-In6B8teKZQll5heMl9bS7CESTbGvuAt3VVV86BUQBDk='"/>
|
<meta charset="utf-8" http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-In6B8teKZQll5heMl9bS7CESTbGvuAt3VVV86BUQBDk='"/>
|
||||||
<title>Westeroscraft Launcher</title>
|
<title>Westeroscraft Launcher</title>
|
||||||
<script src="./assets/js/scripts/uicore.js"></script>
|
<script src="../../out/scripts/uicore.js"></script>
|
||||||
<script src="./assets/js/scripts/uibinder.js"></script>
|
<script src="../../out/scripts/uibinder.js"></script>
|
||||||
<link type="text/css" rel="stylesheet" href="./assets/css/launcher.css">
|
<link type="text/css" rel="stylesheet" href="../css/launcher.css">
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
/*background: url('assets/images/backgrounds/<%=bkid%>.jpg') no-repeat center center fixed;*/
|
/*background: url('assets/images/backgrounds/<%=bkid%>.jpg') no-repeat center center fixed;*/
|
||||||
@ -38,8 +38,8 @@
|
|||||||
<div id="loadingContainer">
|
<div id="loadingContainer">
|
||||||
<div id="loadingContent">
|
<div id="loadingContent">
|
||||||
<div id="loadSpinnerContainer">
|
<div id="loadSpinnerContainer">
|
||||||
<img id="loadCenterImage" src="assets/images/LoadingSeal.png">
|
<img id="loadCenterImage" src="../images/LoadingSeal.png">
|
||||||
<img id="loadSpinnerImage" class="rotating" src="assets/images/LoadingText.png">
|
<img id="loadSpinnerImage" class="rotating" src="../images/LoadingText.png">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -2,7 +2,7 @@
|
|||||||
<div id="upper">
|
<div id="upper">
|
||||||
<div id="left">
|
<div id="left">
|
||||||
<div id="image_seal_container">
|
<div id="image_seal_container">
|
||||||
<img id="image_seal" src="assets/images/SealCircle.png"/>
|
<img id="image_seal" src="../images/SealCircle.png"/>
|
||||||
<div id="updateAvailableTooltip">Update Available</div>
|
<div id="updateAvailableTooltip">Update Available</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -216,5 +216,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="./assets/js/scripts/landing.js"></script>
|
<script src="../../out/scripts/landing.js"></script>
|
||||||
</div>
|
</div>
|
@ -7,7 +7,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="loginContent">
|
<div id="loginContent">
|
||||||
<form id="loginForm">
|
<form id="loginForm">
|
||||||
<img id="loginImageSeal" src="assets/images/SealCircle.png"/>
|
<img id="loginImageSeal" src="../images/SealCircle.png"/>
|
||||||
<span id="loginSubheader">MINECRAFT LOGIN</span>
|
<span id="loginSubheader">MINECRAFT LOGIN</span>
|
||||||
<div class="loginFieldContainer">
|
<div class="loginFieldContainer">
|
||||||
<svg id="profileSVG" class="loginSVG" viewBox="40 37 65.36 61.43">
|
<svg id="profileSVG" class="loginSVG" viewBox="40 37 65.36 61.43">
|
||||||
@ -61,5 +61,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<script src="./assets/js/scripts/login.js"></script>
|
<script src="../../out/scripts/login.js"></script>
|
||||||
</div>
|
</div>
|
@ -37,5 +37,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="./assets/js/scripts/overlay.js"></script>
|
<script src="../../out/scripts/overlay.js"></script>
|
||||||
</div>
|
</div>
|
@ -277,7 +277,7 @@
|
|||||||
<div id="settingsAboutCurrentContainer">
|
<div id="settingsAboutCurrentContainer">
|
||||||
<div id="settingsAboutCurrentContent">
|
<div id="settingsAboutCurrentContent">
|
||||||
<div id="settingsAboutCurrentHeadline">
|
<div id="settingsAboutCurrentHeadline">
|
||||||
<img id="settingsAboutLogo" src="./assets/images/SealCircle.png">
|
<img id="settingsAboutLogo" src="../images/SealCircle.png">
|
||||||
<span id="settingsAboutTitle">Helios Launcher</span>
|
<span id="settingsAboutTitle">Helios Launcher</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="settingsAboutCurrentVersion">
|
<div id="settingsAboutCurrentVersion">
|
||||||
@ -352,5 +352,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="./assets/js/scripts/settings.js"></script>
|
<script src="../../out/scripts/settings.js"></script>
|
||||||
</div>
|
</div>
|
@ -4,7 +4,7 @@
|
|||||||
<div class="cloudBottom"></div>
|
<div class="cloudBottom"></div>
|
||||||
</div>-->
|
</div>-->
|
||||||
<div id="welcomeContent">
|
<div id="welcomeContent">
|
||||||
<img id="welcomeImageSeal" src="assets/images/SealCircle.png"/>
|
<img id="welcomeImageSeal" src="../images/SealCircle.png"/>
|
||||||
<span id="welcomeHeader">WELCOME TO WESTEROSCRAFT</span>
|
<span id="welcomeHeader">WELCOME TO WESTEROSCRAFT</span>
|
||||||
<span id="welcomeDescription">Our mission is to recreate the universe imagined by author George RR Martin in his fantasy series, A Song of Ice and Fire. Through the collaborative effort of thousands of community members, we have sought to create Westeros as accurately and precisely as possible within Minecraft. The world we are creating is yours to explore. Journey from Dorne to Castle Black, and if you aren’t afraid, beyond the Wall itself, but best not delay. As the words of House Stark ominously warn: Winter is Coming.</span>
|
<span id="welcomeDescription">Our mission is to recreate the universe imagined by author George RR Martin in his fantasy series, A Song of Ice and Fire. Through the collaborative effort of thousands of community members, we have sought to create Westeros as accurately and precisely as possible within Minecraft. The world we are creating is yours to explore. Journey from Dorne to Castle Black, and if you aren’t afraid, beyond the Wall itself, but best not delay. As the words of House Stark ominously warn: Winter is Coming.</span>
|
||||||
<br>
|
<br>
|
||||||
@ -21,5 +21,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<script src="./assets/js/scripts/welcome.js"></script>
|
<script src="../../out/scripts/welcome.js"></script>
|
||||||
</div>
|
</div>
|
@ -1,4 +1,8 @@
|
|||||||
{
|
{
|
||||||
|
"__comment__": [
|
||||||
|
"This is an example only.",
|
||||||
|
"The file is hosted on the URL in DistroManager.js"
|
||||||
|
],
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"discord": {
|
"discord": {
|
||||||
"clientId": "385581240906022916",
|
"clientId": "385581240906022916",
|
170
package-lock.json
generated
@ -71,6 +71,27 @@
|
|||||||
"defer-to-connect": "^1.0.1"
|
"defer-to-connect": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/adm-zip": {
|
||||||
|
"version": "0.4.32",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.32.tgz",
|
||||||
|
"integrity": "sha512-hv1O7ySn+XvP5OeDQcJFWwVb2v+GFGO1A9aMTQ5B/bzxb7WW21O8iRhVdsKKr8QwuiagzGmPP+gsUAYZ6bRddQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/async": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/async/-/async-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-X4NAFPUAfVGKzP9YFuy/YkwlFLxXYwzmzdstKwQ2LHhoUGMyJdj8d5FNMX3/RLsQ0N5KhNY8CVYb0mKE/WnPHg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/caseless": {
|
||||||
|
"version": "0.12.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
|
||||||
|
"integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/color-name": {
|
"@types/color-name": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||||
@ -83,17 +104,103 @@
|
|||||||
"integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==",
|
"integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/discord-rpc": {
|
||||||
"version": "12.12.24",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.24.tgz",
|
"resolved": "https://registry.npmjs.org/@types/discord-rpc/-/discord-rpc-3.0.2.tgz",
|
||||||
"integrity": "sha512-1Ciqv9pqwVtW6FsIUKSZNB82E5Cu1I2bBTj1xuIHXLe/1zYLl3956Nbhg2MzSYHVfl9/rmanjbQIb7LibfCnug==",
|
"integrity": "sha512-n4g+vo/Co+KGat+wSoFil2fWiPMno93/zvTe+JlnECW03nciVBXeDhUSazaR1B1jO5vMnfWzEx0VqyB26XSCaw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/fs-extra": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-J00cVDALmi/hJOYsunyT52Hva5TnJeKP5yd1r+mH/ZU0mbYZflR0Z5kw5kITtKTRYMhm1JMClOFYdHnQszEvqw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/jquery": {
|
||||||
|
"version": "3.3.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.31.tgz",
|
||||||
|
"integrity": "sha512-Lz4BAJihoFw5nRzKvg4nawXPzutkv7wmfQ5121avptaSIXlDNJCUuxZxX/G+9EVidZGuO0UBlk+YjKbwRKJigg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/sizzle": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "12.12.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.25.tgz",
|
||||||
|
"integrity": "sha512-nf1LMGZvgFX186geVZR1xMZKKblJiRfiASTHw85zED2kI1yDKHDwTKMdkaCbTlXoRKlGKaDfYywt+V0As30q3w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/request": {
|
||||||
|
"version": "2.48.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.4.tgz",
|
||||||
|
"integrity": "sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/caseless": "*",
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/tough-cookie": "*",
|
||||||
|
"form-data": "^2.5.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"form-data": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.6",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/semver": {
|
"@types/semver": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.0.tgz",
|
||||||
"integrity": "sha512-1OzrNb4RuAzIT7wHSsgZRlMBlNsJl+do6UblR7JMW4oB7bbR+uBEYtUh7gEc/jM84GGilh68lSOokyM/zNUlBA=="
|
"integrity": "sha512-1OzrNb4RuAzIT7wHSsgZRlMBlNsJl+do6UblR7JMW4oB7bbR+uBEYtUh7gEc/jM84GGilh68lSOokyM/zNUlBA=="
|
||||||
},
|
},
|
||||||
|
"@types/sizzle": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/tar-fs": {
|
||||||
|
"version": "1.16.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/tar-fs/-/tar-fs-1.16.2.tgz",
|
||||||
|
"integrity": "sha512-eds/pbRf0Fe0EKmrHDbs8mRkfbjz2upAdoUfREw14dPboZaHqqZ1Y+uVeoakoPavpZMpj22nhUTAYkX5bz3DXA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/tar-stream": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/tar-stream": {
|
||||||
|
"version": "1.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/tar-stream/-/tar-stream-1.6.1.tgz",
|
||||||
|
"integrity": "sha512-pYCDOPuRE+4tXFk1rSMYiuI+kSrXiJ4av1bboQbkcEBA2rqwEWfIn9kdMSH+5nYu58WksHuxwx+7kVbtg0Le7w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/tough-cookie": {
|
||||||
|
"version": "2.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.6.tgz",
|
||||||
|
"integrity": "sha512-wHNBMnkoEBiRAd3s8KTKwIuO9biFtTf0LehITzBhSco+HQI0xkXZbLOD55SW3Aqw3oUkHstkm5SPv58yaAdFPQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/winreg": {
|
||||||
|
"version": "1.2.30",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/winreg/-/winreg-1.2.30.tgz",
|
||||||
|
"integrity": "sha1-kdZxDlNtNFucmwF8V0z2qNpkxRg=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
|
||||||
@ -260,9 +367,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-3.1.1.tgz",
|
||||||
"integrity": "sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ=="
|
"integrity": "sha512-X5Dj8hK1pJNC2Wzo2Rcp9FBVdJMGRR/S7V+lH46s8GVFhtbo5O4Le5GECCF/8PISVdkUA6mMPvgz7qTTD1rf1g=="
|
||||||
},
|
},
|
||||||
"async-exit-hook": {
|
"async-exit-hook": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@ -722,12 +829,12 @@
|
|||||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||||
},
|
},
|
||||||
"cross-env": {
|
"cross-env": {
|
||||||
"version": "6.0.3",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.0.tgz",
|
||||||
"integrity": "sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag==",
|
"integrity": "sha512-rV6M9ldNgmwP7bx5u6rZsTbYidzwvrwIYZnT08hSGLcQCcggofgFW+sNe7IhA1SRauPS0QuLbbX+wdNtpqE5CQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"cross-spawn": "^7.0.0"
|
"cross-spawn": "^7.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cross-spawn": {
|
"cross-spawn": {
|
||||||
@ -742,9 +849,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-key": {
|
"path-key": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
"integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==",
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"shebang-command": {
|
"shebang-command": {
|
||||||
@ -763,9 +870,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"which": {
|
"which": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
"integrity": "sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==",
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
@ -1069,9 +1176,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "7.1.9",
|
"version": "7.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-7.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-7.1.10.tgz",
|
||||||
"integrity": "sha512-gkzDr08XxRaNZhwPLRXYNXDaPuiAeCrRPcClowlDVfCLKi+kRNhzekZpfYUBq8DdZCD29D3rCtgc9IHjD/xuHw==",
|
"integrity": "sha512-UDpS2CfBN3yufCrbET5Ozw1XrLhuANHn+Zs8Vgl/BcBT/MoNbkY79nRFcyxj6pCFrEde9IoNOf+DgNp6altNxw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@electron/get": "^1.0.1",
|
"@electron/get": "^1.0.1",
|
||||||
@ -1632,6 +1739,17 @@
|
|||||||
"flatted": "^2.0.0",
|
"flatted": "^2.0.0",
|
||||||
"rimraf": "2.6.3",
|
"rimraf": "2.6.3",
|
||||||
"write": "1.0.3"
|
"write": "1.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"rimraf": {
|
||||||
|
"version": "2.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
|
||||||
|
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flatted": {
|
"flatted": {
|
||||||
@ -1857,6 +1975,12 @@
|
|||||||
"integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==",
|
"integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"helios-distribution-types": {
|
||||||
|
"version": "1.0.0-pre.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/helios-distribution-types/-/helios-distribution-types-1.0.0-pre.1.tgz",
|
||||||
|
"integrity": "sha512-rqmuLoiyZTLGH0rlklRpjTvVbBTtP/NOyQqCvBPaLwpU2xUX/Vxzmt3RE8k+OgN3BAUEVJ+4jhSLgCsCXJfx1g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"hosted-git-info": {
|
"hosted-git-info": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.2.tgz",
|
||||||
@ -2752,9 +2876,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rimraf": {
|
"rimraf": {
|
||||||
"version": "2.6.3",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz",
|
||||||
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
|
"integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^7.1.3"
|
||||||
|
24
package.json
@ -10,8 +10,11 @@
|
|||||||
"url": "https://github.com/dscalzi/HeliosLauncher/issues"
|
"url": "https://github.com/dscalzi/HeliosLauncher/issues"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "index.js",
|
"main": "out/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"clean": "rimraf out",
|
||||||
|
"tsc": "tsc",
|
||||||
|
"build": "npm run clean && npm run tsc",
|
||||||
"start": "electron .",
|
"start": "electron .",
|
||||||
"cilinux": "node build.js WINDOWS && node build.js LINUX",
|
"cilinux": "node build.js WINDOWS && node build.js LINUX",
|
||||||
"cidarwin": "node build.js MAC",
|
"cidarwin": "node build.js MAC",
|
||||||
@ -26,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "^0.4.13",
|
"adm-zip": "^0.4.13",
|
||||||
"async": "^3.1.0",
|
"async": "^3.1.1",
|
||||||
"discord-rpc": "3.1.0",
|
"discord-rpc": "3.1.0",
|
||||||
"ejs": "^3.0.1",
|
"ejs": "^3.0.1",
|
||||||
"ejs-electron": "^2.0.3",
|
"ejs-electron": "^2.0.3",
|
||||||
@ -40,10 +43,21 @@
|
|||||||
"winreg": "^1.2.4"
|
"winreg": "^1.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "^6.0.3",
|
"@types/adm-zip": "^0.4.32",
|
||||||
"electron": "^7.1.9",
|
"@types/async": "^3.0.5",
|
||||||
|
"@types/discord-rpc": "^3.0.2",
|
||||||
|
"@types/fs-extra": "^8.0.1",
|
||||||
|
"@types/jquery": "^3.3.31",
|
||||||
|
"@types/node": "^12.12.25",
|
||||||
|
"@types/request": "^2.48.4",
|
||||||
|
"@types/tar-fs": "^1.16.2",
|
||||||
|
"@types/winreg": "^1.2.30",
|
||||||
|
"cross-env": "^7.0.0",
|
||||||
|
"electron": "^7.1.10",
|
||||||
"electron-builder": "^22.2.0",
|
"electron-builder": "^22.2.0",
|
||||||
"eslint": "^6.8.0"
|
"eslint": "^6.8.0",
|
||||||
|
"helios-distribution-types": "1.0.0-pre.1",
|
||||||
|
"rimraf": "^3.0.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
let target = require('./assetguard')[process.argv[2]]
|
let target = require('./assetguard')[process.argv[2]]
|
||||||
|
|
||||||
if(target == null){
|
if(target == null){
|
||||||
process.send({context: 'error', data: null, error: 'Invalid class name'})
|
process.send!({context: 'error', data: null, error: 'Invalid class name'})
|
||||||
console.error('Invalid class name passed to argv[2], cannot continue.')
|
console.error('Invalid class name passed to argv[2], cannot continue.')
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
@ -15,17 +16,17 @@ console.log('AssetExec Started')
|
|||||||
process.on('unhandledRejection', r => console.log(r))
|
process.on('unhandledRejection', r => console.log(r))
|
||||||
|
|
||||||
function assignListeners(){
|
function assignListeners(){
|
||||||
tracker.on('validate', (data) => {
|
tracker.on('validate', (data: any) => {
|
||||||
process.send({context: 'validate', data})
|
process.send!({context: 'validate', data})
|
||||||
})
|
})
|
||||||
tracker.on('progress', (data, acc, total) => {
|
tracker.on('progress', (data: any, acc: number, total: number) => {
|
||||||
process.send({context: 'progress', data, value: acc, total, percent: parseInt((acc/total)*100)})
|
process.send!({context: 'progress', data, value: acc, total, percent: parseInt(((acc/total)*100) as unknown as string)})
|
||||||
})
|
})
|
||||||
tracker.on('complete', (data, ...args) => {
|
tracker.on('complete', (data: any, ...args: any[]) => {
|
||||||
process.send({context: 'complete', data, args})
|
process.send!({context: 'complete', data, args})
|
||||||
})
|
})
|
||||||
tracker.on('error', (data, error) => {
|
tracker.on('error', (data: any, error: any) => {
|
||||||
process.send({context: 'error', data, error})
|
process.send!({context: 'error', data, error})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,20 +42,20 @@ process.on('message', (msg) => {
|
|||||||
const res = f.apply(f === nS ? tracker : null, msg.argsArr)
|
const res = f.apply(f === nS ? tracker : null, msg.argsArr)
|
||||||
if(res instanceof Promise){
|
if(res instanceof Promise){
|
||||||
res.then((v) => {
|
res.then((v) => {
|
||||||
process.send({result: v, context: func})
|
process.send!({result: v, context: func})
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
process.send({result: err.message || err, context: func})
|
process.send!({result: err.message || err, context: func})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
process.send({result: res, context: func})
|
process.send!({result: res, context: func})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
process.send({context: 'error', data: null, error: `Function ${func} not found on ${process.argv[2]}`})
|
process.send!({context: 'error', data: null, error: `Function ${func} not found on ${process.argv[2]}`})
|
||||||
}
|
}
|
||||||
} else if(msg.task === 'changeContext'){
|
} else if(msg.task === 'changeContext'){
|
||||||
target = require('./assetguard')[msg.class]
|
target = require('./assetguard')[msg.class]
|
||||||
if(target == null){
|
if(target == null){
|
||||||
process.send({context: 'error', data: null, error: `Invalid class ${msg.class}`})
|
process.send!({context: 'error', data: null, error: `Invalid class ${msg.class}`})
|
||||||
} else {
|
} else {
|
||||||
tracker = new target(...(msg.args))
|
tracker = new target(...(msg.args))
|
||||||
assignListeners()
|
assignListeners()
|
@ -1,3 +1,8 @@
|
|||||||
|
import { LoggerUtil } from './loggerutil'
|
||||||
|
import { ConfigManager } from './configmanager'
|
||||||
|
import { Mojang } from './mojang/mojang'
|
||||||
|
import { SavedAccount } from './model/internal/config/SavedAccount'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AuthManager
|
* AuthManager
|
||||||
*
|
*
|
||||||
@ -8,12 +13,9 @@
|
|||||||
*
|
*
|
||||||
* @module authmanager
|
* @module authmanager
|
||||||
*/
|
*/
|
||||||
// Requirements
|
|
||||||
const ConfigManager = require('./configmanager')
|
const logger = new LoggerUtil('%c[AuthManager]', 'color: #a02d2a; font-weight: bold')
|
||||||
const LoggerUtil = require('./loggerutil')
|
const loggerSuccess = new LoggerUtil('%c[AuthManager]', 'color: #209b07; font-weight: bold')
|
||||||
const Mojang = require('./mojang')
|
|
||||||
const logger = LoggerUtil('%c[AuthManager]', 'color: #a02d2a; font-weight: bold')
|
|
||||||
const loggerSuccess = LoggerUtil('%c[AuthManager]', 'color: #209b07; font-weight: bold')
|
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ const loggerSuccess = LoggerUtil('%c[AuthManager]', 'color: #209b07; font-weight
|
|||||||
* @param {string} password The account password.
|
* @param {string} password The account password.
|
||||||
* @returns {Promise.<Object>} Promise which resolves the resolved authenticated account object.
|
* @returns {Promise.<Object>} Promise which resolves the resolved authenticated account object.
|
||||||
*/
|
*/
|
||||||
exports.addAccount = async function(username, password){
|
exports.addAccount = async function(username: string, password: string){
|
||||||
try {
|
try {
|
||||||
const session = await Mojang.authenticate(username, password, ConfigManager.getClientToken())
|
const session = await Mojang.authenticate(username, password, ConfigManager.getClientToken())
|
||||||
if(session.selectedProfile != null){
|
if(session.selectedProfile != null){
|
||||||
@ -52,10 +54,10 @@ exports.addAccount = async function(username, password){
|
|||||||
* @param {string} uuid The UUID of the account to be removed.
|
* @param {string} uuid The UUID of the account to be removed.
|
||||||
* @returns {Promise.<void>} Promise which resolves to void when the action is complete.
|
* @returns {Promise.<void>} Promise which resolves to void when the action is complete.
|
||||||
*/
|
*/
|
||||||
exports.removeAccount = async function(uuid){
|
exports.removeAccount = async function(uuid: string){
|
||||||
try {
|
try {
|
||||||
const authAcc = ConfigManager.getAuthAccount(uuid)
|
const authAcc = ConfigManager.getAuthAccount(uuid)
|
||||||
await Mojang.invalidate(authAcc.accessToken, ConfigManager.getClientToken())
|
await Mojang.invalidate(authAcc.accessToken, ConfigManager.getClientToken() as string)
|
||||||
ConfigManager.removeAuthAccount(uuid)
|
ConfigManager.removeAuthAccount(uuid)
|
||||||
ConfigManager.save()
|
ConfigManager.save()
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
@ -75,11 +77,11 @@ exports.removeAccount = async function(uuid){
|
|||||||
* otherwise false.
|
* otherwise false.
|
||||||
*/
|
*/
|
||||||
exports.validateSelected = async function(){
|
exports.validateSelected = async function(){
|
||||||
const current = ConfigManager.getSelectedAccount()
|
const current = ConfigManager.getSelectedAccount() as SavedAccount
|
||||||
const isValid = await Mojang.validate(current.accessToken, ConfigManager.getClientToken())
|
const isValid = await Mojang.validate(current.accessToken, ConfigManager.getClientToken() as string)
|
||||||
if(!isValid){
|
if(!isValid){
|
||||||
try {
|
try {
|
||||||
const session = await Mojang.refresh(current.accessToken, ConfigManager.getClientToken())
|
const session = await Mojang.refresh(current.accessToken, ConfigManager.getClientToken() as string)
|
||||||
ConfigManager.updateAuthAccount(current.uuid, session.accessToken)
|
ConfigManager.updateAuthAccount(current.uuid, session.accessToken)
|
||||||
ConfigManager.save()
|
ConfigManager.save()
|
||||||
} catch(err) {
|
} catch(err) {
|
703
src/configmanager.ts
Normal file
@ -0,0 +1,703 @@
|
|||||||
|
import { LoggerUtil } from './loggerutil'
|
||||||
|
import { join } from 'path'
|
||||||
|
import { pathExistsSync, writeFileSync, ensureDirSync, moveSync, readFileSync } from 'fs-extra'
|
||||||
|
import { totalmem } from 'os'
|
||||||
|
import { SavedAccount } from './model/internal/config/SavedAccount'
|
||||||
|
import { LauncherConfig } from './model/internal/config/LauncherConfig'
|
||||||
|
import { ModConfig } from './model/internal/config/ModConfig'
|
||||||
|
import { NewsCache } from './model/internal/config/NewsCache'
|
||||||
|
|
||||||
|
export class ConfigManager {
|
||||||
|
|
||||||
|
private static readonly logger = new LoggerUtil('%c[ConfigManager]', 'color: #a02d2a; font-weight: bold')
|
||||||
|
private static readonly sysRoot = process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Application Support' : process.env.HOME)
|
||||||
|
// TODO change
|
||||||
|
private static readonly dataPath = join(ConfigManager.sysRoot as string, '.westeroscraft')
|
||||||
|
|
||||||
|
// Forked processes do not have access to electron, so we have this workaround.
|
||||||
|
private static readonly launcherDir = process.env.CONFIG_DIRECT_PATH || require('electron').remote.app.getPath('userData')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the absolute path of the launcher directory.
|
||||||
|
*
|
||||||
|
* @returns {string} The absolute path of the launcher directory.
|
||||||
|
*/
|
||||||
|
public static getLauncherDirectory(){
|
||||||
|
return ConfigManager.launcherDir
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the launcher's data directory. This is where all files related
|
||||||
|
* to game launch are installed (common, instances, java, etc).
|
||||||
|
*
|
||||||
|
* @returns {string} The absolute path of the launcher's data directory.
|
||||||
|
*/
|
||||||
|
public static getDataDirectory(def = false){
|
||||||
|
return !def ? ConfigManager.config.settings.launcher.dataDirectory : ConfigManager.DEFAULT_CONFIG.settings.launcher.dataDirectory
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the new data directory.
|
||||||
|
*
|
||||||
|
* @param {string} dataDirectory The new data directory.
|
||||||
|
*/
|
||||||
|
public static setDataDirectory(dataDirectory: string){
|
||||||
|
ConfigManager.config.settings.launcher.dataDirectory = dataDirectory
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly configPath = join(ConfigManager.getLauncherDirectory(), 'config.json')
|
||||||
|
private static readonly configPathLEGACY = join(ConfigManager.dataPath, 'config.json') // TODO remove, it's been 1 year.
|
||||||
|
private static readonly firstLaunch = !pathExistsSync(ConfigManager.configPath) && !pathExistsSync(ConfigManager.configPathLEGACY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Three types of values:
|
||||||
|
* Static = Explicitly declared.
|
||||||
|
* Dynamic = Calculated by a private function.
|
||||||
|
* Resolved = Resolved externally, defaults to null.
|
||||||
|
*/
|
||||||
|
private static readonly DEFAULT_CONFIG: LauncherConfig = {
|
||||||
|
settings: {
|
||||||
|
java: {
|
||||||
|
minRAM: ConfigManager.resolveMinRAM(),
|
||||||
|
maxRAM: ConfigManager.resolveMaxRAM(), // Dynamic
|
||||||
|
executable: null,
|
||||||
|
jvmOptions: [
|
||||||
|
'-XX:+UseConcMarkSweepGC',
|
||||||
|
'-XX:+CMSIncrementalMode',
|
||||||
|
'-XX:-UseAdaptiveSizePolicy',
|
||||||
|
'-Xmn128M'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
game: {
|
||||||
|
resWidth: 1280,
|
||||||
|
resHeight: 720,
|
||||||
|
fullscreen: false,
|
||||||
|
autoConnect: true,
|
||||||
|
launchDetached: true
|
||||||
|
},
|
||||||
|
launcher: {
|
||||||
|
allowPrerelease: false,
|
||||||
|
dataDirectory: ConfigManager.dataPath
|
||||||
|
}
|
||||||
|
},
|
||||||
|
newsCache: {
|
||||||
|
date: null,
|
||||||
|
content: null,
|
||||||
|
dismissed: false
|
||||||
|
},
|
||||||
|
clientToken: null,
|
||||||
|
selectedServer: null, // Resolved
|
||||||
|
selectedAccount: null,
|
||||||
|
authenticationDatabase: {},
|
||||||
|
modConfigurations: []
|
||||||
|
}
|
||||||
|
|
||||||
|
private static config: LauncherConfig = null as unknown as LauncherConfig
|
||||||
|
|
||||||
|
public static getAbsoluteMinRAM(){
|
||||||
|
const mem = totalmem()
|
||||||
|
return mem >= 6000000000 ? 3 : 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getAbsoluteMaxRAM(){
|
||||||
|
const mem = totalmem()
|
||||||
|
const gT16 = mem-16000000000
|
||||||
|
return Math.floor((mem-1000000000-(gT16 > 0 ? (Number.parseInt(gT16/8 as unknown as string) + 16000000000/4) : mem/4))/1000000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static resolveMaxRAM(){
|
||||||
|
const mem = totalmem()
|
||||||
|
return mem >= 8000000000 ? '4G' : (mem >= 6000000000 ? '3G' : '2G')
|
||||||
|
}
|
||||||
|
|
||||||
|
private static resolveMinRAM(){
|
||||||
|
return ConfigManager.resolveMaxRAM()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Persistance Utility Functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the current configuration to a file.
|
||||||
|
*/
|
||||||
|
public static save(){
|
||||||
|
writeFileSync(ConfigManager.configPath, JSON.stringify(ConfigManager.config, null, 4), 'UTF-8')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the configuration into memory. If a configuration file exists,
|
||||||
|
* that will be read and saved. Otherwise, a default configuration will
|
||||||
|
* be generated. Note that "resolved" values default to null and will
|
||||||
|
* need to be externally assigned.
|
||||||
|
*/
|
||||||
|
public static load(){
|
||||||
|
let doLoad = true
|
||||||
|
|
||||||
|
if(!pathExistsSync(ConfigManager.configPath)){
|
||||||
|
// Create all parent directories.
|
||||||
|
ensureDirSync(join(ConfigManager.configPath, '..'))
|
||||||
|
if(pathExistsSync(ConfigManager.configPathLEGACY)){
|
||||||
|
moveSync(ConfigManager.configPathLEGACY, ConfigManager.configPath)
|
||||||
|
} else {
|
||||||
|
doLoad = false
|
||||||
|
ConfigManager.config = ConfigManager.DEFAULT_CONFIG
|
||||||
|
ConfigManager.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(doLoad){
|
||||||
|
let doValidate = false
|
||||||
|
try {
|
||||||
|
ConfigManager.config = JSON.parse(readFileSync(ConfigManager.configPath, 'UTF-8'))
|
||||||
|
doValidate = true
|
||||||
|
} catch (err){
|
||||||
|
ConfigManager.logger.error(err)
|
||||||
|
ConfigManager.logger.log('Configuration file contains malformed JSON or is corrupt.')
|
||||||
|
ConfigManager.logger.log('Generating a new configuration file.')
|
||||||
|
ensureDirSync(join(ConfigManager.configPath, '..'))
|
||||||
|
ConfigManager.config = ConfigManager.DEFAULT_CONFIG
|
||||||
|
ConfigManager.save()
|
||||||
|
}
|
||||||
|
if(doValidate){
|
||||||
|
ConfigManager.config = ConfigManager.validateKeySet(ConfigManager.DEFAULT_CONFIG, ConfigManager.config)
|
||||||
|
ConfigManager.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigManager.logger.log('Successfully Loaded')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean} Whether or not the manager has been loaded.
|
||||||
|
*/
|
||||||
|
public static isLoaded(): boolean {
|
||||||
|
return ConfigManager.config != null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that the destination object has at least every field
|
||||||
|
* present in the source object. Assign a default value otherwise.
|
||||||
|
*
|
||||||
|
* @param {Object} srcObj The source object to reference against.
|
||||||
|
* @param {Object} destObj The destination object.
|
||||||
|
* @returns {Object} A validated destination object.
|
||||||
|
*/
|
||||||
|
private static validateKeySet(srcObj: any, destObj: any){
|
||||||
|
if(srcObj == null){
|
||||||
|
srcObj = {}
|
||||||
|
}
|
||||||
|
const validationBlacklist = ['authenticationDatabase']
|
||||||
|
const keys = Object.keys(srcObj)
|
||||||
|
for(let i=0; i<keys.length; i++){
|
||||||
|
if(typeof destObj[keys[i]] === 'undefined'){
|
||||||
|
destObj[keys[i]] = srcObj[keys[i]]
|
||||||
|
} else if(typeof srcObj[keys[i]] === 'object' && srcObj[keys[i]] != null && !(srcObj[keys[i]] instanceof Array) && validationBlacklist.indexOf(keys[i]) === -1){
|
||||||
|
destObj[keys[i]] = ConfigManager.validateKeySet(srcObj[keys[i]], destObj[keys[i]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return destObj
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if this is the first time the user has launched the
|
||||||
|
* application. This is determined by the existance of the data path.
|
||||||
|
*
|
||||||
|
* @returns {boolean} True if this is the first launch, otherwise false.
|
||||||
|
*/
|
||||||
|
public static isFirstLaunch(): boolean {
|
||||||
|
return ConfigManager.firstLaunch
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the folder in the OS temp directory which we
|
||||||
|
* will use to extract and store native dependencies for game launch.
|
||||||
|
*
|
||||||
|
* @returns {string} The name of the folder.
|
||||||
|
*/
|
||||||
|
public static getTempNativeFolder(): string {
|
||||||
|
return 'HeliosLauncherNatives'
|
||||||
|
}
|
||||||
|
|
||||||
|
// System Settings (Unconfigurable on UI)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the news cache to determine
|
||||||
|
* whether or not there is newer news.
|
||||||
|
*
|
||||||
|
* @returns {NewsCache} The news cache object.
|
||||||
|
*/
|
||||||
|
public static getNewsCache(): NewsCache {
|
||||||
|
return ConfigManager.config.newsCache
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the new news cache object.
|
||||||
|
*
|
||||||
|
* @param {Object} newsCache The new news cache object.
|
||||||
|
*/
|
||||||
|
public static setNewsCache(newsCache: any): void {
|
||||||
|
ConfigManager.config.newsCache = newsCache
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether or not the news has been dismissed (checked)
|
||||||
|
*
|
||||||
|
* @param {boolean} dismissed Whether or not the news has been dismissed (checked).
|
||||||
|
*/
|
||||||
|
public static setNewsCacheDismissed(dismissed: boolean): void {
|
||||||
|
ConfigManager.config.newsCache.dismissed = dismissed
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the common directory for shared
|
||||||
|
* game files (assets, libraries, etc).
|
||||||
|
*
|
||||||
|
* @returns {string} The launcher's common directory.
|
||||||
|
*/
|
||||||
|
public static getCommonDirectory(): string {
|
||||||
|
return join(ConfigManager.getDataDirectory(), 'common')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the instance directory for the per
|
||||||
|
* server game directories.
|
||||||
|
*
|
||||||
|
* @returns {string} The launcher's instance directory.
|
||||||
|
*/
|
||||||
|
public static getInstanceDirectory(): string {
|
||||||
|
return join(ConfigManager.getDataDirectory(), 'instances')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the launcher's Client Token.
|
||||||
|
* There is no default client token.
|
||||||
|
*
|
||||||
|
* @returns {string | null} The launcher's Client Token.
|
||||||
|
*/
|
||||||
|
public static getClientToken(): string | null {
|
||||||
|
return ConfigManager.config.clientToken
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the launcher's Client Token.
|
||||||
|
*
|
||||||
|
* @param {string} clientToken The launcher's new Client Token.
|
||||||
|
*/
|
||||||
|
public static setClientToken(clientToken: string): void {
|
||||||
|
ConfigManager.config.clientToken = clientToken
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the ID of the selected serverpack.
|
||||||
|
*
|
||||||
|
* @param {boolean} def Optional. If true, the default value will be returned.
|
||||||
|
* @returns {string | null} The ID of the selected serverpack.
|
||||||
|
*/
|
||||||
|
public static getSelectedServer(def = false): string | null {
|
||||||
|
return !def ? ConfigManager.config.selectedServer : ConfigManager.DEFAULT_CONFIG.selectedServer
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the ID of the selected serverpack.
|
||||||
|
*
|
||||||
|
* @param {string} serverID The ID of the new selected serverpack.
|
||||||
|
*/
|
||||||
|
public static setSelectedServer(serverID: string): void {
|
||||||
|
ConfigManager.config.selectedServer = serverID
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of each account currently authenticated by the launcher.
|
||||||
|
*
|
||||||
|
* @returns {Array.<SavedAccount>} An array of each stored authenticated account.
|
||||||
|
*/
|
||||||
|
public static getAuthAccounts(): {[uuid: string]: SavedAccount} {
|
||||||
|
return ConfigManager.config.authenticationDatabase
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the authenticated account with the given uuid. Value may
|
||||||
|
* be null.
|
||||||
|
*
|
||||||
|
* @param {string} uuid The uuid of the authenticated account.
|
||||||
|
* @returns {SavedAccount} The authenticated account with the given uuid.
|
||||||
|
*/
|
||||||
|
public static getAuthAccount(uuid: string): SavedAccount {
|
||||||
|
return ConfigManager.config.authenticationDatabase[uuid]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the access token of an authenticated account.
|
||||||
|
*
|
||||||
|
* @param {string} uuid The uuid of the authenticated account.
|
||||||
|
* @param {string} accessToken The new Access Token.
|
||||||
|
*
|
||||||
|
* @returns {SavedAccount} The authenticated account object created by this action.
|
||||||
|
*/
|
||||||
|
public static updateAuthAccount(uuid: string, accessToken: string): SavedAccount {
|
||||||
|
ConfigManager.config.authenticationDatabase[uuid].accessToken = accessToken
|
||||||
|
return ConfigManager.config.authenticationDatabase[uuid]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an authenticated account to the database to be stored.
|
||||||
|
*
|
||||||
|
* @param {string} uuid The uuid of the authenticated account.
|
||||||
|
* @param {string} accessToken The accessToken of the authenticated account.
|
||||||
|
* @param {string} username The username (usually email) of the authenticated account.
|
||||||
|
* @param {string} displayName The in game name of the authenticated account.
|
||||||
|
*
|
||||||
|
* @returns {SavedAccount} The authenticated account object created by this action.
|
||||||
|
*/
|
||||||
|
public static addAuthAccount(
|
||||||
|
uuid: string,
|
||||||
|
accessToken: string,
|
||||||
|
username: string,
|
||||||
|
displayName: string
|
||||||
|
): SavedAccount {
|
||||||
|
ConfigManager.config.selectedAccount = uuid
|
||||||
|
ConfigManager.config.authenticationDatabase[uuid] = {
|
||||||
|
accessToken,
|
||||||
|
username: username.trim(),
|
||||||
|
uuid: uuid.trim(),
|
||||||
|
displayName: displayName.trim()
|
||||||
|
}
|
||||||
|
return ConfigManager.config.authenticationDatabase[uuid]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an authenticated account from the database. If the account
|
||||||
|
* was also the selected account, a new one will be selected. If there
|
||||||
|
* are no accounts, the selected account will be null.
|
||||||
|
*
|
||||||
|
* @param {string} uuid The uuid of the authenticated account.
|
||||||
|
*
|
||||||
|
* @returns {boolean} True if the account was removed, false if it never existed.
|
||||||
|
*/
|
||||||
|
public static removeAuthAccount(uuid: string): boolean {
|
||||||
|
if(ConfigManager.config.authenticationDatabase[uuid] != null){
|
||||||
|
delete ConfigManager.config.authenticationDatabase[uuid]
|
||||||
|
if(ConfigManager.config.selectedAccount === uuid){
|
||||||
|
const keys = Object.keys(ConfigManager.config.authenticationDatabase)
|
||||||
|
if(keys.length > 0){
|
||||||
|
ConfigManager.config.selectedAccount = keys[0]
|
||||||
|
} else {
|
||||||
|
ConfigManager.config.selectedAccount = null
|
||||||
|
ConfigManager.config.clientToken = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently selected authenticated account.
|
||||||
|
*
|
||||||
|
* @returns {SavedAccount | null} The selected authenticated account.
|
||||||
|
*/
|
||||||
|
public static getSelectedAccount(): SavedAccount | null {
|
||||||
|
return ConfigManager.config.selectedAccount == null ?
|
||||||
|
null :
|
||||||
|
ConfigManager.config.authenticationDatabase[ConfigManager.config.selectedAccount]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected authenticated account.
|
||||||
|
*
|
||||||
|
* @param {string} uuid The UUID of the account which is to be set
|
||||||
|
* as the selected account.
|
||||||
|
*
|
||||||
|
* @returns {SavedAccount} The selected authenticated account.
|
||||||
|
*/
|
||||||
|
public static setSelectedAccount(uuid: string): SavedAccount {
|
||||||
|
const authAcc = ConfigManager.config.authenticationDatabase[uuid]
|
||||||
|
if(authAcc != null) {
|
||||||
|
ConfigManager.config.selectedAccount = uuid
|
||||||
|
}
|
||||||
|
return authAcc
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of each mod configuration currently stored.
|
||||||
|
*
|
||||||
|
* @returns {Array.<ModConfig>} An array of each stored mod configuration.
|
||||||
|
*/
|
||||||
|
public static getModConfigurations(): ModConfig[] {
|
||||||
|
return ConfigManager.config.modConfigurations
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the array of stored mod configurations.
|
||||||
|
*
|
||||||
|
* @param {Array.<ModConfig>} configurations An array of mod configurations.
|
||||||
|
*/
|
||||||
|
public static setModConfigurations(configurations: ModConfig[]): void {
|
||||||
|
ConfigManager.config.modConfigurations = configurations
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the mod configuration for a specific server.
|
||||||
|
*
|
||||||
|
* @param {string} serverid The id of the server.
|
||||||
|
* @returns {ModConfig | null} The mod configuration for the given server.
|
||||||
|
*/
|
||||||
|
public static getModConfiguration(serverid: string): ModConfig | null {
|
||||||
|
const cfgs = ConfigManager.config.modConfigurations
|
||||||
|
for(let i=0; i<cfgs.length; i++){
|
||||||
|
if(cfgs[i].id === serverid){
|
||||||
|
return cfgs[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the mod configuration for a specific server. This overrides any existing value.
|
||||||
|
*
|
||||||
|
* @param {string} serverid The id of the server for the given mod configuration.
|
||||||
|
* @param {ModConfig} configuration The mod configuration for the given server.
|
||||||
|
*/
|
||||||
|
public static setModConfiguration(serverid: string, configuration: ModConfig): void {
|
||||||
|
const cfgs = ConfigManager.config.modConfigurations
|
||||||
|
for(let i=0; i<cfgs.length; i++){
|
||||||
|
if(cfgs[i].id === serverid){
|
||||||
|
cfgs[i] = configuration
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cfgs.push(configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// User Configurable Settings
|
||||||
|
|
||||||
|
// Java Settings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the minimum amount of memory for JVM initialization. This value
|
||||||
|
* contains the units of memory. For example, '5G' = 5 GigaBytes, '1024M' =
|
||||||
|
* 1024 MegaBytes, etc.
|
||||||
|
*
|
||||||
|
* @param {boolean} def Optional. If true, the default value will be returned.
|
||||||
|
* @returns {string} The minimum amount of memory for JVM initialization.
|
||||||
|
*/
|
||||||
|
public static getMinRAM(def = false): string {
|
||||||
|
return !def ? ConfigManager.config.settings.java.minRAM : ConfigManager.DEFAULT_CONFIG.settings.java.minRAM
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the minimum amount of memory for JVM initialization. This value should
|
||||||
|
* contain the units of memory. For example, '5G' = 5 GigaBytes, '1024M' =
|
||||||
|
* 1024 MegaBytes, etc.
|
||||||
|
*
|
||||||
|
* @param {string} minRAM The new minimum amount of memory for JVM initialization.
|
||||||
|
*/
|
||||||
|
public static setMinRAM(minRAM: string): void {
|
||||||
|
ConfigManager.config.settings.java.minRAM = minRAM
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the maximum amount of memory for JVM initialization. This value
|
||||||
|
* contains the units of memory. For example, '5G' = 5 GigaBytes, '1024M' =
|
||||||
|
* 1024 MegaBytes, etc.
|
||||||
|
*
|
||||||
|
* @param {boolean} def Optional. If true, the default value will be returned.
|
||||||
|
* @returns {string} The maximum amount of memory for JVM initialization.
|
||||||
|
*/
|
||||||
|
public static getMaxRAM(def = false): string {
|
||||||
|
return !def ? ConfigManager.config.settings.java.maxRAM : ConfigManager.resolveMaxRAM()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum amount of memory for JVM initialization. This value should
|
||||||
|
* contain the units of memory. For example, '5G' = 5 GigaBytes, '1024M' =
|
||||||
|
* 1024 MegaBytes, etc.
|
||||||
|
*
|
||||||
|
* @param {string} maxRAM The new maximum amount of memory for JVM initialization.
|
||||||
|
*/
|
||||||
|
public static setMaxRAM(maxRAM: string): void {
|
||||||
|
ConfigManager.config.settings.java.maxRAM = maxRAM
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the path of the Java Executable.
|
||||||
|
*
|
||||||
|
* This is a resolved configuration value and defaults to null until externally assigned.
|
||||||
|
*
|
||||||
|
* @returns {string | null} The path of the Java Executable.
|
||||||
|
*/
|
||||||
|
public static getJavaExecutable(): string | null {
|
||||||
|
return ConfigManager.config.settings.java.executable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the path of the Java Executable.
|
||||||
|
*
|
||||||
|
* @param {string} executable The new path of the Java Executable.
|
||||||
|
*/
|
||||||
|
public static setJavaExecutable(executable: string): void {
|
||||||
|
ConfigManager.config.settings.java.executable = executable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the additional arguments for JVM initialization. Required arguments,
|
||||||
|
* such as memory allocation, will be dynamically resolved and will not be included
|
||||||
|
* in this value.
|
||||||
|
*
|
||||||
|
* @param {boolean} def Optional. If true, the default value will be returned.
|
||||||
|
* @returns {Array.<string>} An array of the additional arguments for JVM initialization.
|
||||||
|
*/
|
||||||
|
public static getJVMOptions(def = false): string[] {
|
||||||
|
return !def ? ConfigManager.config.settings.java.jvmOptions : ConfigManager.DEFAULT_CONFIG.settings.java.jvmOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the additional arguments for JVM initialization. Required arguments,
|
||||||
|
* such as memory allocation, will be dynamically resolved and should not be
|
||||||
|
* included in this value.
|
||||||
|
*
|
||||||
|
* @param {Array.<string>} jvmOptions An array of the new additional arguments for JVM
|
||||||
|
* initialization.
|
||||||
|
*/
|
||||||
|
public static setJVMOptions(jvmOptions: string[]): void {
|
||||||
|
ConfigManager.config.settings.java.jvmOptions = jvmOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Game Settings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the width of the game window.
|
||||||
|
*
|
||||||
|
* @param {boolean} def Optional. If true, the default value will be returned.
|
||||||
|
* @returns {number} The width of the game window.
|
||||||
|
*/
|
||||||
|
public static getGameWidth(def = false): number {
|
||||||
|
return !def ? ConfigManager.config.settings.game.resWidth : ConfigManager.DEFAULT_CONFIG.settings.game.resWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the width of the game window.
|
||||||
|
*
|
||||||
|
* @param {number} resWidth The new width of the game window.
|
||||||
|
*/
|
||||||
|
public static setGameWidth(resWidth: number): void {
|
||||||
|
ConfigManager.config.settings.game.resWidth = Number.parseInt(resWidth as unknown as string)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a potential new width value.
|
||||||
|
*
|
||||||
|
* @param {number} resWidth The width value to validate.
|
||||||
|
* @returns {boolean} Whether or not the value is valid.
|
||||||
|
*/
|
||||||
|
public static validateGameWidth(resWidth: number): boolean {
|
||||||
|
const nVal = Number.parseInt(resWidth as unknown as string)
|
||||||
|
return Number.isInteger(nVal) && nVal >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the height of the game window.
|
||||||
|
*
|
||||||
|
* @param {boolean} def Optional. If true, the default value will be returned.
|
||||||
|
* @returns {number} The height of the game window.
|
||||||
|
*/
|
||||||
|
public static getGameHeight(def = false): number {
|
||||||
|
return !def ? ConfigManager.config.settings.game.resHeight : ConfigManager.DEFAULT_CONFIG.settings.game.resHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the height of the game window.
|
||||||
|
*
|
||||||
|
* @param {number} resHeight The new height of the game window.
|
||||||
|
*/
|
||||||
|
public static setGameHeight(resHeight: number): void {
|
||||||
|
ConfigManager.config.settings.game.resHeight = Number.parseInt(resHeight as unknown as string)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a potential new height value.
|
||||||
|
*
|
||||||
|
* @param {number} resHeight The height value to validate.
|
||||||
|
* @returns {boolean} Whether or not the value is valid.
|
||||||
|
*/
|
||||||
|
public static validateGameHeight(resHeight: number): boolean {
|
||||||
|
const nVal = Number.parseInt(resHeight as unknown as string)
|
||||||
|
return Number.isInteger(nVal) && nVal >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the game should be launched in fullscreen mode.
|
||||||
|
*
|
||||||
|
* @param {boolean} def Optional. If true, the default value will be returned.
|
||||||
|
* @returns {boolean} Whether or not the game is set to launch in fullscreen mode.
|
||||||
|
*/
|
||||||
|
public static getFullscreen(def = false): boolean {
|
||||||
|
return !def ? ConfigManager.config.settings.game.fullscreen : ConfigManager.DEFAULT_CONFIG.settings.game.fullscreen
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the status of if the game should be launched in fullscreen mode.
|
||||||
|
*
|
||||||
|
* @param {boolean} fullscreen Whether or not the game should launch in fullscreen mode.
|
||||||
|
*/
|
||||||
|
public static setFullscreen(fullscreen: boolean): void {
|
||||||
|
ConfigManager.config.settings.game.fullscreen = fullscreen
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the game should auto connect to servers.
|
||||||
|
*
|
||||||
|
* @param {boolean} def Optional. If true, the default value will be returned.
|
||||||
|
* @returns {boolean} Whether or not the game should auto connect to servers.
|
||||||
|
*/
|
||||||
|
public static getAutoConnect(def = false): boolean {
|
||||||
|
return !def ? ConfigManager.config.settings.game.autoConnect : ConfigManager.DEFAULT_CONFIG.settings.game.autoConnect
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the status of whether or not the game should auto connect to servers.
|
||||||
|
*
|
||||||
|
* @param {boolean} autoConnect Whether or not the game should auto connect to servers.
|
||||||
|
*/
|
||||||
|
public static setAutoConnect(autoConnect: boolean): void {
|
||||||
|
ConfigManager.config.settings.game.autoConnect = autoConnect
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the game should launch as a detached process.
|
||||||
|
*
|
||||||
|
* @param {boolean} def Optional. If true, the default value will be returned.
|
||||||
|
* @returns {boolean} Whether or not the game will launch as a detached process.
|
||||||
|
*/
|
||||||
|
public static getLaunchDetached(def = false): boolean {
|
||||||
|
return !def ? ConfigManager.config.settings.game.launchDetached : ConfigManager.DEFAULT_CONFIG.settings.game.launchDetached
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the status of whether or not the game should launch as a detached process.
|
||||||
|
*
|
||||||
|
* @param {boolean} launchDetached Whether or not the game should launch as a detached process.
|
||||||
|
*/
|
||||||
|
public static setLaunchDetached(launchDetached: boolean): void {
|
||||||
|
ConfigManager.config.settings.game.launchDetached = launchDetached
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launcher Settings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the launcher should download prerelease versions.
|
||||||
|
*
|
||||||
|
* @param {boolean} def Optional. If true, the default value will be returned.
|
||||||
|
* @returns {boolean} Whether or not the launcher should download prerelease versions.
|
||||||
|
*/
|
||||||
|
public static getAllowPrerelease(def = false): boolean {
|
||||||
|
return !def ? ConfigManager.config.settings.launcher.allowPrerelease : ConfigManager.DEFAULT_CONFIG.settings.launcher.allowPrerelease
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the status of Whether or not the launcher should download prerelease versions.
|
||||||
|
*
|
||||||
|
* @param {boolean} launchDetached Whether or not the launcher should download prerelease versions.
|
||||||
|
*/
|
||||||
|
public static setAllowPrerelease(allowPrerelease: boolean): void {
|
||||||
|
ConfigManager.config.settings.launcher.allowPrerelease = allowPrerelease
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,12 +1,14 @@
|
|||||||
|
import { LoggerUtil } from './loggerutil'
|
||||||
|
import { Client, Presence } from 'discord-rpc'
|
||||||
|
|
||||||
// Work in progress
|
// Work in progress
|
||||||
const logger = require('./loggerutil')('%c[DiscordWrapper]', 'color: #7289da; font-weight: bold')
|
const logger = new LoggerUtil('%c[DiscordWrapper]', 'color: #7289da; font-weight: bold')
|
||||||
|
|
||||||
const {Client} = require('discord-rpc')
|
let client: Client
|
||||||
|
let activity: Presence
|
||||||
|
|
||||||
let client
|
// TODO types for these settings
|
||||||
let activity
|
export function initRPC(genSettings: any, servSettings: any, initialDetails = 'Waiting for Client..'){
|
||||||
|
|
||||||
exports.initRPC = function(genSettings, servSettings, initialDetails = 'Waiting for Client..'){
|
|
||||||
client = new Client({ transport: 'ipc' })
|
client = new Client({ transport: 'ipc' })
|
||||||
|
|
||||||
activity = {
|
activity = {
|
||||||
@ -34,15 +36,15 @@ exports.initRPC = function(genSettings, servSettings, initialDetails = 'Waiting
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updateDetails = function(details){
|
export function updateDetails(details: string){
|
||||||
activity.details = details
|
activity.details = details
|
||||||
client.setActivity(activity)
|
client.setActivity(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.shutdownRPC = function(){
|
export function shutdownRPC(){
|
||||||
if(!client) return
|
if(!client) return
|
||||||
client.clearActivity()
|
client.clearActivity()
|
||||||
client.destroy()
|
client.destroy()
|
||||||
client = null
|
client = null as unknown as Client // TODO cleanup
|
||||||
activity = null
|
activity = null as unknown as Presence // TODO cleanup
|
||||||
}
|
}
|
296
src/distromanager.ts
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
import request from 'request'
|
||||||
|
import { Distribution, Module, Type, TypeMetadata, Server } from 'helios-distribution-types'
|
||||||
|
import { readJson, writeJson } from 'fs-extra'
|
||||||
|
import { join } from 'path'
|
||||||
|
import { LoggerUtil } from './loggerutil'
|
||||||
|
import { ConfigManager } from './configmanager'
|
||||||
|
|
||||||
|
const logger = new LoggerUtil('%c[DistroManager]', 'color: #a02d2a; font-weight: bold')
|
||||||
|
|
||||||
|
interface ArtifactMeta {
|
||||||
|
group: string
|
||||||
|
artifact: string
|
||||||
|
version: string
|
||||||
|
classifier?: string
|
||||||
|
extension: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ModuleWrapper {
|
||||||
|
|
||||||
|
private artifactMeta: ArtifactMeta
|
||||||
|
private subModules: ModuleWrapper[] = []
|
||||||
|
|
||||||
|
constructor(public module: Module, private serverId: string) {
|
||||||
|
this.artifactMeta = this.resolveMetaData()
|
||||||
|
this.resolveArtifactPath()
|
||||||
|
this.resolveRequired()
|
||||||
|
if (this.module.subModules != null) {
|
||||||
|
this.subModules = this.module.subModules.map(mdl => new ModuleWrapper(mdl, serverId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveMetaData(): ArtifactMeta {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const m0 = this.module.id.split('@')
|
||||||
|
const m1 = m0[0].split(':')
|
||||||
|
|
||||||
|
return {
|
||||||
|
group: m1[0] || '???',
|
||||||
|
artifact: m1[1] || '???',
|
||||||
|
version: m1[2] || '???',
|
||||||
|
classifier: m1[3] || undefined,
|
||||||
|
extension: m0[1] || TypeMetadata[this.module.type].defaultExtension || 'undefined'
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Improper ID for module', this.module.id, err)
|
||||||
|
return {
|
||||||
|
group: '???',
|
||||||
|
artifact: '???',
|
||||||
|
version: '???',
|
||||||
|
classifier: undefined,
|
||||||
|
extension: '???'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveArtifactPath(): void {
|
||||||
|
const relativePath = this.module.artifact.path == null ? join(
|
||||||
|
...this.artifactMeta.group.split('.'),
|
||||||
|
this.artifactMeta.artifact,
|
||||||
|
this.artifactMeta.version,
|
||||||
|
`${this.artifactMeta.artifact}-${this.artifactMeta.version}${this.artifactMeta.classifier != undefined ? `-${this.artifactMeta.classifier}` : ''}.${this.artifactMeta.extension}`
|
||||||
|
) : this.module.artifact.path
|
||||||
|
|
||||||
|
switch (this.module.type){
|
||||||
|
case Type.Library:
|
||||||
|
case Type.ForgeHosted:
|
||||||
|
case Type.LiteLoader:
|
||||||
|
this.module.artifact.path = join(ConfigManager.getCommonDirectory(), 'libraries', relativePath)
|
||||||
|
break
|
||||||
|
case Type.ForgeMod:
|
||||||
|
case Type.LiteMod:
|
||||||
|
this.module.artifact.path = join(ConfigManager.getCommonDirectory(), 'modstore', relativePath)
|
||||||
|
break
|
||||||
|
case Type.VersionManifest:
|
||||||
|
this.module.artifact.path = join(ConfigManager.getCommonDirectory(), 'versions', this.module.id, `${this.module.id}.json`)
|
||||||
|
break
|
||||||
|
case Type.File:
|
||||||
|
default:
|
||||||
|
this.module.artifact.path = join(ConfigManager.getInstanceDirectory(), this.serverId, relativePath)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveRequired(): void {
|
||||||
|
if (this.module.required == null) {
|
||||||
|
this.module.required = {
|
||||||
|
value: true,
|
||||||
|
def: true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.module.required.value == null) {
|
||||||
|
this.module.required.value = true
|
||||||
|
}
|
||||||
|
if (this.module.required.def == null) {
|
||||||
|
this.module.required.def = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string} The maven identifier of this module's artifact.
|
||||||
|
*/
|
||||||
|
public getArtifact(): string {
|
||||||
|
return this.artifactMeta.artifact
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string} The maven group of this module's artifact.
|
||||||
|
*/
|
||||||
|
public getGroup(): string {
|
||||||
|
return this.artifactMeta.group
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string} The version of this module's artifact.
|
||||||
|
*/
|
||||||
|
public getVersion(): string {
|
||||||
|
return this.artifactMeta.version
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string | undefined} The classifier of this module's artifact
|
||||||
|
*/
|
||||||
|
public getClassifier(): string | undefined {
|
||||||
|
return this.artifactMeta.classifier
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string} The extension of this module's artifact.
|
||||||
|
*/
|
||||||
|
public getExtension(): string {
|
||||||
|
return this.artifactMeta.extension
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string} The identifier without he version or extension.
|
||||||
|
*/
|
||||||
|
public getVersionlessID(): string {
|
||||||
|
return this.artifactMeta.group + ':' + this.artifactMeta.artifact
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string} The identifier without the extension.
|
||||||
|
*/
|
||||||
|
public getExtensionlessID(): string {
|
||||||
|
return this.module.id.split('@')[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean} Whether or not this module has sub modules.
|
||||||
|
*/
|
||||||
|
public hasSubModules(): boolean {
|
||||||
|
return this.module.subModules != null
|
||||||
|
}
|
||||||
|
|
||||||
|
public getWrappedSubmodules(): ModuleWrapper[] {
|
||||||
|
return this.subModules
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ServerWrapper {
|
||||||
|
|
||||||
|
private modules: ModuleWrapper[] = []
|
||||||
|
|
||||||
|
constructor(public server: Server) {
|
||||||
|
this.server.modules.map(mdl => new ModuleWrapper(mdl, server.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
public getWrappedModules() {
|
||||||
|
return this.modules
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DistributionWrapper {
|
||||||
|
|
||||||
|
private mainServer: ServerWrapper | null = null
|
||||||
|
private servers: ServerWrapper[]
|
||||||
|
|
||||||
|
constructor(public distro: Distribution) {
|
||||||
|
this.servers = this.distro.servers.map(serv => new ServerWrapper(serv))
|
||||||
|
this.resolveMainServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveMainServer(): void {
|
||||||
|
|
||||||
|
for(const serv of this.servers){
|
||||||
|
if(serv.server.mainServer){
|
||||||
|
this.mainServer = serv
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no server declares default_selected, default to the first one declared.
|
||||||
|
this.mainServer = (this.servers.length > 0) ? this.servers[0] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
public getServer(id: string): ServerWrapper | null {
|
||||||
|
for(const serv of this.servers){
|
||||||
|
if(serv.server.id === id){
|
||||||
|
return serv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMainServer(): ServerWrapper | null {
|
||||||
|
return this.mainServer
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class DistroManager {
|
||||||
|
|
||||||
|
private static readonly DISTRO_PATH = join(ConfigManager.getLauncherDirectory(), 'distribution.json')
|
||||||
|
private static readonly DEV_PATH = join(ConfigManager.getLauncherDirectory(), 'dev_distribution.json')
|
||||||
|
|
||||||
|
private static readonly DISTRIBUTION_URL = 'http://mc.westeroscraft.com/WesterosCraftLauncher/distribution.json'
|
||||||
|
// private static readonly DISTRIBUTION_URL = 'https://gist.githubusercontent.com/dscalzi/53b1ba7a11d26a5c353f9d5ae484b71b/raw/'
|
||||||
|
|
||||||
|
private static DEV_MODE = false
|
||||||
|
|
||||||
|
private static distro: DistributionWrapper
|
||||||
|
|
||||||
|
public static isDevMode() {
|
||||||
|
return DistroManager.DEV_MODE
|
||||||
|
}
|
||||||
|
|
||||||
|
public static setDevMode(value: boolean) {
|
||||||
|
if(value){
|
||||||
|
logger.log('Developer mode enabled.')
|
||||||
|
logger.log('If you don\'t know what that means, revert immediately.')
|
||||||
|
} else {
|
||||||
|
logger.log('Developer mode disabled.')
|
||||||
|
}
|
||||||
|
DistroManager.DEV_MODE = value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static pullRemote(): Promise<DistributionWrapper> {
|
||||||
|
if(DistroManager.DEV_MODE){
|
||||||
|
return DistroManager.pullLocal()
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const opts = {
|
||||||
|
url: DistroManager.DISTRIBUTION_URL,
|
||||||
|
timeout: 2500
|
||||||
|
}
|
||||||
|
const distroDest = join(ConfigManager.getLauncherDirectory(), 'distribution.json')
|
||||||
|
request(opts, async (error, resp, body) => {
|
||||||
|
if(!error){
|
||||||
|
|
||||||
|
let data: Distribution
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = JSON.parse(body) as Distribution
|
||||||
|
|
||||||
|
DistroManager.distro = new DistributionWrapper(data)
|
||||||
|
} catch (e) {
|
||||||
|
reject(e)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await writeJson(distroDest, DistroManager.distro)
|
||||||
|
resolve(DistroManager.distro)
|
||||||
|
} catch (err) {
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async pullLocal(): Promise<DistributionWrapper> {
|
||||||
|
const data = await readJson(DistroManager.DEV_MODE ? DistroManager.DEV_PATH : DistroManager.DISTRO_PATH) as Distribution
|
||||||
|
|
||||||
|
DistroManager.distro = new DistributionWrapper(data)
|
||||||
|
|
||||||
|
return DistroManager.distro
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getDistribution(): DistributionWrapper {
|
||||||
|
return DistroManager.distro
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
const fs = require('fs-extra')
|
import { ensureDirSync, pathExistsSync, readdirSync, moveSync, readFileSync, writeFileSync, rename } from 'fs-extra'
|
||||||
const path = require('path')
|
import { join } from 'path'
|
||||||
const { shell } = require('electron')
|
import { shell } from 'electron'
|
||||||
|
|
||||||
// Group #1: File Name (without .disabled, if any)
|
// Group #1: File Name (without .disabled, if any)
|
||||||
// Group #2: File Extension (jar, zip, or litemod)
|
// Group #2: File Extension (jar, zip, or litemod)
|
||||||
@ -19,8 +19,8 @@ const SHADER_CONFIG = 'optionsshaders.txt'
|
|||||||
*
|
*
|
||||||
* @param {string} modsDir The path to the mods directory.
|
* @param {string} modsDir The path to the mods directory.
|
||||||
*/
|
*/
|
||||||
exports.validateDir = function(dir) {
|
export function validateDir(dir: string) {
|
||||||
fs.ensureDirSync(dir)
|
ensureDirSync(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,14 +33,14 @@ exports.validateDir = function(dir) {
|
|||||||
* @returns {{fullName: string, name: string, ext: string, disabled: boolean}[]}
|
* @returns {{fullName: string, name: string, ext: string, disabled: boolean}[]}
|
||||||
* An array of objects storing metadata about each discovered mod.
|
* An array of objects storing metadata about each discovered mod.
|
||||||
*/
|
*/
|
||||||
exports.scanForDropinMods = function(modsDir, version) {
|
export function scanForDropinMods(modsDir: string, version: string) {
|
||||||
const modsDiscovered = []
|
const modsDiscovered = []
|
||||||
if(fs.existsSync(modsDir)){
|
if(pathExistsSync(modsDir)){
|
||||||
let modCandidates = fs.readdirSync(modsDir)
|
let modCandidates = readdirSync(modsDir)
|
||||||
let verCandidates = []
|
let verCandidates: string[] = []
|
||||||
const versionDir = path.join(modsDir, version)
|
const versionDir = join(modsDir, version)
|
||||||
if(fs.existsSync(versionDir)){
|
if(pathExistsSync(versionDir)){
|
||||||
verCandidates = fs.readdirSync(versionDir)
|
verCandidates = readdirSync(versionDir)
|
||||||
}
|
}
|
||||||
for(let file of modCandidates){
|
for(let file of modCandidates){
|
||||||
const match = MOD_REGEX.exec(file)
|
const match = MOD_REGEX.exec(file)
|
||||||
@ -57,7 +57,7 @@ exports.scanForDropinMods = function(modsDir, version) {
|
|||||||
const match = MOD_REGEX.exec(file)
|
const match = MOD_REGEX.exec(file)
|
||||||
if(match != null){
|
if(match != null){
|
||||||
modsDiscovered.push({
|
modsDiscovered.push({
|
||||||
fullName: path.join(version, match[0]),
|
fullName: join(version, match[0]),
|
||||||
name: match[1],
|
name: match[1],
|
||||||
ext: match[2],
|
ext: match[2],
|
||||||
disabled: match[3] != null
|
disabled: match[3] != null
|
||||||
@ -74,13 +74,13 @@ exports.scanForDropinMods = function(modsDir, version) {
|
|||||||
* @param {FileList} files The files to add.
|
* @param {FileList} files The files to add.
|
||||||
* @param {string} modsDir The path to the mods directory.
|
* @param {string} modsDir The path to the mods directory.
|
||||||
*/
|
*/
|
||||||
exports.addDropinMods = function(files, modsdir) {
|
export function addDropinMods(files: any, modsdir: string) {
|
||||||
|
|
||||||
exports.validateDir(modsdir)
|
exports.validateDir(modsdir)
|
||||||
|
|
||||||
for(let f of files) {
|
for(let f of files) {
|
||||||
if(MOD_REGEX.exec(f.name) != null) {
|
if(MOD_REGEX.exec(f.name) != null) {
|
||||||
fs.moveSync(f.path, path.join(modsdir, f.name))
|
moveSync(f.path, join(modsdir, f.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +94,8 @@ exports.addDropinMods = function(files, modsdir) {
|
|||||||
*
|
*
|
||||||
* @returns {boolean} True if the mod was deleted, otherwise false.
|
* @returns {boolean} True if the mod was deleted, otherwise false.
|
||||||
*/
|
*/
|
||||||
exports.deleteDropinMod = function(modsDir, fullName){
|
export function deleteDropinMod(modsDir: string, fullName: string){
|
||||||
const res = shell.moveItemToTrash(path.join(modsDir, fullName))
|
const res = shell.moveItemToTrash(join(modsDir, fullName))
|
||||||
if(!res){
|
if(!res){
|
||||||
shell.beep()
|
shell.beep()
|
||||||
}
|
}
|
||||||
@ -113,12 +113,12 @@ exports.deleteDropinMod = function(modsDir, fullName){
|
|||||||
* @returns {Promise.<void>} A promise which resolves when the mod has
|
* @returns {Promise.<void>} A promise which resolves when the mod has
|
||||||
* been toggled. If an IO error occurs the promise will be rejected.
|
* been toggled. If an IO error occurs the promise will be rejected.
|
||||||
*/
|
*/
|
||||||
exports.toggleDropinMod = function(modsDir, fullName, enable){
|
export function toggleDropinMod(modsDir: string, fullName: string, enable: boolean){
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const oldPath = path.join(modsDir, fullName)
|
const oldPath = join(modsDir, fullName)
|
||||||
const newPath = path.join(modsDir, enable ? fullName.substring(0, fullName.indexOf(DISABLED_EXT)) : fullName + DISABLED_EXT)
|
const newPath = join(modsDir, enable ? fullName.substring(0, fullName.indexOf(DISABLED_EXT)) : fullName + DISABLED_EXT)
|
||||||
|
|
||||||
fs.rename(oldPath, newPath, (err) => {
|
rename(oldPath, newPath, (err) => {
|
||||||
if(err){
|
if(err){
|
||||||
reject(err)
|
reject(err)
|
||||||
} else {
|
} else {
|
||||||
@ -134,7 +134,7 @@ exports.toggleDropinMod = function(modsDir, fullName, enable){
|
|||||||
* @param {string} fullName The fullName of the discovered mod to toggle.
|
* @param {string} fullName The fullName of the discovered mod to toggle.
|
||||||
* @returns {boolean} True if the mod is enabled, otherwise false.
|
* @returns {boolean} True if the mod is enabled, otherwise false.
|
||||||
*/
|
*/
|
||||||
exports.isDropinModEnabled = function(fullName){
|
export function isDropinModEnabled(fullName: string){
|
||||||
return !fullName.endsWith(DISABLED_EXT)
|
return !fullName.endsWith(DISABLED_EXT)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,14 +146,14 @@ exports.isDropinModEnabled = function(fullName){
|
|||||||
* @returns {{fullName: string, name: string}[]}
|
* @returns {{fullName: string, name: string}[]}
|
||||||
* An array of objects storing metadata about each discovered shaderpack.
|
* An array of objects storing metadata about each discovered shaderpack.
|
||||||
*/
|
*/
|
||||||
exports.scanForShaderpacks = function(instanceDir){
|
export function scanForShaderpacks(instanceDir: string){
|
||||||
const shaderDir = path.join(instanceDir, SHADER_DIR)
|
const shaderDir = join(instanceDir, SHADER_DIR)
|
||||||
const packsDiscovered = [{
|
const packsDiscovered = [{
|
||||||
fullName: 'OFF',
|
fullName: 'OFF',
|
||||||
name: 'Off (Default)'
|
name: 'Off (Default)'
|
||||||
}]
|
}]
|
||||||
if(fs.existsSync(shaderDir)){
|
if(pathExistsSync(shaderDir)){
|
||||||
let modCandidates = fs.readdirSync(shaderDir)
|
let modCandidates = readdirSync(shaderDir)
|
||||||
for(let file of modCandidates){
|
for(let file of modCandidates){
|
||||||
const match = SHADER_REGEX.exec(file)
|
const match = SHADER_REGEX.exec(file)
|
||||||
if(match != null){
|
if(match != null){
|
||||||
@ -175,12 +175,12 @@ exports.scanForShaderpacks = function(instanceDir){
|
|||||||
*
|
*
|
||||||
* @returns {string} The file name of the enabled shaderpack.
|
* @returns {string} The file name of the enabled shaderpack.
|
||||||
*/
|
*/
|
||||||
exports.getEnabledShaderpack = function(instanceDir){
|
export function getEnabledShaderpack(instanceDir: string){
|
||||||
exports.validateDir(instanceDir)
|
validateDir(instanceDir)
|
||||||
|
|
||||||
const optionsShaders = path.join(instanceDir, SHADER_CONFIG)
|
const optionsShaders = join(instanceDir, SHADER_CONFIG)
|
||||||
if(fs.existsSync(optionsShaders)){
|
if(pathExistsSync(optionsShaders)){
|
||||||
const buf = fs.readFileSync(optionsShaders, {encoding: 'utf-8'})
|
const buf = readFileSync(optionsShaders, {encoding: 'utf-8'})
|
||||||
const match = SHADER_OPTION.exec(buf)
|
const match = SHADER_OPTION.exec(buf)
|
||||||
if(match != null){
|
if(match != null){
|
||||||
return match[1]
|
return match[1]
|
||||||
@ -197,18 +197,18 @@ exports.getEnabledShaderpack = function(instanceDir){
|
|||||||
* @param {string} instanceDir The path to the server instance directory.
|
* @param {string} instanceDir The path to the server instance directory.
|
||||||
* @param {string} pack the file name of the shaderpack.
|
* @param {string} pack the file name of the shaderpack.
|
||||||
*/
|
*/
|
||||||
exports.setEnabledShaderpack = function(instanceDir, pack){
|
export function setEnabledShaderpack(instanceDir: string, pack: string){
|
||||||
exports.validateDir(instanceDir)
|
validateDir(instanceDir)
|
||||||
|
|
||||||
const optionsShaders = path.join(instanceDir, SHADER_CONFIG)
|
const optionsShaders = join(instanceDir, SHADER_CONFIG)
|
||||||
let buf
|
let buf
|
||||||
if(fs.existsSync(optionsShaders)){
|
if(pathExistsSync(optionsShaders)){
|
||||||
buf = fs.readFileSync(optionsShaders, {encoding: 'utf-8'})
|
buf = readFileSync(optionsShaders, {encoding: 'utf-8'})
|
||||||
buf = buf.replace(SHADER_OPTION, `shaderPack=${pack}`)
|
buf = buf.replace(SHADER_OPTION, `shaderPack=${pack}`)
|
||||||
} else {
|
} else {
|
||||||
buf = `shaderPack=${pack}`
|
buf = `shaderPack=${pack}`
|
||||||
}
|
}
|
||||||
fs.writeFileSync(optionsShaders, buf, {encoding: 'utf-8'})
|
writeFileSync(optionsShaders, buf, {encoding: 'utf-8'})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,15 +217,15 @@ exports.setEnabledShaderpack = function(instanceDir, pack){
|
|||||||
* @param {FileList} files The files to add.
|
* @param {FileList} files The files to add.
|
||||||
* @param {string} instanceDir The path to the server instance directory.
|
* @param {string} instanceDir The path to the server instance directory.
|
||||||
*/
|
*/
|
||||||
exports.addShaderpacks = function(files, instanceDir) {
|
export function addShaderpacks(files: any, instanceDir: string) {
|
||||||
|
|
||||||
const p = path.join(instanceDir, SHADER_DIR)
|
const p = join(instanceDir, SHADER_DIR)
|
||||||
|
|
||||||
exports.validateDir(p)
|
exports.validateDir(p)
|
||||||
|
|
||||||
for(let f of files) {
|
for(let f of files) {
|
||||||
if(SHADER_REGEX.exec(f.name) != null) {
|
if(SHADER_REGEX.exec(f.name) != null) {
|
||||||
fs.moveSync(f.path, path.join(p, f.name))
|
moveSync(f.path, join(p, f.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,16 +1,15 @@
|
|||||||
// Requirements
|
import { ipcMain, app, BrowserWindow, Menu, MenuItem } from "electron"
|
||||||
const {app, BrowserWindow, ipcMain} = require('electron')
|
import { prerelease } from "semver"
|
||||||
const Menu = require('electron').Menu
|
import { join } from "path"
|
||||||
const autoUpdater = require('electron-updater').autoUpdater
|
import { readdirSync } from "fs-extra"
|
||||||
const ejse = require('ejs-electron')
|
import { format } from "url"
|
||||||
const fs = require('fs')
|
import { autoUpdater } from 'electron-updater'
|
||||||
const isDev = require('./app/assets/js/isdev')
|
import isdev from "./isdev"
|
||||||
const path = require('path')
|
|
||||||
const semver = require('semver')
|
const ejse = require('ejs-electron')
|
||||||
const url = require('url')
|
|
||||||
|
|
||||||
// Setup auto updater.
|
// Setup auto updater.
|
||||||
function initAutoUpdater(event, data) {
|
function initAutoUpdater(event: any, data: any) {
|
||||||
|
|
||||||
if(data){
|
if(data){
|
||||||
autoUpdater.allowPrerelease = true
|
autoUpdater.allowPrerelease = true
|
||||||
@ -19,9 +18,9 @@ function initAutoUpdater(event, data) {
|
|||||||
// autoUpdater.allowPrerelease = true
|
// autoUpdater.allowPrerelease = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isDev){
|
if(isdev){
|
||||||
autoUpdater.autoInstallOnAppQuit = false
|
autoUpdater.autoInstallOnAppQuit = false
|
||||||
autoUpdater.updateConfigPath = path.join(__dirname, 'dev-app-update.yml')
|
autoUpdater.updateConfigPath = join(__dirname, '..', 'dev-app-update.yml')
|
||||||
}
|
}
|
||||||
if(process.platform === 'darwin'){
|
if(process.platform === 'darwin'){
|
||||||
autoUpdater.autoDownload = false
|
autoUpdater.autoDownload = false
|
||||||
@ -59,7 +58,7 @@ ipcMain.on('autoUpdateAction', (event, arg, data) => {
|
|||||||
break
|
break
|
||||||
case 'allowPrereleaseChange':
|
case 'allowPrereleaseChange':
|
||||||
if(!data){
|
if(!data){
|
||||||
const preRelComp = semver.prerelease(app.getVersion())
|
const preRelComp = prerelease(app.getVersion())
|
||||||
if(preRelComp != null && preRelComp.length > 0){
|
if(preRelComp != null && preRelComp.length > 0){
|
||||||
autoUpdater.allowPrerelease = true
|
autoUpdater.allowPrerelease = true
|
||||||
} else {
|
} else {
|
||||||
@ -88,7 +87,7 @@ app.disableHardwareAcceleration()
|
|||||||
|
|
||||||
// Keep a global reference of the window object, if you don't, the window will
|
// Keep a global reference of the window object, if you don't, the window will
|
||||||
// be closed automatically when the JavaScript object is garbage collected.
|
// be closed automatically when the JavaScript object is garbage collected.
|
||||||
let win
|
let win: BrowserWindow | null
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
|
|
||||||
@ -98,17 +97,17 @@ function createWindow() {
|
|||||||
icon: getPlatformIcon('SealCircle'),
|
icon: getPlatformIcon('SealCircle'),
|
||||||
frame: false,
|
frame: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(__dirname, 'app', 'assets', 'js', 'preloader.js'),
|
preload: join(__dirname, '..', 'out', 'preloader.js'),
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
contextIsolation: false
|
contextIsolation: false
|
||||||
},
|
},
|
||||||
backgroundColor: '#171614'
|
backgroundColor: '#171614'
|
||||||
})
|
})
|
||||||
|
|
||||||
ejse.data('bkid', Math.floor((Math.random() * fs.readdirSync(path.join(__dirname, 'app', 'assets', 'images', 'backgrounds')).length)))
|
ejse.data('bkid', Math.floor((Math.random() * readdirSync(join(__dirname, '..', 'assets', 'images', 'backgrounds')).length)))
|
||||||
|
|
||||||
win.loadURL(url.format({
|
win.loadURL(format({
|
||||||
pathname: path.join(__dirname, 'app', 'app.ejs'),
|
pathname: join(__dirname, '..', 'assets', 'templates', 'app.ejs'),
|
||||||
protocol: 'file:',
|
protocol: 'file:',
|
||||||
slashes: true
|
slashes: true
|
||||||
}))
|
}))
|
||||||
@ -131,56 +130,65 @@ function createMenu() {
|
|||||||
if(process.platform === 'darwin') {
|
if(process.platform === 'darwin') {
|
||||||
|
|
||||||
// Extend default included application menu to continue support for quit keyboard shortcut
|
// Extend default included application menu to continue support for quit keyboard shortcut
|
||||||
let applicationSubMenu = {
|
let applicationSubMenu = new MenuItem({
|
||||||
label: 'Application',
|
label: 'Application',
|
||||||
submenu: [{
|
submenu: [{
|
||||||
label: 'About Application',
|
label: 'About Application',
|
||||||
selector: 'orderFrontStandardAboutPanel:'
|
role: 'about'
|
||||||
}, {
|
}, {
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
}, {
|
}, {
|
||||||
label: 'Quit',
|
label: 'Quit',
|
||||||
accelerator: 'Command+Q',
|
accelerator: 'Command+Q',
|
||||||
|
role: 'quit',
|
||||||
click: () => {
|
click: () => {
|
||||||
app.quit()
|
app.quit()
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}
|
})
|
||||||
|
|
||||||
// New edit menu adds support for text-editing keyboard shortcuts
|
// New edit menu adds support for text-editing keyboard shortcuts
|
||||||
let editSubMenu = {
|
let editSubMenu = new MenuItem({
|
||||||
label: 'Edit',
|
label: 'Edit',
|
||||||
submenu: [{
|
submenu: [
|
||||||
label: 'Undo',
|
{
|
||||||
accelerator: 'CmdOrCtrl+Z',
|
label: 'Undo',
|
||||||
selector: 'undo:'
|
accelerator: 'CmdOrCtrl+Z',
|
||||||
}, {
|
role: 'undo'
|
||||||
label: 'Redo',
|
},
|
||||||
accelerator: 'Shift+CmdOrCtrl+Z',
|
{
|
||||||
selector: 'redo:'
|
label: 'Redo',
|
||||||
}, {
|
accelerator: 'Shift+CmdOrCtrl+Z',
|
||||||
type: 'separator'
|
role: 'redo'
|
||||||
}, {
|
},
|
||||||
label: 'Cut',
|
{
|
||||||
accelerator: 'CmdOrCtrl+X',
|
type: 'separator'
|
||||||
selector: 'cut:'
|
},
|
||||||
}, {
|
{
|
||||||
label: 'Copy',
|
label: 'Cut',
|
||||||
accelerator: 'CmdOrCtrl+C',
|
accelerator: 'CmdOrCtrl+X',
|
||||||
selector: 'copy:'
|
role: 'cut'
|
||||||
}, {
|
},
|
||||||
label: 'Paste',
|
{
|
||||||
accelerator: 'CmdOrCtrl+V',
|
label: 'Copy',
|
||||||
selector: 'paste:'
|
accelerator: 'CmdOrCtrl+C',
|
||||||
}, {
|
role: 'copy'
|
||||||
label: 'Select All',
|
},
|
||||||
accelerator: 'CmdOrCtrl+A',
|
{
|
||||||
selector: 'selectAll:'
|
label: 'Paste',
|
||||||
}]
|
accelerator: 'CmdOrCtrl+V',
|
||||||
}
|
role: 'paste'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Select All',
|
||||||
|
accelerator: 'CmdOrCtrl+A',
|
||||||
|
role: 'selectAll'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
// Bundle submenus into a single template and build a menu object with it
|
// Bundle submenus into a single template and build a menu object with it
|
||||||
let menuTemplate = [applicationSubMenu, editSubMenu]
|
let menuTemplate: MenuItem[] = [applicationSubMenu, editSubMenu]
|
||||||
let menuObject = Menu.buildFromTemplate(menuTemplate)
|
let menuObject = Menu.buildFromTemplate(menuTemplate)
|
||||||
|
|
||||||
// Assign it to the application
|
// Assign it to the application
|
||||||
@ -190,7 +198,7 @@ function createMenu() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPlatformIcon(filename){
|
function getPlatformIcon(filename: string){
|
||||||
const opSys = process.platform
|
const opSys = process.platform
|
||||||
if (opSys === 'darwin') {
|
if (opSys === 'darwin') {
|
||||||
filename = filename + '.icns'
|
filename = filename + '.icns'
|
||||||
@ -200,7 +208,7 @@ function getPlatformIcon(filename){
|
|||||||
filename = filename + '.png'
|
filename = filename + '.png'
|
||||||
}
|
}
|
||||||
|
|
||||||
return path.join(__dirname, 'app', 'assets', 'images', filename)
|
return join(__dirname, '..', 'assets', 'images', filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('ready', createWindow)
|
app.on('ready', createWindow)
|
5
src/isdev.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
'use strict'
|
||||||
|
const getFromEnv = parseInt(process.env.ELECTRON_IS_DEV as string, 10) === 1
|
||||||
|
const isEnvSet = 'ELECTRON_IS_DEV' in process.env
|
||||||
|
|
||||||
|
export default (isEnvSet ? getFromEnv : (process.defaultApp || /node_modules[\\/]electron[\\/]/.test(process.execPath))) as boolean
|
23
src/langloader.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { readJSONSync } from 'fs-extra'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
// TODO revisit
|
||||||
|
|
||||||
|
let lang: any
|
||||||
|
|
||||||
|
export function loadLanguage(id: string){
|
||||||
|
lang = readJSONSync(join(__dirname, '..', 'assets', 'lang', `${id}.json`)) || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function query(id: string){
|
||||||
|
let query = id.split('.')
|
||||||
|
let res = lang
|
||||||
|
for(let q of query){
|
||||||
|
res = res[q]
|
||||||
|
}
|
||||||
|
return res === lang ? {} : res
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queryJS(id: string){
|
||||||
|
return exports.query(`js.${id}`)
|
||||||
|
}
|
28
src/loggerutil.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export class LoggerUtil {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected prefix: string,
|
||||||
|
protected style: string
|
||||||
|
){}
|
||||||
|
|
||||||
|
public log(...args: any[]){
|
||||||
|
console.log(this.prefix, this.style, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
public info(...args: any[]){
|
||||||
|
console.info(this.prefix, this.style, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
public warn(...args: any[]){
|
||||||
|
console.warn(this.prefix, this.style, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
public debug(...args: any[]){
|
||||||
|
console.debug(this.prefix, this.style, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
public error(...args: any[]){
|
||||||
|
console.error(this.prefix, this.style, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
33
src/model/internal/config/LauncherConfig.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { SavedAccount } from './SavedAccount';
|
||||||
|
import { NewsCache } from './NewsCache';
|
||||||
|
import { ModConfig } from './ModConfig';
|
||||||
|
|
||||||
|
export interface LauncherConfig {
|
||||||
|
|
||||||
|
settings: {
|
||||||
|
java: {
|
||||||
|
minRAM: string
|
||||||
|
maxRAM: string
|
||||||
|
executable: string | null
|
||||||
|
jvmOptions: string[]
|
||||||
|
}
|
||||||
|
game: {
|
||||||
|
resWidth: number
|
||||||
|
resHeight: number
|
||||||
|
fullscreen: boolean
|
||||||
|
autoConnect: boolean
|
||||||
|
launchDetached: boolean
|
||||||
|
}
|
||||||
|
launcher: {
|
||||||
|
allowPrerelease: boolean
|
||||||
|
dataDirectory: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newsCache: NewsCache
|
||||||
|
clientToken: string | null
|
||||||
|
selectedServer: string | null
|
||||||
|
selectedAccount: string | null
|
||||||
|
authenticationDatabase: {[uuid: string]: SavedAccount},
|
||||||
|
modConfigurations: ModConfig[]
|
||||||
|
|
||||||
|
}
|
17
src/model/internal/config/ModConfig.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export interface SubModConfig {
|
||||||
|
|
||||||
|
mods: {
|
||||||
|
[id: string]: boolean | SubModConfig
|
||||||
|
}
|
||||||
|
value: boolean
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModConfig {
|
||||||
|
|
||||||
|
id: string
|
||||||
|
mods: {
|
||||||
|
[id: string]: boolean | SubModConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
7
src/model/internal/config/NewsCache.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface NewsCache {
|
||||||
|
|
||||||
|
date: string | null
|
||||||
|
content: string | null
|
||||||
|
dismissed: boolean
|
||||||
|
|
||||||
|
}
|
7
src/model/internal/config/SavedAccount.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface SavedAccount {
|
||||||
|
|
||||||
|
accessToken: string
|
||||||
|
username: string
|
||||||
|
uuid: string
|
||||||
|
displayName: string
|
||||||
|
}
|
6
src/model/mojang/auth/Agent.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export interface Agent {
|
||||||
|
|
||||||
|
name: 'Minecraft'
|
||||||
|
version: number
|
||||||
|
|
||||||
|
}
|
11
src/model/mojang/auth/AuthPayload.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Agent } from "./Agent";
|
||||||
|
|
||||||
|
export interface AuthPayload {
|
||||||
|
|
||||||
|
agent: Agent
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
clientToken?: string
|
||||||
|
requestUser?: boolean
|
||||||
|
|
||||||
|
}
|
17
src/model/mojang/auth/Session.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export interface Session {
|
||||||
|
|
||||||
|
accessToken: string
|
||||||
|
clientToken: string
|
||||||
|
selectedProfile: {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
user?: {
|
||||||
|
id: string
|
||||||
|
properties: Array<{
|
||||||
|
name: string
|
||||||
|
value: string
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
src/model/mojang/index/LauncherJson.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
interface LauncherJava {
|
||||||
|
sha1: string
|
||||||
|
url: string
|
||||||
|
version: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LauncherVersions {
|
||||||
|
launcher: {
|
||||||
|
commit: string
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LauncherJson {
|
||||||
|
|
||||||
|
java: {
|
||||||
|
lzma: {
|
||||||
|
sha1: string
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
sha1: string
|
||||||
|
}
|
||||||
|
linux: {
|
||||||
|
applink: string
|
||||||
|
downloadhash: string
|
||||||
|
versions: LauncherVersions
|
||||||
|
}
|
||||||
|
osx: {
|
||||||
|
'64': {
|
||||||
|
jdk: LauncherJava
|
||||||
|
jre: LauncherJava
|
||||||
|
}
|
||||||
|
apphash: string
|
||||||
|
applink: string
|
||||||
|
downloadhash: string
|
||||||
|
versions: LauncherVersions
|
||||||
|
}
|
||||||
|
windows: {
|
||||||
|
'32': {
|
||||||
|
jdk: LauncherJava
|
||||||
|
jre: LauncherJava
|
||||||
|
}
|
||||||
|
'64': {
|
||||||
|
jdk: LauncherJava
|
||||||
|
jre: LauncherJava
|
||||||
|
}
|
||||||
|
apphash: string
|
||||||
|
applink: string
|
||||||
|
downloadhash: string
|
||||||
|
rolloutPercent: number
|
||||||
|
versions: LauncherVersions
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
103
src/model/mojang/index/VersionJson.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
export interface Rule {
|
||||||
|
action: string
|
||||||
|
os?: {
|
||||||
|
name: string
|
||||||
|
version?: string
|
||||||
|
}
|
||||||
|
features?: {
|
||||||
|
[key: string]: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Natives {
|
||||||
|
linux?: string
|
||||||
|
osx?: string
|
||||||
|
windows?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BaseArtifact {
|
||||||
|
|
||||||
|
sha1: string
|
||||||
|
size: number
|
||||||
|
url: string
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LibraryArtifact extends BaseArtifact {
|
||||||
|
|
||||||
|
path: string
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Library {
|
||||||
|
downloads: {
|
||||||
|
artifact: LibraryArtifact
|
||||||
|
classifiers?: {
|
||||||
|
javadoc?: LibraryArtifact
|
||||||
|
'natives-linux'?: LibraryArtifact
|
||||||
|
'natives-macos'?: LibraryArtifact
|
||||||
|
'natives-windows'?: LibraryArtifact
|
||||||
|
sources?: LibraryArtifact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extract?: {
|
||||||
|
exclude: string[]
|
||||||
|
}
|
||||||
|
name: string
|
||||||
|
natives?: Natives
|
||||||
|
rules?: Rule[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VersionJson {
|
||||||
|
|
||||||
|
arguments: {
|
||||||
|
game: string[]
|
||||||
|
jvm: {
|
||||||
|
rules: Rule[]
|
||||||
|
value: string[]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
assetIndex: {
|
||||||
|
id: string
|
||||||
|
sha1: string
|
||||||
|
size: number
|
||||||
|
totalSize: number
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
assets: string
|
||||||
|
downloads: {
|
||||||
|
client: BaseArtifact
|
||||||
|
server: BaseArtifact
|
||||||
|
}
|
||||||
|
id: string
|
||||||
|
libraries: Library[]
|
||||||
|
logging: {
|
||||||
|
client: {
|
||||||
|
argument: string
|
||||||
|
file: {
|
||||||
|
id: string
|
||||||
|
sha1: string
|
||||||
|
size: number
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mainClass: string
|
||||||
|
minimumLauncherVersion: number
|
||||||
|
releaseTime: string
|
||||||
|
time: string
|
||||||
|
type: string
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetIndex {
|
||||||
|
|
||||||
|
objects: {
|
||||||
|
[file: string]: {
|
||||||
|
hash: string
|
||||||
|
size: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
277
src/mojang/mojang.ts
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
import request from 'request'
|
||||||
|
import { LoggerUtil } from '../loggerutil'
|
||||||
|
import { Agent } from '../model/mojang/auth/Agent'
|
||||||
|
import { AuthPayload } from '../model/mojang/auth/AuthPayload'
|
||||||
|
import { Session } from '../model/mojang/auth/Session'
|
||||||
|
import { Status } from './type/Status'
|
||||||
|
|
||||||
|
export class Mojang {
|
||||||
|
|
||||||
|
private static readonly logger = new LoggerUtil('%c[Mojang]', 'color: #a02d2a; font-weight: bold')
|
||||||
|
|
||||||
|
public static readonly AUTH_ENDPOINT = 'https://authserver.mojang.com'
|
||||||
|
public static readonly MINECRAFT_AGENT: Agent = {
|
||||||
|
name: 'Minecraft',
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static statuses: Status[] = [
|
||||||
|
{
|
||||||
|
service: 'sessionserver.mojang.com',
|
||||||
|
status: 'grey',
|
||||||
|
name: 'Multiplayer Session Service',
|
||||||
|
essential: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: 'authserver.mojang.com',
|
||||||
|
status: 'grey',
|
||||||
|
name: 'Authentication Service',
|
||||||
|
essential: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: 'textures.minecraft.net',
|
||||||
|
status: 'grey',
|
||||||
|
name: 'Minecraft Skins',
|
||||||
|
essential: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: 'api.mojang.com',
|
||||||
|
status: 'grey',
|
||||||
|
name: 'Public API',
|
||||||
|
essential: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: 'minecraft.net',
|
||||||
|
status: 'grey',
|
||||||
|
name: 'Minecraft.net',
|
||||||
|
essential: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: 'account.mojang.com',
|
||||||
|
status: 'grey',
|
||||||
|
name: 'Mojang Accounts Website',
|
||||||
|
essential: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a Mojang status color to a hex value. Valid statuses
|
||||||
|
* are 'green', 'yellow', 'red', and 'grey'. Grey is a custom status
|
||||||
|
* to our project which represents an unknown status.
|
||||||
|
*
|
||||||
|
* @param {string} status A valid status code.
|
||||||
|
* @returns {string} The hex color of the status code.
|
||||||
|
*/
|
||||||
|
public static statusToHex(status: string){
|
||||||
|
switch(status.toLowerCase()){
|
||||||
|
case 'green':
|
||||||
|
return '#a5c325'
|
||||||
|
case 'yellow':
|
||||||
|
return '#eac918'
|
||||||
|
case 'red':
|
||||||
|
return '#c32625'
|
||||||
|
case 'grey':
|
||||||
|
default:
|
||||||
|
return '#848484'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the status of Mojang's services.
|
||||||
|
* The response is condensed into a single object. Each service is
|
||||||
|
* a key, where the value is an object containing a status and name
|
||||||
|
* property.
|
||||||
|
*
|
||||||
|
* @see http://wiki.vg/Mojang_API#API_Status
|
||||||
|
*/
|
||||||
|
public static status(): Promise<Status[]>{
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request.get('https://status.mojang.com/check',
|
||||||
|
{
|
||||||
|
json: true,
|
||||||
|
timeout: 2500
|
||||||
|
},
|
||||||
|
function(error, response, body: {[service: string]: 'red' | 'yellow' | 'green'}[]){
|
||||||
|
|
||||||
|
if(error || response.statusCode !== 200){
|
||||||
|
Mojang.logger.warn('Unable to retrieve Mojang status.')
|
||||||
|
Mojang.logger.debug('Error while retrieving Mojang statuses:', error)
|
||||||
|
//reject(error || response.statusCode)
|
||||||
|
for(let i=0; i<Mojang.statuses.length; i++){
|
||||||
|
Mojang.statuses[i].status = 'grey'
|
||||||
|
}
|
||||||
|
resolve(Mojang.statuses)
|
||||||
|
} else {
|
||||||
|
for(let i=0; i<body.length; i++){
|
||||||
|
const key = Object.keys(body[i])[0]
|
||||||
|
inner:
|
||||||
|
for(let j=0; j<Mojang.statuses.length; j++){
|
||||||
|
if(Mojang.statuses[j].service === key) {
|
||||||
|
Mojang.statuses[j].status = body[i][key]
|
||||||
|
break inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve(Mojang.statuses)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticate a user with their Mojang credentials.
|
||||||
|
*
|
||||||
|
* @param {string} username The user's username, this is often an email.
|
||||||
|
* @param {string} password The user's password.
|
||||||
|
* @param {string} clientToken The launcher's Client Token.
|
||||||
|
* @param {boolean} requestUser Optional. Adds user object to the reponse.
|
||||||
|
* @param {Object} agent Optional. Provided by default. Adds user info to the response.
|
||||||
|
*
|
||||||
|
* @see http://wiki.vg/Authentication#Authenticate
|
||||||
|
*/
|
||||||
|
public static authenticate(
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
clientToken: string | null,
|
||||||
|
requestUser: boolean = true,
|
||||||
|
agent: Agent = Mojang.MINECRAFT_AGENT
|
||||||
|
): Promise<Session> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
const body: AuthPayload = {
|
||||||
|
agent,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
requestUser
|
||||||
|
}
|
||||||
|
if(clientToken != null){
|
||||||
|
body.clientToken = clientToken
|
||||||
|
}
|
||||||
|
|
||||||
|
request.post(Mojang.AUTH_ENDPOINT + '/authenticate',
|
||||||
|
{
|
||||||
|
json: true,
|
||||||
|
body
|
||||||
|
},
|
||||||
|
function(error, response, body){
|
||||||
|
if(error){
|
||||||
|
Mojang.logger.error('Error during authentication.', error)
|
||||||
|
reject(error)
|
||||||
|
} else {
|
||||||
|
if(response.statusCode === 200){
|
||||||
|
resolve(body)
|
||||||
|
} else {
|
||||||
|
reject(body || {code: 'ENOTFOUND'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate an access token. This should always be done before launching.
|
||||||
|
* The client token should match the one used to create the access token.
|
||||||
|
*
|
||||||
|
* @param {string} accessToken The access token to validate.
|
||||||
|
* @param {string} clientToken The launcher's client token.
|
||||||
|
*
|
||||||
|
* @see http://wiki.vg/Authentication#Validate
|
||||||
|
*/
|
||||||
|
public static validate(accessToken: string, clientToken: string): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request.post(Mojang.AUTH_ENDPOINT + '/validate',
|
||||||
|
{
|
||||||
|
json: true,
|
||||||
|
body: {
|
||||||
|
accessToken,
|
||||||
|
clientToken
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(error, response, body){
|
||||||
|
if(error){
|
||||||
|
Mojang.logger.error('Error during validation.', error)
|
||||||
|
reject(error)
|
||||||
|
} else {
|
||||||
|
if(response.statusCode === 403){
|
||||||
|
resolve(false)
|
||||||
|
} else {
|
||||||
|
// 204 if valid
|
||||||
|
resolve(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates an access token. The clientToken must match the
|
||||||
|
* token used to create the provided accessToken.
|
||||||
|
*
|
||||||
|
* @param {string} accessToken The access token to invalidate.
|
||||||
|
* @param {string} clientToken The launcher's client token.
|
||||||
|
*
|
||||||
|
* @see http://wiki.vg/Authentication#Invalidate
|
||||||
|
*/
|
||||||
|
public static invalidate(accessToken: string, clientToken: string): Promise<void>{
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request.post(Mojang.AUTH_ENDPOINT + '/invalidate',
|
||||||
|
{
|
||||||
|
json: true,
|
||||||
|
body: {
|
||||||
|
accessToken,
|
||||||
|
clientToken
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(error, response, body){
|
||||||
|
if(error){
|
||||||
|
Mojang.logger.error('Error during invalidation.', error)
|
||||||
|
reject(error)
|
||||||
|
} else {
|
||||||
|
if(response.statusCode === 204){
|
||||||
|
resolve()
|
||||||
|
} else {
|
||||||
|
reject(body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh a user's authentication. This should be used to keep a user logged
|
||||||
|
* in without asking them for their credentials again. A new access token will
|
||||||
|
* be generated using a recent invalid access token. See Wiki for more info.
|
||||||
|
*
|
||||||
|
* @param {string} accessToken The old access token.
|
||||||
|
* @param {string} clientToken The launcher's client token.
|
||||||
|
* @param {boolean} requestUser Optional. Adds user object to the reponse.
|
||||||
|
*
|
||||||
|
* @see http://wiki.vg/Authentication#Refresh
|
||||||
|
*/
|
||||||
|
public static refresh(accessToken: string, clientToken: string, requestUser: boolean = true): Promise<Session> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request.post(Mojang.AUTH_ENDPOINT + '/refresh',
|
||||||
|
{
|
||||||
|
json: true,
|
||||||
|
body: {
|
||||||
|
accessToken,
|
||||||
|
clientToken,
|
||||||
|
requestUser
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(error, response, body){
|
||||||
|
if(error){
|
||||||
|
Mojang.logger.error('Error during refresh.', error)
|
||||||
|
reject(error)
|
||||||
|
} else {
|
||||||
|
if(response.statusCode === 200){
|
||||||
|
resolve(body)
|
||||||
|
} else {
|
||||||
|
reject(body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
8
src/mojang/type/Status.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export interface Status {
|
||||||
|
|
||||||
|
service: string
|
||||||
|
status: 'red' | 'yellow' | 'green' | 'grey'
|
||||||
|
name: string
|
||||||
|
essential: boolean
|
||||||
|
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
const {ipcRenderer} = require('electron')
|
import { ConfigManager } from './configmanager'
|
||||||
const fs = require('fs-extra')
|
import { DistroManager, DistributionWrapper } from './distromanager'
|
||||||
const os = require('os')
|
import { join } from 'path'
|
||||||
const path = require('path')
|
import { remove } from 'fs-extra'
|
||||||
|
import { loadLanguage } from './langloader'
|
||||||
|
import { LoggerUtil } from './loggerutil'
|
||||||
|
import { tmpdir } from 'os'
|
||||||
|
import { ipcRenderer } from 'electron'
|
||||||
|
|
||||||
const ConfigManager = require('./configmanager')
|
const logger = new LoggerUtil('%c[Preloader]', 'color: #a02d2a; font-weight: bold')
|
||||||
const DistroManager = require('./distromanager')
|
|
||||||
const LangLoader = require('./langloader')
|
|
||||||
const logger = require('./loggerutil')('%c[Preloader]', 'color: #a02d2a; font-weight: bold')
|
|
||||||
|
|
||||||
logger.log('Loading..')
|
logger.log('Loading..')
|
||||||
|
|
||||||
@ -14,15 +15,16 @@ logger.log('Loading..')
|
|||||||
ConfigManager.load()
|
ConfigManager.load()
|
||||||
|
|
||||||
// Load Strings
|
// Load Strings
|
||||||
LangLoader.loadLanguage('en_US')
|
loadLanguage('en_US')
|
||||||
|
|
||||||
function onDistroLoad(data){
|
function onDistroLoad(data: DistributionWrapper | null){
|
||||||
if(data != null){
|
if(data != null){
|
||||||
|
|
||||||
// Resolve the selected server if its value has yet to be set.
|
// Resolve the selected server if its value has yet to be set.
|
||||||
if(ConfigManager.getSelectedServer() == null || data.getServer(ConfigManager.getSelectedServer()) == null){
|
if(ConfigManager.getSelectedServer() == null || data.getServer(ConfigManager.getSelectedServer()!) == null){
|
||||||
logger.log('Determining default selected server..')
|
logger.log('Determining default selected server..')
|
||||||
ConfigManager.setSelectedServer(data.getMainServer().getID())
|
// TODO what if undefined
|
||||||
|
ConfigManager.setSelectedServer(data.getMainServer()!.server.id as string)
|
||||||
ConfigManager.save()
|
ConfigManager.save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,10 +62,10 @@ DistroManager.pullRemote().then((data) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Clean up temp dir incase previous launches ended unexpectedly.
|
// Clean up temp dir incase previous launches ended unexpectedly.
|
||||||
fs.remove(path.join(os.tmpdir(), ConfigManager.getTempNativeFolder()), (err) => {
|
remove(join(tmpdir(), ConfigManager.getTempNativeFolder()), (err) => {
|
||||||
if(err){
|
if(err){
|
||||||
logger.warn('Error while cleaning natives directory', err)
|
logger.warn('Error while cleaning natives directory', err)
|
||||||
} else {
|
} else {
|
||||||
logger.log('Cleaned natives directory.')
|
logger.log('Cleaned natives directory.')
|
||||||
}
|
}
|
||||||
})
|
})
|
755
src/processbuilder.ts
Normal file
@ -0,0 +1,755 @@
|
|||||||
|
import AdmZip from 'adm-zip'
|
||||||
|
import { pathExistsSync, writeFile, ensureDirSync, writeFileSync, remove } from 'fs-extra'
|
||||||
|
import { join, basename } from 'path'
|
||||||
|
import { ModuleWrapper, ServerWrapper } from './distromanager'
|
||||||
|
import { Type, Required } from 'helios-distribution-types'
|
||||||
|
import { LoggerUtil } from './loggerutil'
|
||||||
|
import { ConfigManager } from './configmanager'
|
||||||
|
import { spawn } from 'child_process'
|
||||||
|
import { SavedAccount } from './model/internal/config/SavedAccount'
|
||||||
|
import { tmpdir, release } from 'os'
|
||||||
|
import { SubModConfig } from './model/internal/config/ModConfig'
|
||||||
|
import { pseudoRandomBytes } from 'crypto'
|
||||||
|
import { Util, LibraryInternal } from './assetguard'
|
||||||
|
import { VersionJson, Rule } from './model/mojang/index/VersionJson'
|
||||||
|
import { URL } from 'url'
|
||||||
|
|
||||||
|
const logger = new LoggerUtil('%c[ProcessBuilder]', 'color: #003996; font-weight: bold')
|
||||||
|
|
||||||
|
export class ProcessBuilder {
|
||||||
|
|
||||||
|
private gameDir: string
|
||||||
|
private commonDir: string
|
||||||
|
private fmlDir: string
|
||||||
|
private llDir: string
|
||||||
|
private libPath: string
|
||||||
|
|
||||||
|
private usingLiteLoader: boolean
|
||||||
|
private llPath: string | null
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private wrappedServer: ServerWrapper,
|
||||||
|
private versionData: VersionJson,
|
||||||
|
private forgeData: any, // TODO type
|
||||||
|
private authUser: SavedAccount,
|
||||||
|
private launcherVersion: string
|
||||||
|
){
|
||||||
|
this.gameDir = join(ConfigManager.getInstanceDirectory(), wrappedServer.server.id)
|
||||||
|
this.commonDir = ConfigManager.getCommonDirectory()
|
||||||
|
this.authUser = authUser
|
||||||
|
this.launcherVersion = launcherVersion
|
||||||
|
this.fmlDir = join(this.gameDir, 'forgeModList.json')
|
||||||
|
this.llDir = join(this.gameDir, 'liteloaderModList.json')
|
||||||
|
this.libPath = join(this.commonDir, 'libraries')
|
||||||
|
|
||||||
|
this.usingLiteLoader = false
|
||||||
|
this.llPath = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convienence method to run the functions typically used to build a process.
|
||||||
|
*/
|
||||||
|
build(){
|
||||||
|
ensureDirSync(this.gameDir)
|
||||||
|
const tempNativePath = join(tmpdir(), ConfigManager.getTempNativeFolder(), pseudoRandomBytes(16).toString('hex'))
|
||||||
|
process.throwDeprecation = true
|
||||||
|
this.setupLiteLoader()
|
||||||
|
logger.log('Using liteloader:', this.usingLiteLoader)
|
||||||
|
const modObj = this.resolveModConfiguration(ConfigManager.getModConfiguration(this.wrappedServer.server.id)!.mods, this.wrappedServer.getWrappedModules())
|
||||||
|
|
||||||
|
// Mod list below 1.13
|
||||||
|
if(!Util.mcVersionAtLeast('1.13', this.wrappedServer.server.minecraftVersion)){
|
||||||
|
this.constructModList('forge', modObj.fMods, true)
|
||||||
|
if(this.usingLiteLoader){
|
||||||
|
this.constructModList('liteloader', modObj.lMods, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uberModArr = modObj.fMods.concat(modObj.lMods)
|
||||||
|
let args = this.constructJVMArguments(uberModArr, tempNativePath)
|
||||||
|
|
||||||
|
if(Util.mcVersionAtLeast('1.13', this.wrappedServer.server.minecraftVersion)){
|
||||||
|
args = args.concat(this.constructModArguments(modObj.fMods))
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('Launch Arguments:', args)
|
||||||
|
|
||||||
|
const child = spawn(ConfigManager.getJavaExecutable()!, args, {
|
||||||
|
cwd: this.gameDir,
|
||||||
|
detached: ConfigManager.getLaunchDetached()
|
||||||
|
})
|
||||||
|
|
||||||
|
if(ConfigManager.getLaunchDetached()){
|
||||||
|
child.unref()
|
||||||
|
}
|
||||||
|
|
||||||
|
child.stdout.setEncoding('utf8')
|
||||||
|
child.stderr.setEncoding('utf8')
|
||||||
|
|
||||||
|
const loggerMCstdout = new LoggerUtil('%c[Minecraft]', 'color: #36b030; font-weight: bold')
|
||||||
|
const loggerMCstderr = new LoggerUtil('%c[Minecraft]', 'color: #b03030; font-weight: bold')
|
||||||
|
|
||||||
|
child.stdout.on('data', (data) => {
|
||||||
|
loggerMCstdout.log(data)
|
||||||
|
})
|
||||||
|
child.stderr.on('data', (data) => {
|
||||||
|
loggerMCstderr.log(data)
|
||||||
|
})
|
||||||
|
child.on('close', (code, signal) => {
|
||||||
|
logger.log('Exited with code', code)
|
||||||
|
remove(tempNativePath, (err) => {
|
||||||
|
if(err){
|
||||||
|
logger.warn('Error while deleting temp dir', err)
|
||||||
|
} else {
|
||||||
|
logger.log('Temp dir deleted successfully.')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if an optional mod is enabled from its configuration value. If the
|
||||||
|
* configuration value is null, the required object will be used to
|
||||||
|
* determine if it is enabled.
|
||||||
|
*
|
||||||
|
* A mod is enabled if:
|
||||||
|
* * The configuration is not null and one of the following:
|
||||||
|
* * The configuration is a boolean and true.
|
||||||
|
* * The configuration is an object and its 'value' property is true.
|
||||||
|
* * The configuration is null and one of the following:
|
||||||
|
* * The required object is null.
|
||||||
|
* * The required object's 'def' property is null or true.
|
||||||
|
*
|
||||||
|
* @param {SubModConfig | boolean} modCfg The mod configuration object.
|
||||||
|
* @param {Required | undefined} required Optional. The required object from the mod's distro declaration.
|
||||||
|
* @returns {boolean} True if the mod is enabled, false otherwise.
|
||||||
|
*/
|
||||||
|
public static isModEnabled(modCfg: SubModConfig | boolean, required?: Required){
|
||||||
|
return modCfg != null ? ((typeof modCfg === 'boolean' && modCfg) || (typeof modCfg === 'object' && (typeof modCfg.value !== 'undefined' ? modCfg.value : true))) : required != null ? required.def : true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function which performs a preliminary scan of the top level
|
||||||
|
* mods. If liteloader is present here, we setup the special liteloader
|
||||||
|
* launch options. Note that liteloader is only allowed as a top level
|
||||||
|
* mod. It must not be declared as a submodule.
|
||||||
|
*/
|
||||||
|
private setupLiteLoader(): void {
|
||||||
|
for(const ll of this.wrappedServer.getWrappedModules()){
|
||||||
|
if(ll.module.type === Type.LiteLoader){
|
||||||
|
if(!ll.module.required!.value!){
|
||||||
|
const modCfg = ConfigManager.getModConfiguration(this.wrappedServer.server.id)!.mods
|
||||||
|
if(ProcessBuilder.isModEnabled(modCfg[ll.getVersionlessID()], ll.module.required)){
|
||||||
|
if(pathExistsSync(ll.module.artifact.path!)){
|
||||||
|
this.usingLiteLoader = true
|
||||||
|
this.llPath = ll.module.artifact.path!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(pathExistsSync(ll.module.artifact.path!)){
|
||||||
|
this.usingLiteLoader = true
|
||||||
|
this.llPath = ll.module.artifact.path!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve an array of all enabled mods. These mods will be constructed into
|
||||||
|
* a mod list format and enabled at launch.
|
||||||
|
*
|
||||||
|
* @param {{[id: string]: boolean | SubModConfig}} modCfg The mod configuration object.
|
||||||
|
* @param {Array.<ModuleWrapper>} mdls An array of modules to parse.
|
||||||
|
* @returns {{fMods: Array.<ModuleWrapper>, lMods: Array.<ModuleWrapper>}} An object which contains
|
||||||
|
* a list of enabled forge mods and litemods.
|
||||||
|
*/
|
||||||
|
resolveModConfiguration(modCfg: {[id: string]: boolean | SubModConfig}, mdls: ModuleWrapper[]): {fMods: ModuleWrapper[], lMods: ModuleWrapper[]}{
|
||||||
|
let fMods: ModuleWrapper[] = []
|
||||||
|
let lMods: ModuleWrapper[] = []
|
||||||
|
|
||||||
|
for(const mdl of mdls){
|
||||||
|
const type = mdl.module.type
|
||||||
|
if(type === Type.ForgeMod || type === Type.LiteMod || type === Type.LiteLoader){
|
||||||
|
const o = !mdl.module.required!.value!
|
||||||
|
const e = ProcessBuilder.isModEnabled(modCfg[mdl.getVersionlessID()], mdl.module.required)
|
||||||
|
if(!o || (o && e)){
|
||||||
|
if(mdl.hasSubModules()){
|
||||||
|
const v = this.resolveModConfiguration((modCfg[mdl.getVersionlessID()] as SubModConfig).mods, mdl.getWrappedSubmodules())
|
||||||
|
fMods = fMods.concat(v.fMods)
|
||||||
|
lMods = lMods.concat(v.lMods)
|
||||||
|
if(mdl.module.type === Type.LiteLoader){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(mdl.module.type === Type.ForgeMod){
|
||||||
|
fMods.push(mdl)
|
||||||
|
} else {
|
||||||
|
lMods.push(mdl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fMods,
|
||||||
|
lMods
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_isBelowOneDotSeven() {
|
||||||
|
return Number(this.forgeData.id.split('-')[0].split('.')[1]) <= 7
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test to see if this version of forge requires the absolute: prefix
|
||||||
|
* on the modListFile repository field.
|
||||||
|
*/
|
||||||
|
_requiresAbsolute(){
|
||||||
|
try {
|
||||||
|
if(this._isBelowOneDotSeven()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const ver = this.forgeData.id.split('-')[2]
|
||||||
|
const pts = ver.split('.')
|
||||||
|
const min = [14, 23, 3, 2655]
|
||||||
|
for(let i=0; i<pts.length; i++){
|
||||||
|
const parsed = Number.parseInt(pts[i])
|
||||||
|
if(parsed < min[i]){
|
||||||
|
return false
|
||||||
|
} else if(parsed > min[i]){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// We know old forge versions follow this format.
|
||||||
|
// Error must be caused by newer version.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal or errored
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a mod list json object.
|
||||||
|
*
|
||||||
|
* @param {'forge' | 'liteloader'} type The mod list type to construct.
|
||||||
|
* @param {Array.<ModuleWrapper>} mods An array of mods to add to the mod list.
|
||||||
|
* @param {boolean} save Optional. Whether or not we should save the mod list file.
|
||||||
|
*/
|
||||||
|
constructModList(type: 'forge' | 'liteloader', mods: ModuleWrapper[], save = false){
|
||||||
|
const modList = {
|
||||||
|
repositoryRoot: ((type === 'forge' && this._requiresAbsolute()) ? 'absolute:' : '') + join(this.commonDir, 'modstore'),
|
||||||
|
modRef: [] as string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const ids = []
|
||||||
|
if(type === 'forge'){
|
||||||
|
for(let mod of mods){
|
||||||
|
ids.push(mod.getExtensionlessID())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(let mod of mods){
|
||||||
|
ids.push(mod.getExtensionlessID() + '@' + mod.getExtension())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modList.modRef = ids
|
||||||
|
|
||||||
|
if(save){
|
||||||
|
const json = JSON.stringify(modList, null, 4)
|
||||||
|
writeFileSync(type === 'forge' ? this.fmlDir : this.llDir, json, 'UTF-8')
|
||||||
|
}
|
||||||
|
|
||||||
|
return modList
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the mod argument list for forge 1.13
|
||||||
|
*
|
||||||
|
* @param {Array.<ModuleWrapper>} mods An array of mods to add to the mod list.
|
||||||
|
*/
|
||||||
|
constructModArguments(mods: ModuleWrapper[]){
|
||||||
|
const argStr = mods.map(mod => {
|
||||||
|
return mod.getExtensionlessID()
|
||||||
|
}).join(',')
|
||||||
|
|
||||||
|
if(argStr){
|
||||||
|
return [
|
||||||
|
'--fml.mavenRoots',
|
||||||
|
join('..', '..', 'common', 'modstore'),
|
||||||
|
'--fml.mods',
|
||||||
|
argStr
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the argument array that will be passed to the JVM process.
|
||||||
|
*
|
||||||
|
* @param {Array.<ModuleWrapper>} mods An array of enabled mods which will be launched with this process.
|
||||||
|
* @param {string} tempNativePath The path to store the native libraries.
|
||||||
|
* @returns {Array.<string| number>} An array containing the full JVM arguments for this process.
|
||||||
|
*/
|
||||||
|
constructJVMArguments(mods: ModuleWrapper[], tempNativePath: string): string[] {
|
||||||
|
if(Util.mcVersionAtLeast('1.13', this.wrappedServer.server.minecraftVersion)){
|
||||||
|
return this._constructJVMArguments113(mods, tempNativePath)
|
||||||
|
} else {
|
||||||
|
return this._constructJVMArguments112(mods, tempNativePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the argument array that will be passed to the JVM process.
|
||||||
|
* This function is for 1.12 and below.
|
||||||
|
*
|
||||||
|
* @param {Array.<ModuleWrapper>} mods An array of enabled mods which will be launched with this process.
|
||||||
|
* @param {string} tempNativePath The path to store the native libraries.
|
||||||
|
* @returns {Array.<string>} An array containing the full JVM arguments for this process.
|
||||||
|
*/
|
||||||
|
_constructJVMArguments112(mods: ModuleWrapper[], tempNativePath: string): string[] {
|
||||||
|
|
||||||
|
let args = []
|
||||||
|
|
||||||
|
// Classpath Argument
|
||||||
|
args.push('-cp')
|
||||||
|
args.push(this.classpathArg(mods, tempNativePath).join(process.platform === 'win32' ? ';' : ':'))
|
||||||
|
|
||||||
|
// Java Arguments
|
||||||
|
if(process.platform === 'darwin'){
|
||||||
|
args.push('-Xdock:name=HeliosLauncher')
|
||||||
|
args.push('-Xdock:icon=' + join(__dirname, '..', 'images', 'minecraft.icns'))
|
||||||
|
}
|
||||||
|
args.push('-Xmx' + ConfigManager.getMaxRAM())
|
||||||
|
args.push('-Xms' + ConfigManager.getMinRAM())
|
||||||
|
args = args.concat(ConfigManager.getJVMOptions())
|
||||||
|
args.push('-Djava.library.path=' + tempNativePath)
|
||||||
|
|
||||||
|
// Main Java Class
|
||||||
|
args.push(this.forgeData.mainClass)
|
||||||
|
|
||||||
|
// Forge Arguments
|
||||||
|
args = args.concat(this._resolveForgeArgs())
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the argument array that will be passed to the JVM process.
|
||||||
|
* This function is for 1.13+
|
||||||
|
*
|
||||||
|
* Note: Required Libs https://github.com/MinecraftForge/MinecraftForge/blob/af98088d04186452cb364280340124dfd4766a5c/src/fmllauncher/java/net/minecraftforge/fml/loading/LibraryFinder.java#L82
|
||||||
|
*
|
||||||
|
* @param {Array.<ModuleWrapper>} mods An array of enabled mods which will be launched with this process.
|
||||||
|
* @param {string} tempNativePath The path to store the native libraries.
|
||||||
|
* @returns {Array.<string>} An array containing the full JVM arguments for this process.
|
||||||
|
*/
|
||||||
|
_constructJVMArguments113(mods: ModuleWrapper[], tempNativePath: string): string[] {
|
||||||
|
|
||||||
|
const argDiscovery = /\${*(.*)}/
|
||||||
|
|
||||||
|
// JVM Arguments First
|
||||||
|
let args: (string | { rules: Rule[], value: string[] })[] = this.versionData.arguments.jvm
|
||||||
|
|
||||||
|
//args.push('-Dlog4j.configurationFile=D:\\WesterosCraft\\game\\common\\assets\\log_configs\\client-1.12.xml')
|
||||||
|
|
||||||
|
// Java Arguments
|
||||||
|
if(process.platform === 'darwin'){
|
||||||
|
args.push('-Xdock:name=HeliosLauncher')
|
||||||
|
args.push('-Xdock:icon=' + join(__dirname, '..', 'images', 'minecraft.icns'))
|
||||||
|
}
|
||||||
|
args.push('-Xmx' + ConfigManager.getMaxRAM())
|
||||||
|
args.push('-Xms' + ConfigManager.getMinRAM())
|
||||||
|
args = args.concat(ConfigManager.getJVMOptions())
|
||||||
|
|
||||||
|
// Main Java Class
|
||||||
|
args.push(this.forgeData.mainClass)
|
||||||
|
|
||||||
|
// Vanilla Arguments
|
||||||
|
args = args.concat(this.versionData.arguments.game)
|
||||||
|
|
||||||
|
for(let i=0; i<args.length; i++){
|
||||||
|
if(typeof args[i] === 'object' && (args[i] as any).rules != null){
|
||||||
|
const arg = args[i] as { rules: Rule[], value: string[] }
|
||||||
|
|
||||||
|
let checksum = 0
|
||||||
|
for(let rule of arg.rules){
|
||||||
|
if(rule.os != null){
|
||||||
|
if(rule.os.name === LibraryInternal.mojangFriendlyOS()
|
||||||
|
&& (rule.os.version == null || new RegExp(rule.os.version).test(release()))){
|
||||||
|
if(rule.action === 'allow'){
|
||||||
|
checksum++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(rule.action === 'disallow'){
|
||||||
|
checksum++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(rule.features != null){
|
||||||
|
// We don't have many 'features' in the index at the moment.
|
||||||
|
// This should be fine for a while.
|
||||||
|
if(rule.features.has_custom_resolution != null && rule.features.has_custom_resolution === true){
|
||||||
|
if(ConfigManager.getFullscreen()){
|
||||||
|
arg.value = [
|
||||||
|
'--fullscreen',
|
||||||
|
'true'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
checksum++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO splice not push
|
||||||
|
if(checksum === arg.rules.length){
|
||||||
|
if(typeof arg.value === 'string'){
|
||||||
|
args[i] = arg.value
|
||||||
|
} else if(typeof arg.value === 'object'){
|
||||||
|
//args = args.concat(args[i].value)
|
||||||
|
args.splice(i, 1, ...arg.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement i to reprocess the resolved value
|
||||||
|
i--
|
||||||
|
} else {
|
||||||
|
args[i] = null! // TODO lol
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(typeof args[i] === 'string'){
|
||||||
|
const arg = args[i] as string
|
||||||
|
if(argDiscovery.test(arg)){
|
||||||
|
const identifier = arg.match(argDiscovery)![1]
|
||||||
|
let val = null
|
||||||
|
switch(identifier){
|
||||||
|
case 'auth_player_name':
|
||||||
|
val = this.authUser.displayName.trim()
|
||||||
|
break
|
||||||
|
case 'version_name':
|
||||||
|
//val = versionData.id
|
||||||
|
val = this.wrappedServer.server.id
|
||||||
|
break
|
||||||
|
case 'game_directory':
|
||||||
|
val = this.gameDir
|
||||||
|
break
|
||||||
|
case 'assets_root':
|
||||||
|
val = join(this.commonDir, 'assets')
|
||||||
|
break
|
||||||
|
case 'assets_index_name':
|
||||||
|
val = this.versionData.assets
|
||||||
|
break
|
||||||
|
case 'auth_uuid':
|
||||||
|
val = this.authUser.uuid.trim()
|
||||||
|
break
|
||||||
|
case 'auth_access_token':
|
||||||
|
val = this.authUser.accessToken
|
||||||
|
break
|
||||||
|
case 'user_type':
|
||||||
|
val = 'mojang'
|
||||||
|
break
|
||||||
|
case 'version_type':
|
||||||
|
val = this.versionData.type
|
||||||
|
break
|
||||||
|
case 'resolution_width':
|
||||||
|
val = ConfigManager.getGameWidth()
|
||||||
|
break
|
||||||
|
case 'resolution_height':
|
||||||
|
val = ConfigManager.getGameHeight()
|
||||||
|
break
|
||||||
|
case 'natives_directory':
|
||||||
|
val = arg.replace(argDiscovery, tempNativePath)
|
||||||
|
break
|
||||||
|
case 'launcher_name':
|
||||||
|
val = arg.replace(argDiscovery, 'Helios-Launcher')
|
||||||
|
break
|
||||||
|
case 'launcher_version':
|
||||||
|
val = arg.replace(argDiscovery, this.launcherVersion)
|
||||||
|
break
|
||||||
|
case 'classpath':
|
||||||
|
val = this.classpathArg(mods, tempNativePath).join(process.platform === 'win32' ? ';' : ':')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if(val != null){
|
||||||
|
args[i] = val.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forge Specific Arguments
|
||||||
|
args = args.concat(this.forgeData.arguments.game)
|
||||||
|
|
||||||
|
// Filter null values
|
||||||
|
args = args.filter(arg => {
|
||||||
|
return arg != null
|
||||||
|
})
|
||||||
|
|
||||||
|
return args as string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the arguments required by forge.
|
||||||
|
*
|
||||||
|
* @returns {Array.<string>} An array containing the arguments required by forge.
|
||||||
|
*/
|
||||||
|
_resolveForgeArgs(): string[] {
|
||||||
|
const mcArgs: string[] = 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.displayName.trim()
|
||||||
|
break
|
||||||
|
case 'version_name':
|
||||||
|
//val = versionData.id
|
||||||
|
val = this.wrappedServer.server.id
|
||||||
|
break
|
||||||
|
case 'game_directory':
|
||||||
|
val = this.gameDir
|
||||||
|
break
|
||||||
|
case 'assets_root':
|
||||||
|
val = join(this.commonDir, 'assets')
|
||||||
|
break
|
||||||
|
case 'assets_index_name':
|
||||||
|
val = this.versionData.assets
|
||||||
|
break
|
||||||
|
case 'auth_uuid':
|
||||||
|
val = this.authUser.uuid.trim()
|
||||||
|
break
|
||||||
|
case 'auth_access_token':
|
||||||
|
val = this.authUser.accessToken
|
||||||
|
break
|
||||||
|
case 'user_type':
|
||||||
|
val = 'mojang'
|
||||||
|
break
|
||||||
|
case 'user_properties': // 1.8.9 and below.
|
||||||
|
val = '{}'
|
||||||
|
break
|
||||||
|
case 'version_type':
|
||||||
|
val = this.versionData.type
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if(val != null){
|
||||||
|
mcArgs[i] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autoconnect to the selected server.
|
||||||
|
if(ConfigManager.getAutoConnect() && this.wrappedServer.server.autoconnect){
|
||||||
|
const serverURL = new URL('my://' + this.wrappedServer.server.address)
|
||||||
|
mcArgs.push('--server')
|
||||||
|
mcArgs.push(serverURL.hostname)
|
||||||
|
if(serverURL.port){
|
||||||
|
mcArgs.push('--port')
|
||||||
|
mcArgs.push(serverURL.port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare game resolution
|
||||||
|
if(ConfigManager.getFullscreen()){
|
||||||
|
mcArgs.push('--fullscreen')
|
||||||
|
mcArgs.push('true')
|
||||||
|
} else {
|
||||||
|
mcArgs.push('--width')
|
||||||
|
mcArgs.push(ConfigManager.getGameWidth().toString())
|
||||||
|
mcArgs.push('--height')
|
||||||
|
mcArgs.push(ConfigManager.getGameHeight().toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mod List File Argument
|
||||||
|
mcArgs.push('--modListFile')
|
||||||
|
if(this._isBelowOneDotSeven()) {
|
||||||
|
mcArgs.push(basename(this.fmlDir))
|
||||||
|
} else {
|
||||||
|
mcArgs.push('absolute:' + this.fmlDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// LiteLoader
|
||||||
|
if(this.usingLiteLoader){
|
||||||
|
mcArgs.push('--modRepo')
|
||||||
|
mcArgs.push(this.llDir)
|
||||||
|
|
||||||
|
// Set first arg to liteloader tweak class
|
||||||
|
mcArgs.unshift('com.mumfrey.liteloader.launch.LiteLoaderTweaker')
|
||||||
|
mcArgs.unshift('--tweakClass')
|
||||||
|
}
|
||||||
|
|
||||||
|
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.<ModuleWrapper>} mods An array of enabled mods which will be launched with this process.
|
||||||
|
* @param {string} tempNativePath The path to store the native libraries.
|
||||||
|
* @returns {Array.<string>} An array containing the paths of each library required by this process.
|
||||||
|
*/
|
||||||
|
classpathArg(mods: ModuleWrapper[], tempNativePath: string): string[] {
|
||||||
|
let cpArgs: string[] = []
|
||||||
|
|
||||||
|
// Add the version.jar to the classpath.
|
||||||
|
const version = this.versionData.id
|
||||||
|
cpArgs.push(join(this.commonDir, 'versions', version, version + '.jar'))
|
||||||
|
|
||||||
|
if(this.usingLiteLoader){
|
||||||
|
cpArgs.push(this.llPath!)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the Mojang declared libraries.
|
||||||
|
const mojangLibs = this._resolveMojangLibraries(tempNativePath)
|
||||||
|
|
||||||
|
// Resolve the server declared libraries.
|
||||||
|
const servLibs = this._resolveServerLibraries(mods)
|
||||||
|
|
||||||
|
// Merge libraries, server libs with the same
|
||||||
|
// maven identifier will override the mojang ones.
|
||||||
|
// Ex. 1.7.10 forge overrides mojang's guava with newer version.
|
||||||
|
const finalLibs = {...mojangLibs, ...servLibs}
|
||||||
|
cpArgs = cpArgs.concat(Object.values(finalLibs))
|
||||||
|
|
||||||
|
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
|
||||||
|
*
|
||||||
|
* @param {string} tempNativePath The path to store the native libraries.
|
||||||
|
* @returns {{[id: string]: string}} An object containing the paths of each library mojang declares.
|
||||||
|
*/
|
||||||
|
_resolveMojangLibraries(tempNativePath: string): {[id: string]: string} {
|
||||||
|
const libs: {[id: string]: string} = {}
|
||||||
|
|
||||||
|
const libArr = this.versionData.libraries
|
||||||
|
ensureDirSync(tempNativePath)
|
||||||
|
for(let i=0; i<libArr.length; i++){
|
||||||
|
const lib = libArr[i]
|
||||||
|
if(LibraryInternal.validateRules(lib.rules, lib.natives)){
|
||||||
|
if(lib.natives == null){
|
||||||
|
const dlInfo = lib.downloads
|
||||||
|
const artifact = dlInfo.artifact
|
||||||
|
const to = join(this.libPath, artifact.path)
|
||||||
|
const versionIndependentId: string = lib.name.substring(0, lib.name.lastIndexOf(':'))
|
||||||
|
libs[versionIndependentId] = to
|
||||||
|
} else {
|
||||||
|
// Extract the native library.
|
||||||
|
const exclusionArr: string[] = lib.extract != null ? lib.extract.exclude : ['META-INF/']
|
||||||
|
// @ts-ignore
|
||||||
|
const artifact = lib.downloads.classifiers[lib.natives[LibraryInternal.mojangFriendlyOS()].replace('${arch}', process.arch.replace('x', ''))]
|
||||||
|
|
||||||
|
// Location of native zip.
|
||||||
|
const to = 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((exclusion: string) => {
|
||||||
|
if(fileName.indexOf(exclusion) > -1){
|
||||||
|
shouldExclude = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Extract the file.
|
||||||
|
if(!shouldExclude){
|
||||||
|
writeFile(join(tempNativePath, fileName), zipEntries[i].getData(), (err) => {
|
||||||
|
if(err){
|
||||||
|
logger.error('Error while extracting native library:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.<ModuleWrapper>} mods An array of enabled mods which will be launched with this process.
|
||||||
|
* @returns {{[id: string]: string}} An object containing the paths of each library this server requires.
|
||||||
|
*/
|
||||||
|
_resolveServerLibraries(mods: ModuleWrapper[]): {[id: string]: string} {
|
||||||
|
const mdls: ModuleWrapper[] = this.wrappedServer.getWrappedModules()
|
||||||
|
let libs: {[id: string]: string} = {}
|
||||||
|
|
||||||
|
// Locate Forge/Libraries
|
||||||
|
for(let mdl of mdls){
|
||||||
|
const type = mdl.module.type
|
||||||
|
if(type === Type.ForgeHosted || type === Type.Library){
|
||||||
|
libs[mdl.getVersionlessID()] = mdl.module.artifact.path as string
|
||||||
|
if(mdl.hasSubModules()){
|
||||||
|
const res = this._resolveModuleLibraries(mdl)
|
||||||
|
if(Object.keys(res).length > 0){
|
||||||
|
libs = {...libs, ...res}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check for any libraries in our mod list.
|
||||||
|
for(let i=0; i<mods.length; i++){
|
||||||
|
if(mods[i].hasSubModules()){
|
||||||
|
const res = this._resolveModuleLibraries(mods[i])
|
||||||
|
if(Object.keys(res).length > 0){
|
||||||
|
libs = {...libs, ...res}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return libs
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively resolve the path of each library required by this module.
|
||||||
|
*
|
||||||
|
* @param {ModuleWrapper} mdl A module object from the server distro index.
|
||||||
|
* @returns {Array.<string>} An array containing the paths of each library this module requires.
|
||||||
|
*/
|
||||||
|
_resolveModuleLibraries(mdl: ModuleWrapper): {[id: string]: string} {
|
||||||
|
if(!mdl.hasSubModules()){
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
let libs: {[id: string]: string} = {}
|
||||||
|
for(const sm of mdl.getWrappedSubmodules()){
|
||||||
|
if(sm.module.type === Type.Library){
|
||||||
|
libs[sm.getVersionlessID()] = sm.module.artifact.path as string
|
||||||
|
}
|
||||||
|
// If this module has submodules, we need to resolve the libraries for those.
|
||||||
|
// To avoid unnecessary recursive calls, base case is checked here.
|
||||||
|
if(mdl.hasSubModules()){
|
||||||
|
const res = this._resolveModuleLibraries(sm)
|
||||||
|
if(Object.keys(res).length > 0){
|
||||||
|
libs = {...libs, ...res}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return libs
|
||||||
|
}
|
||||||
|
}
|
@ -7,10 +7,10 @@ const crypto = require('crypto')
|
|||||||
const {URL} = require('url')
|
const {URL} = require('url')
|
||||||
|
|
||||||
// Internal Requirements
|
// Internal Requirements
|
||||||
const DiscordWrapper = require('./assets/js/discordwrapper')
|
const DiscordWrapper = require('./../discordwrapper')
|
||||||
const Mojang = require('./assets/js/mojang')
|
const Mojang = require('./../mojang/mojang')
|
||||||
const ProcessBuilder = require('./assets/js/processbuilder')
|
const ProcessBuilder = require('./../processbuilder')
|
||||||
const ServerStatus = require('./assets/js/serverstatus')
|
const ServerStatus = require('./../serverstatus')
|
||||||
|
|
||||||
// Launch Elements
|
// Launch Elements
|
||||||
const launch_content = document.getElementById('launch_content')
|
const launch_content = document.getElementById('launch_content')
|
@ -21,7 +21,7 @@ const loginForm = document.getElementById('loginForm')
|
|||||||
// Control variables.
|
// Control variables.
|
||||||
let lu = false, lp = false
|
let lu = false, lp = false
|
||||||
|
|
||||||
const loggerLogin = LoggerUtil('%c[Login]', 'color: #000668; font-weight: bold')
|
const loggerLogin = new LoggerUtil('%c[Login]', 'color: #000668; font-weight: bold')
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
@ -2,8 +2,8 @@
|
|||||||
const os = require('os')
|
const os = require('os')
|
||||||
const semver = require('semver')
|
const semver = require('semver')
|
||||||
|
|
||||||
const { JavaGuard } = require('./assets/js/assetguard')
|
const { JavaGuard } = require('./../assetguard')
|
||||||
const DropinModUtil = require('./assets/js/dropinmodutil')
|
const DropinModUtil = require('./../dropinmodutil')
|
||||||
|
|
||||||
const settingsState = {
|
const settingsState = {
|
||||||
invalid: new Set()
|
invalid: new Set()
|
@ -5,10 +5,10 @@
|
|||||||
// Requirements
|
// Requirements
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
const AuthManager = require('./assets/js/authmanager')
|
const { AuthManager } = require('./../authmanager')
|
||||||
const ConfigManager = require('./assets/js/configmanager')
|
const ConfigManager = require('./../configmanager')
|
||||||
const DistroManager = require('./assets/js/distromanager')
|
const DistroManager = require('./../distromanager')
|
||||||
const Lang = require('./assets/js/langloader')
|
const Lang = require('./../langloader')
|
||||||
|
|
||||||
let rscShouldLoad = false
|
let rscShouldLoad = false
|
||||||
let fatalStartupError = false
|
let fatalStartupError = false
|
@ -1,18 +1,18 @@
|
|||||||
|
import $ from 'jquery'
|
||||||
|
import { ipcRenderer, remote, shell, webFrame } from 'electron'
|
||||||
|
import { LoggerUtil } from '../loggerutil'
|
||||||
|
import isDev from '../isdev'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core UI functions are initialized in this file. This prevents
|
* Core UI functions are initialized in this file. This prevents
|
||||||
* unexpected errors from breaking the core features. Specifically,
|
* unexpected errors from breaking the core features. Specifically,
|
||||||
* actions in this file should not require the usage of any internal
|
* actions in this file should not require the usage of any internal
|
||||||
* modules, excluding dependencies.
|
* modules, excluding dependencies.
|
||||||
*/
|
*/
|
||||||
// Requirements
|
|
||||||
const $ = require('jquery')
|
|
||||||
const {ipcRenderer, remote, shell, webFrame} = require('electron')
|
|
||||||
const isDev = require('./assets/js/isdev')
|
|
||||||
const LoggerUtil = require('./assets/js/loggerutil')
|
|
||||||
|
|
||||||
const loggerUICore = LoggerUtil('%c[UICore]', 'color: #000668; font-weight: bold')
|
const loggerUICore = new LoggerUtil('%c[UICore]', 'color: #000668; font-weight: bold')
|
||||||
const loggerAutoUpdater = LoggerUtil('%c[AutoUpdater]', 'color: #000668; font-weight: bold')
|
const loggerAutoUpdater = new LoggerUtil('%c[AutoUpdater]', 'color: #000668; font-weight: bold')
|
||||||
const loggerAutoUpdaterSuccess = LoggerUtil('%c[AutoUpdater]', 'color: #209b07; font-weight: bold')
|
const loggerAutoUpdaterSuccess = new LoggerUtil('%c[AutoUpdater]', 'color: #209b07; font-weight: bold')
|
||||||
|
|
||||||
// Log deprecation and process warnings.
|
// Log deprecation and process warnings.
|
||||||
process.traceProcessWarnings = true
|
process.traceProcessWarnings = true
|
@ -1,4 +1,4 @@
|
|||||||
const net = require('net')
|
import { connect } from 'net'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the status of a minecraft server.
|
* Retrieves the status of a minecraft server.
|
||||||
@ -8,17 +8,19 @@ const net = require('net')
|
|||||||
* @returns {Promise.<Object>} A promise which resolves to an object containing
|
* @returns {Promise.<Object>} A promise which resolves to an object containing
|
||||||
* status information.
|
* status information.
|
||||||
*/
|
*/
|
||||||
exports.getStatus = function(address, port = 25565){
|
export function getStatus(address: string, port: number | string = 25565){
|
||||||
|
|
||||||
|
let sanitizedPort: number
|
||||||
|
|
||||||
if(port == null || port == ''){
|
if(port == null || port == ''){
|
||||||
port = 25565
|
sanitizedPort = 25565
|
||||||
}
|
}
|
||||||
if(typeof port === 'string'){
|
if(typeof port === 'string'){
|
||||||
port = parseInt(port)
|
sanitizedPort = parseInt(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const socket = net.connect(port, address, () => {
|
const socket = connect(sanitizedPort, address, () => {
|
||||||
let buff = Buffer.from([0xFE, 0x01])
|
let buff = Buffer.from([0xFE, 0x01])
|
||||||
socket.write(buff)
|
socket.write(buff)
|
||||||
})
|
})
|
||||||
@ -29,12 +31,12 @@ exports.getStatus = function(address, port = 25565){
|
|||||||
code: 'ETIMEDOUT',
|
code: 'ETIMEDOUT',
|
||||||
errno: 'ETIMEDOUT',
|
errno: 'ETIMEDOUT',
|
||||||
address,
|
address,
|
||||||
port
|
sanitizedPort
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('data', (data) => {
|
socket.on('data', (data) => {
|
||||||
if(data != null && data != ''){
|
if(data != null){
|
||||||
let server_info = data.toString().split('\x00\x00\x00')
|
let server_info = data.toString().split('\x00\x00\x00')
|
||||||
const NUM_FIELDS = 6
|
const NUM_FIELDS = 6
|
||||||
if(server_info != null && server_info.length >= NUM_FIELDS){
|
if(server_info != null && server_info.length >= NUM_FIELDS){
|
26
tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Basic Options */
|
||||||
|
"target": "ES2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||||
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||||
|
"lib": [ /* Specify library files to be included in the compilation. */
|
||||||
|
"ES2019",
|
||||||
|
"dom"
|
||||||
|
],
|
||||||
|
"allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
|
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||||
|
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||||
|
"sourceMap": false, /* Generates corresponding '.map' file. */
|
||||||
|
"outDir": "out", /* Redirect output structure to the directory. */
|
||||||
|
"baseUrl": ".", /* Base directory to resolve non-absolute module names. */
|
||||||
|
// "paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
|
// "*": ["node_modules/*"]
|
||||||
|
// },
|
||||||
|
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [ "node_modules" ]
|
||||||
|
}
|