Add files via upload

This commit is contained in:
MastermDEV 2020-06-18 18:19:14 -07:00 committed by GitHub
parent d84b162867
commit 6c0e851f2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 4113 additions and 4113 deletions

View File

@ -1,73 +1,73 @@
let target = require('./assetguard')[process.argv[2]]
if(target == null){
process.send({context: 'error', data: null, error: 'Invalid class name'})
console.error('Invalid class name passed to argv[2], cannot continue.')
process.exit(1)
}
let tracker = new target(...(process.argv.splice(3)))
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
//const tracker = new AssetGuard(process.argv[2], process.argv[3])
console.log('AssetExec Started')
// Temporary for debug purposes.
process.on('unhandledRejection', r => console.log(r))
let percent = 0
function assignListeners(){
tracker.on('validate', (data) => {
process.send({context: 'validate', data})
})
tracker.on('progress', (data, acc, total) => {
const currPercent = parseInt((acc/total) * 100)
if (currPercent !== percent) {
percent = currPercent
process.send({context: 'progress', data, value: acc, total, percent})
}
})
tracker.on('complete', (data, ...args) => {
process.send({context: 'complete', data, args})
})
tracker.on('error', (data, error) => {
process.send({context: 'error', data, error})
})
}
assignListeners()
process.on('message', (msg) => {
if(msg.task === 'execute'){
const func = msg.function
let nS = tracker[func] // Nonstatic context
let iS = target[func] // Static context
if(typeof nS === 'function' || typeof iS === 'function'){
const f = typeof nS === 'function' ? nS : iS
const res = f.apply(f === nS ? tracker : null, msg.argsArr)
if(res instanceof Promise){
res.then((v) => {
process.send({result: v, context: func})
}).catch((err) => {
process.send({result: err.message || err, context: func})
})
} else {
process.send({result: res, context: func})
}
} else {
process.send({context: 'error', data: null, error: `Function ${func} not found on ${process.argv[2]}`})
}
} else if(msg.task === 'changeContext'){
target = require('./assetguard')[msg.class]
if(target == null){
process.send({context: 'error', data: null, error: `Invalid class ${msg.class}`})
} else {
tracker = new target(...(msg.args))
assignListeners()
}
}
})
process.on('disconnect', () => {
console.log('AssetExec Disconnected')
process.exit(0)
let target = require('./assetguard')[process.argv[2]]
if(target == null){
process.send({context: 'error', data: null, error: 'Invalid class name'})
console.error('Invalid class name passed to argv[2], cannot continue.')
process.exit(1)
}
let tracker = new target(...(process.argv.splice(3)))
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
//const tracker = new AssetGuard(process.argv[2], process.argv[3])
console.log('AssetExec Started')
// Temporary for debug purposes.
process.on('unhandledRejection', r => console.log(r))
let percent = 0
function assignListeners(){
tracker.on('validate', (data) => {
process.send({context: 'validate', data})
})
tracker.on('progress', (data, acc, total) => {
const currPercent = parseInt((acc/total) * 100)
if (currPercent !== percent) {
percent = currPercent
process.send({context: 'progress', data, value: acc, total, percent})
}
})
tracker.on('complete', (data, ...args) => {
process.send({context: 'complete', data, args})
})
tracker.on('error', (data, error) => {
process.send({context: 'error', data, error})
})
}
assignListeners()
process.on('message', (msg) => {
if(msg.task === 'execute'){
const func = msg.function
let nS = tracker[func] // Nonstatic context
let iS = target[func] // Static context
if(typeof nS === 'function' || typeof iS === 'function'){
const f = typeof nS === 'function' ? nS : iS
const res = f.apply(f === nS ? tracker : null, msg.argsArr)
if(res instanceof Promise){
res.then((v) => {
process.send({result: v, context: func})
}).catch((err) => {
process.send({result: err.message || err, context: func})
})
} else {
process.send({result: res, context: func})
}
} else {
process.send({context: 'error', data: null, error: `Function ${func} not found on ${process.argv[2]}`})
}
} else if(msg.task === 'changeContext'){
target = require('./assetguard')[msg.class]
if(target == null){
process.send({context: 'error', data: null, error: `Invalid class ${msg.class}`})
} else {
tracker = new target(...(msg.args))
assignListeners()
}
}
})
process.on('disconnect', () => {
console.log('AssetExec Disconnected')
process.exit(0)
})

File diff suppressed because it is too large Load Diff

View File

@ -1,99 +1,99 @@
/**
* AuthManager
*
* This module aims to abstract login procedures. Results from Mojang's REST api
* are retrieved through our Mojang module. These results are processed and stored,
* if applicable, in the config using the ConfigManager. All login procedures should
* be made through this module.
*
* @module authmanager
*/
// Requirements
const ConfigManager = require('./configmanager')
const LoggerUtil = require('./loggerutil')
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
/**
* Add an account. This will authenticate the given credentials with Mojang's
* authserver. The resultant data will be stored as an auth account in the
* configuration database.
*
* @param {string} username The account username (email if migrated).
* @param {string} password The account password.
* @returns {Promise.<Object>} Promise which resolves the resolved authenticated account object.
*/
exports.addAccount = async function(username, password){
try {
const session = await Mojang.authenticate(username, password, ConfigManager.getClientToken())
if(session.selectedProfile != null){
const ret = ConfigManager.addAuthAccount(session.selectedProfile.id, session.accessToken, username, session.selectedProfile.name)
if(ConfigManager.getClientToken() == null){
ConfigManager.setClientToken(session.clientToken)
}
ConfigManager.save()
return ret
} else {
throw new Error('NotPaidAccount')
}
} catch (err){
return Promise.reject(err)
}
}
/**
* Remove an account. This will invalidate the access token associated
* with the account and then remove it from the database.
*
* @param {string} uuid The UUID of the account to be removed.
* @returns {Promise.<void>} Promise which resolves to void when the action is complete.
*/
exports.removeAccount = async function(uuid){
try {
const authAcc = ConfigManager.getAuthAccount(uuid)
await Mojang.invalidate(authAcc.accessToken, ConfigManager.getClientToken())
ConfigManager.removeAuthAccount(uuid)
ConfigManager.save()
return Promise.resolve()
} catch (err){
return Promise.reject(err)
}
}
/**
* Validate the selected account with Mojang's authserver. If the account is not valid,
* we will attempt to refresh the access token and update that value. If that fails, a
* new login will be required.
*
* **Function is WIP**
*
* @returns {Promise.<boolean>} Promise which resolves to true if the access token is valid,
* otherwise false.
*/
exports.validateSelected = async function(){
const current = ConfigManager.getSelectedAccount()
const isValid = await Mojang.validate(current.accessToken, ConfigManager.getClientToken())
if(!isValid){
try {
const session = await Mojang.refresh(current.accessToken, ConfigManager.getClientToken())
ConfigManager.updateAuthAccount(current.uuid, session.accessToken)
ConfigManager.save()
} catch(err) {
logger.debug('Error while validating selected profile:', err)
if(err && err.error === 'ForbiddenOperationException'){
// What do we do?
}
logger.log('Account access token is invalid.')
return false
}
loggerSuccess.log('Account access token validated.')
return true
} else {
loggerSuccess.log('Account access token validated.')
return true
}
/**
* AuthManager
*
* This module aims to abstract login procedures. Results from Mojang's REST api
* are retrieved through our Mojang module. These results are processed and stored,
* if applicable, in the config using the ConfigManager. All login procedures should
* be made through this module.
*
* @module authmanager
*/
// Requirements
const ConfigManager = require('./configmanager')
const LoggerUtil = require('./loggerutil')
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
/**
* Add an account. This will authenticate the given credentials with Mojang's
* authserver. The resultant data will be stored as an auth account in the
* configuration database.
*
* @param {string} username The account username (email if migrated).
* @param {string} password The account password.
* @returns {Promise.<Object>} Promise which resolves the resolved authenticated account object.
*/
exports.addAccount = async function(username, password){
try {
const session = await Mojang.authenticate(username, password, ConfigManager.getClientToken())
if(session.selectedProfile != null){
const ret = ConfigManager.addAuthAccount(session.selectedProfile.id, session.accessToken, username, session.selectedProfile.name)
if(ConfigManager.getClientToken() == null){
ConfigManager.setClientToken(session.clientToken)
}
ConfigManager.save()
return ret
} else {
throw new Error('NotPaidAccount')
}
} catch (err){
return Promise.reject(err)
}
}
/**
* Remove an account. This will invalidate the access token associated
* with the account and then remove it from the database.
*
* @param {string} uuid The UUID of the account to be removed.
* @returns {Promise.<void>} Promise which resolves to void when the action is complete.
*/
exports.removeAccount = async function(uuid){
try {
const authAcc = ConfigManager.getAuthAccount(uuid)
await Mojang.invalidate(authAcc.accessToken, ConfigManager.getClientToken())
ConfigManager.removeAuthAccount(uuid)
ConfigManager.save()
return Promise.resolve()
} catch (err){
return Promise.reject(err)
}
}
/**
* Validate the selected account with Mojang's authserver. If the account is not valid,
* we will attempt to refresh the access token and update that value. If that fails, a
* new login will be required.
*
* **Function is WIP**
*
* @returns {Promise.<boolean>} Promise which resolves to true if the access token is valid,
* otherwise false.
*/
exports.validateSelected = async function(){
const current = ConfigManager.getSelectedAccount()
const isValid = await Mojang.validate(current.accessToken, ConfigManager.getClientToken())
if(!isValid){
try {
const session = await Mojang.refresh(current.accessToken, ConfigManager.getClientToken())
ConfigManager.updateAuthAccount(current.uuid, session.accessToken)
ConfigManager.save()
} catch(err) {
logger.debug('Error while validating selected profile:', err)
if(err && err.error === 'ForbiddenOperationException'){
// What do we do?
}
logger.log('Account access token is invalid.')
return false
}
loggerSuccess.log('Account access token validated.')
return true
} else {
loggerSuccess.log('Account access token validated.')
return true
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +1,48 @@
// Work in progress
const logger = require('./loggerutil')('%c[DiscordWrapper]', 'color: #7289da; font-weight: bold')
const {Client} = require('discord-rpc')
let client
let activity
exports.initRPC = function(genSettings, servSettings, initialDetails = 'Waiting for Client..'){
client = new Client({ transport: 'ipc' })
activity = {
details: initialDetails,
state: 'Server: ' + servSettings.shortId,
largeImageKey: servSettings.largeImageKey,
largeImageText: servSettings.largeImageText,
smallImageKey: genSettings.smallImageKey,
smallImageText: genSettings.smallImageText,
startTimestamp: new Date().getTime(),
instance: false
}
client.on('ready', () => {
logger.log('Discord RPC Connected')
client.setActivity(activity)
})
client.login({clientId: genSettings.clientId}).catch(error => {
if(error.message.includes('ENOENT')) {
logger.log('Unable to initialize Discord Rich Presence, no client detected.')
} else {
logger.log('Unable to initialize Discord Rich Presence: ' + error.message, error)
}
})
}
exports.updateDetails = function(details){
activity.details = details
client.setActivity(activity)
}
exports.shutdownRPC = function(){
if(!client) return
client.clearActivity()
client.destroy()
client = null
activity = null
// Work in progress
const logger = require('./loggerutil')('%c[DiscordWrapper]', 'color: #7289da; font-weight: bold')
const {Client} = require('discord-rpc')
let client
let activity
exports.initRPC = function(genSettings, servSettings, initialDetails = 'Waiting for Client..'){
client = new Client({ transport: 'ipc' })
activity = {
details: initialDetails,
state: 'Server: ' + servSettings.shortId,
largeImageKey: servSettings.largeImageKey,
largeImageText: servSettings.largeImageText,
smallImageKey: genSettings.smallImageKey,
smallImageText: genSettings.smallImageText,
startTimestamp: new Date().getTime(),
instance: false
}
client.on('ready', () => {
logger.log('Discord RPC Connected')
client.setActivity(activity)
})
client.login({clientId: genSettings.clientId}).catch(error => {
if(error.message.includes('ENOENT')) {
logger.log('Unable to initialize Discord Rich Presence, no client detected.')
} else {
logger.log('Unable to initialize Discord Rich Presence: ' + error.message, error)
}
})
}
exports.updateDetails = function(details){
activity.details = details
client.setActivity(activity)
}
exports.shutdownRPC = function(){
if(!client) return
client.clearActivity()
client.destroy()
client = null
activity = null
}

File diff suppressed because it is too large Load Diff

View File

@ -1,232 +1,232 @@
const fs = require('fs-extra')
const path = require('path')
const { shell } = require('electron')
// Group #1: File Name (without .disabled, if any)
// Group #2: File Extension (jar, zip, or litemod)
// Group #3: If it is disabled (if string 'disabled' is present)
const MOD_REGEX = /^(.+(jar|zip|litemod))(?:\.(disabled))?$/
const DISABLED_EXT = '.disabled'
const SHADER_REGEX = /^(.+)\.zip$/
const SHADER_OPTION = /shaderPack=(.+)/
const SHADER_DIR = 'shaderpacks'
const SHADER_CONFIG = 'optionsshaders.txt'
/**
* Validate that the given directory exists. If not, it is
* created.
*
* @param {string} modsDir The path to the mods directory.
*/
exports.validateDir = function(dir) {
fs.ensureDirSync(dir)
}
/**
* Scan for drop-in mods in both the mods folder and version
* safe mods folder.
*
* @param {string} modsDir The path to the mods directory.
* @param {string} version The minecraft version of the server configuration.
*
* @returns {{fullName: string, name: string, ext: string, disabled: boolean}[]}
* An array of objects storing metadata about each discovered mod.
*/
exports.scanForDropinMods = function(modsDir, version) {
const modsDiscovered = []
if(fs.existsSync(modsDir)){
let modCandidates = fs.readdirSync(modsDir)
let verCandidates = []
const versionDir = path.join(modsDir, version)
if(fs.existsSync(versionDir)){
verCandidates = fs.readdirSync(versionDir)
}
for(let file of modCandidates){
const match = MOD_REGEX.exec(file)
if(match != null){
modsDiscovered.push({
fullName: match[0],
name: match[1],
ext: match[2],
disabled: match[3] != null
})
}
}
for(let file of verCandidates){
const match = MOD_REGEX.exec(file)
if(match != null){
modsDiscovered.push({
fullName: path.join(version, match[0]),
name: match[1],
ext: match[2],
disabled: match[3] != null
})
}
}
}
return modsDiscovered
}
/**
* Add dropin mods.
*
* @param {FileList} files The files to add.
* @param {string} modsDir The path to the mods directory.
*/
exports.addDropinMods = function(files, modsdir) {
exports.validateDir(modsdir)
for(let f of files) {
if(MOD_REGEX.exec(f.name) != null) {
fs.moveSync(f.path, path.join(modsdir, f.name))
}
}
}
/**
* Delete a drop-in mod from the file system.
*
* @param {string} modsDir The path to the mods directory.
* @param {string} fullName The fullName of the discovered mod to delete.
*
* @returns {boolean} True if the mod was deleted, otherwise false.
*/
exports.deleteDropinMod = function(modsDir, fullName){
const res = shell.moveItemToTrash(path.join(modsDir, fullName))
if(!res){
shell.beep()
}
return res
}
/**
* Toggle a discovered mod on or off. This is achieved by either
* adding or disabling the .disabled extension to the local file.
*
* @param {string} modsDir The path to the mods directory.
* @param {string} fullName The fullName of the discovered mod to toggle.
* @param {boolean} enable Whether to toggle on or off the mod.
*
* @returns {Promise.<void>} A promise which resolves when the mod has
* been toggled. If an IO error occurs the promise will be rejected.
*/
exports.toggleDropinMod = function(modsDir, fullName, enable){
return new Promise((resolve, reject) => {
const oldPath = path.join(modsDir, fullName)
const newPath = path.join(modsDir, enable ? fullName.substring(0, fullName.indexOf(DISABLED_EXT)) : fullName + DISABLED_EXT)
fs.rename(oldPath, newPath, (err) => {
if(err){
reject(err)
} else {
resolve()
}
})
})
}
/**
* Check if a drop-in mod is enabled.
*
* @param {string} fullName The fullName of the discovered mod to toggle.
* @returns {boolean} True if the mod is enabled, otherwise false.
*/
exports.isDropinModEnabled = function(fullName){
return !fullName.endsWith(DISABLED_EXT)
}
/**
* Scan for shaderpacks inside the shaderpacks folder.
*
* @param {string} instanceDir The path to the server instance directory.
*
* @returns {{fullName: string, name: string}[]}
* An array of objects storing metadata about each discovered shaderpack.
*/
exports.scanForShaderpacks = function(instanceDir){
const shaderDir = path.join(instanceDir, SHADER_DIR)
const packsDiscovered = [{
fullName: 'OFF',
name: 'Off (Default)'
}]
if(fs.existsSync(shaderDir)){
let modCandidates = fs.readdirSync(shaderDir)
for(let file of modCandidates){
const match = SHADER_REGEX.exec(file)
if(match != null){
packsDiscovered.push({
fullName: match[0],
name: match[1]
})
}
}
}
return packsDiscovered
}
/**
* Read the optionsshaders.txt file to locate the current
* enabled pack. If the file does not exist, OFF is returned.
*
* @param {string} instanceDir The path to the server instance directory.
*
* @returns {string} The file name of the enabled shaderpack.
*/
exports.getEnabledShaderpack = function(instanceDir){
exports.validateDir(instanceDir)
const optionsShaders = path.join(instanceDir, SHADER_CONFIG)
if(fs.existsSync(optionsShaders)){
const buf = fs.readFileSync(optionsShaders, {encoding: 'utf-8'})
const match = SHADER_OPTION.exec(buf)
if(match != null){
return match[1]
} else {
console.warn('WARNING: Shaderpack regex failed.')
}
}
return 'OFF'
}
/**
* Set the enabled shaderpack.
*
* @param {string} instanceDir The path to the server instance directory.
* @param {string} pack the file name of the shaderpack.
*/
exports.setEnabledShaderpack = function(instanceDir, pack){
exports.validateDir(instanceDir)
const optionsShaders = path.join(instanceDir, SHADER_CONFIG)
let buf
if(fs.existsSync(optionsShaders)){
buf = fs.readFileSync(optionsShaders, {encoding: 'utf-8'})
buf = buf.replace(SHADER_OPTION, `shaderPack=${pack}`)
} else {
buf = `shaderPack=${pack}`
}
fs.writeFileSync(optionsShaders, buf, {encoding: 'utf-8'})
}
/**
* Add shaderpacks.
*
* @param {FileList} files The files to add.
* @param {string} instanceDir The path to the server instance directory.
*/
exports.addShaderpacks = function(files, instanceDir) {
const p = path.join(instanceDir, SHADER_DIR)
exports.validateDir(p)
for(let f of files) {
if(SHADER_REGEX.exec(f.name) != null) {
fs.moveSync(f.path, path.join(p, f.name))
}
}
const fs = require('fs-extra')
const path = require('path')
const { shell } = require('electron')
// Group #1: File Name (without .disabled, if any)
// Group #2: File Extension (jar, zip, or litemod)
// Group #3: If it is disabled (if string 'disabled' is present)
const MOD_REGEX = /^(.+(jar|zip|litemod))(?:\.(disabled))?$/
const DISABLED_EXT = '.disabled'
const SHADER_REGEX = /^(.+)\.zip$/
const SHADER_OPTION = /shaderPack=(.+)/
const SHADER_DIR = 'shaderpacks'
const SHADER_CONFIG = 'optionsshaders.txt'
/**
* Validate that the given directory exists. If not, it is
* created.
*
* @param {string} modsDir The path to the mods directory.
*/
exports.validateDir = function(dir) {
fs.ensureDirSync(dir)
}
/**
* Scan for drop-in mods in both the mods folder and version
* safe mods folder.
*
* @param {string} modsDir The path to the mods directory.
* @param {string} version The minecraft version of the server configuration.
*
* @returns {{fullName: string, name: string, ext: string, disabled: boolean}[]}
* An array of objects storing metadata about each discovered mod.
*/
exports.scanForDropinMods = function(modsDir, version) {
const modsDiscovered = []
if(fs.existsSync(modsDir)){
let modCandidates = fs.readdirSync(modsDir)
let verCandidates = []
const versionDir = path.join(modsDir, version)
if(fs.existsSync(versionDir)){
verCandidates = fs.readdirSync(versionDir)
}
for(let file of modCandidates){
const match = MOD_REGEX.exec(file)
if(match != null){
modsDiscovered.push({
fullName: match[0],
name: match[1],
ext: match[2],
disabled: match[3] != null
})
}
}
for(let file of verCandidates){
const match = MOD_REGEX.exec(file)
if(match != null){
modsDiscovered.push({
fullName: path.join(version, match[0]),
name: match[1],
ext: match[2],
disabled: match[3] != null
})
}
}
}
return modsDiscovered
}
/**
* Add dropin mods.
*
* @param {FileList} files The files to add.
* @param {string} modsDir The path to the mods directory.
*/
exports.addDropinMods = function(files, modsdir) {
exports.validateDir(modsdir)
for(let f of files) {
if(MOD_REGEX.exec(f.name) != null) {
fs.moveSync(f.path, path.join(modsdir, f.name))
}
}
}
/**
* Delete a drop-in mod from the file system.
*
* @param {string} modsDir The path to the mods directory.
* @param {string} fullName The fullName of the discovered mod to delete.
*
* @returns {boolean} True if the mod was deleted, otherwise false.
*/
exports.deleteDropinMod = function(modsDir, fullName){
const res = shell.moveItemToTrash(path.join(modsDir, fullName))
if(!res){
shell.beep()
}
return res
}
/**
* Toggle a discovered mod on or off. This is achieved by either
* adding or disabling the .disabled extension to the local file.
*
* @param {string} modsDir The path to the mods directory.
* @param {string} fullName The fullName of the discovered mod to toggle.
* @param {boolean} enable Whether to toggle on or off the mod.
*
* @returns {Promise.<void>} A promise which resolves when the mod has
* been toggled. If an IO error occurs the promise will be rejected.
*/
exports.toggleDropinMod = function(modsDir, fullName, enable){
return new Promise((resolve, reject) => {
const oldPath = path.join(modsDir, fullName)
const newPath = path.join(modsDir, enable ? fullName.substring(0, fullName.indexOf(DISABLED_EXT)) : fullName + DISABLED_EXT)
fs.rename(oldPath, newPath, (err) => {
if(err){
reject(err)
} else {
resolve()
}
})
})
}
/**
* Check if a drop-in mod is enabled.
*
* @param {string} fullName The fullName of the discovered mod to toggle.
* @returns {boolean} True if the mod is enabled, otherwise false.
*/
exports.isDropinModEnabled = function(fullName){
return !fullName.endsWith(DISABLED_EXT)
}
/**
* Scan for shaderpacks inside the shaderpacks folder.
*
* @param {string} instanceDir The path to the server instance directory.
*
* @returns {{fullName: string, name: string}[]}
* An array of objects storing metadata about each discovered shaderpack.
*/
exports.scanForShaderpacks = function(instanceDir){
const shaderDir = path.join(instanceDir, SHADER_DIR)
const packsDiscovered = [{
fullName: 'OFF',
name: 'Off (Default)'
}]
if(fs.existsSync(shaderDir)){
let modCandidates = fs.readdirSync(shaderDir)
for(let file of modCandidates){
const match = SHADER_REGEX.exec(file)
if(match != null){
packsDiscovered.push({
fullName: match[0],
name: match[1]
})
}
}
}
return packsDiscovered
}
/**
* Read the optionsshaders.txt file to locate the current
* enabled pack. If the file does not exist, OFF is returned.
*
* @param {string} instanceDir The path to the server instance directory.
*
* @returns {string} The file name of the enabled shaderpack.
*/
exports.getEnabledShaderpack = function(instanceDir){
exports.validateDir(instanceDir)
const optionsShaders = path.join(instanceDir, SHADER_CONFIG)
if(fs.existsSync(optionsShaders)){
const buf = fs.readFileSync(optionsShaders, {encoding: 'utf-8'})
const match = SHADER_OPTION.exec(buf)
if(match != null){
return match[1]
} else {
console.warn('WARNING: Shaderpack regex failed.')
}
}
return 'OFF'
}
/**
* Set the enabled shaderpack.
*
* @param {string} instanceDir The path to the server instance directory.
* @param {string} pack the file name of the shaderpack.
*/
exports.setEnabledShaderpack = function(instanceDir, pack){
exports.validateDir(instanceDir)
const optionsShaders = path.join(instanceDir, SHADER_CONFIG)
let buf
if(fs.existsSync(optionsShaders)){
buf = fs.readFileSync(optionsShaders, {encoding: 'utf-8'})
buf = buf.replace(SHADER_OPTION, `shaderPack=${pack}`)
} else {
buf = `shaderPack=${pack}`
}
fs.writeFileSync(optionsShaders, buf, {encoding: 'utf-8'})
}
/**
* Add shaderpacks.
*
* @param {FileList} files The files to add.
* @param {string} instanceDir The path to the server instance directory.
*/
exports.addShaderpacks = function(files, instanceDir) {
const p = path.join(instanceDir, SHADER_DIR)
exports.validateDir(p)
for(let f of files) {
if(SHADER_REGEX.exec(f.name) != null) {
fs.moveSync(f.path, path.join(p, f.name))
}
}
}

View File

@ -1,5 +1,5 @@
'use strict'
const getFromEnv = parseInt(process.env.ELECTRON_IS_DEV, 10) === 1
const isEnvSet = 'ELECTRON_IS_DEV' in process.env
'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))

View File

@ -1,21 +1,21 @@
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}`)
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}`)
}

View File

@ -1,32 +1,32 @@
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)
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)
}

View File

@ -1,271 +1,271 @@
/**
* 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)
}
}
})
})
/**
* 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)
}
}
})
})
}

View File

@ -1,69 +1,69 @@
const {ipcRenderer} = require('electron')
const fs = require('fs-extra')
const os = require('os')
const path = require('path')
const ConfigManager = require('./configmanager')
const DistroManager = require('./distromanager')
const LangLoader = require('./langloader')
const logger = require('./loggerutil')('%c[Preloader]', 'color: #a02d2a; font-weight: bold')
logger.log('Loading..')
// Load ConfigManager
ConfigManager.load()
// Load Strings
LangLoader.loadLanguage('en_US')
function onDistroLoad(data){
if(data != null){
// Resolve the selected server if its value has yet to be set.
if(ConfigManager.getSelectedServer() == null || data.getServer(ConfigManager.getSelectedServer()) == null){
logger.log('Determining default selected server..')
ConfigManager.setSelectedServer(data.getMainServer().getID())
ConfigManager.save()
}
}
ipcRenderer.send('distributionIndexDone', data != null)
}
// Ensure Distribution is downloaded and cached.
DistroManager.pullRemote().then((data) => {
logger.log('Loaded distribution index.')
onDistroLoad(data)
}).catch((err) => {
logger.log('Failed to load distribution index.')
logger.error(err)
logger.log('Attempting to load an older version of the distribution index.')
// Try getting a local copy, better than nothing.
DistroManager.pullLocal().then((data) => {
logger.log('Successfully loaded an older version of the distribution index.')
onDistroLoad(data)
}).catch((err) => {
logger.log('Failed to load an older version of the distribution index.')
logger.log('Application cannot run.')
logger.error(err)
onDistroLoad(null)
})
})
// Clean up temp dir incase previous launches ended unexpectedly.
fs.remove(path.join(os.tmpdir(), ConfigManager.getTempNativeFolder()), (err) => {
if(err){
logger.warn('Error while cleaning natives directory', err)
} else {
logger.log('Cleaned natives directory.')
}
const {ipcRenderer} = require('electron')
const fs = require('fs-extra')
const os = require('os')
const path = require('path')
const ConfigManager = require('./configmanager')
const DistroManager = require('./distromanager')
const LangLoader = require('./langloader')
const logger = require('./loggerutil')('%c[Preloader]', 'color: #a02d2a; font-weight: bold')
logger.log('Loading..')
// Load ConfigManager
ConfigManager.load()
// Load Strings
LangLoader.loadLanguage('en_US')
function onDistroLoad(data){
if(data != null){
// Resolve the selected server if its value has yet to be set.
if(ConfigManager.getSelectedServer() == null || data.getServer(ConfigManager.getSelectedServer()) == null){
logger.log('Determining default selected server..')
ConfigManager.setSelectedServer(data.getMainServer().getID())
ConfigManager.save()
}
}
ipcRenderer.send('distributionIndexDone', data != null)
}
// Ensure Distribution is downloaded and cached.
DistroManager.pullRemote().then((data) => {
logger.log('Loaded distribution index.')
onDistroLoad(data)
}).catch((err) => {
logger.log('Failed to load distribution index.')
logger.error(err)
logger.log('Attempting to load an older version of the distribution index.')
// Try getting a local copy, better than nothing.
DistroManager.pullLocal().then((data) => {
logger.log('Successfully loaded an older version of the distribution index.')
onDistroLoad(data)
}).catch((err) => {
logger.log('Failed to load an older version of the distribution index.')
logger.log('Application cannot run.')
logger.error(err)
onDistroLoad(null)
})
})
// Clean up temp dir incase previous launches ended unexpectedly.
fs.remove(path.join(os.tmpdir(), ConfigManager.getTempNativeFolder()), (err) => {
if(err){
logger.warn('Error while cleaning natives directory', err)
} else {
logger.log('Cleaned natives directory.')
}
})

View File

@ -317,7 +317,7 @@ class ProcessBuilder {
// Java Arguments
if(process.platform === 'darwin'){
args.push('-Xdock:name=HeliosLauncher')
args.push('-Xdock:name=NemesisMCLauncher')
args.push('-Xdock:icon=' + path.join(__dirname, '..', 'images', 'minecraft.icns'))
}
args.push('-Xmx' + ConfigManager.getMaxRAM())
@ -355,7 +355,7 @@ class ProcessBuilder {
// Java Arguments
if(process.platform === 'darwin'){
args.push('-Xdock:name=HeliosLauncher')
args.push('-Xdock:name=NemesisMCLauncher')
args.push('-Xdock:icon=' + path.join(__dirname, '..', 'images', 'minecraft.icns'))
}
args.push('-Xmx' + ConfigManager.getMaxRAM())
@ -457,7 +457,7 @@ class ProcessBuilder {
val = args[i].replace(argDiscovery, tempNativePath)
break
case 'launcher_name':
val = args[i].replace(argDiscovery, 'Helios-Launcher')
val = args[i].replace(argDiscovery, 'NemesisMC-Launcher')
break
case 'launcher_version':
val = args[i].replace(argDiscovery, this.launcherVersion)

View File

@ -1,65 +1,65 @@
const net = require('net')
/**
* Retrieves the status of a minecraft server.
*
* @param {string} address The server address.
* @param {number} port Optional. The port of the server. Defaults to 25565.
* @returns {Promise.<Object>} A promise which resolves to an object containing
* status information.
*/
exports.getStatus = function(address, port = 25565){
if(port == null || port == ''){
port = 25565
}
if(typeof port === 'string'){
port = parseInt(port)
}
return new Promise((resolve, reject) => {
const socket = net.connect(port, address, () => {
let buff = Buffer.from([0xFE, 0x01])
socket.write(buff)
})
socket.setTimeout(2500, () => {
socket.end()
reject({
code: 'ETIMEDOUT',
errno: 'ETIMEDOUT',
address,
port
})
})
socket.on('data', (data) => {
if(data != null && data != ''){
let server_info = data.toString().split('\x00\x00\x00')
const NUM_FIELDS = 6
if(server_info != null && server_info.length >= NUM_FIELDS){
resolve({
online: true,
version: server_info[2].replace(/\u0000/g, ''),
motd: server_info[3].replace(/\u0000/g, ''),
onlinePlayers: server_info[4].replace(/\u0000/g, ''),
maxPlayers: server_info[5].replace(/\u0000/g,'')
})
} else {
resolve({
online: false
})
}
}
socket.end()
})
socket.on('error', (err) => {
socket.destroy()
reject(err)
// ENOTFOUND = Unable to resolve.
// ECONNREFUSED = Unable to connect to port.
})
})
const net = require('net')
/**
* Retrieves the status of a minecraft server.
*
* @param {string} address The server address.
* @param {number} port Optional. The port of the server. Defaults to 25565.
* @returns {Promise.<Object>} A promise which resolves to an object containing
* status information.
*/
exports.getStatus = function(address, port = 25565){
if(port == null || port == ''){
port = 25565
}
if(typeof port === 'string'){
port = parseInt(port)
}
return new Promise((resolve, reject) => {
const socket = net.connect(port, address, () => {
let buff = Buffer.from([0xFE, 0x01])
socket.write(buff)
})
socket.setTimeout(2500, () => {
socket.end()
reject({
code: 'ETIMEDOUT',
errno: 'ETIMEDOUT',
address,
port
})
})
socket.on('data', (data) => {
if(data != null && data != ''){
let server_info = data.toString().split('\x00\x00\x00')
const NUM_FIELDS = 6
if(server_info != null && server_info.length >= NUM_FIELDS){
resolve({
online: true,
version: server_info[2].replace(/\u0000/g, ''),
motd: server_info[3].replace(/\u0000/g, ''),
onlinePlayers: server_info[4].replace(/\u0000/g, ''),
maxPlayers: server_info[5].replace(/\u0000/g,'')
})
} else {
resolve({
online: false
})
}
}
socket.end()
})
socket.on('error', (err) => {
socket.destroy()
reject(err)
// ENOTFOUND = Unable to resolve.
// ECONNREFUSED = Unable to connect to port.
})
})
}