mirror of
https://github.com/dscalzi/HeliosLauncher.git
synced 2024-12-22 03:32:12 -08:00
Compare commits
5 Commits
f119985211
...
d560ef09d1
Author | SHA1 | Date | |
---|---|---|---|
|
d560ef09d1 | ||
|
3a36ce7094 | ||
|
ad47617cd0 | ||
|
2fdb217e64 | ||
|
c1d36d2b03 |
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@ -14,16 +14,22 @@ jobs:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Node.js, NPM and Yarn
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Build/release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
python-version: 3.x
|
||||
|
||||
# If the commit is tagged with a version (e.g. "v1.0.0"),
|
||||
# release the app after building
|
||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.github_token }}
|
||||
run: npm run dist
|
||||
shell: bash
|
@ -16,13 +16,6 @@ const ConfigManager = require('./configmanager')
|
||||
const DistroManager = require('./distromanager')
|
||||
const isDev = require('./isdev')
|
||||
|
||||
// Constants
|
||||
// const PLATFORM_MAP = {
|
||||
// win32: '-windows-x64.tar.gz',
|
||||
// darwin: '-macosx-x64.tar.gz',
|
||||
// linux: '-linux-x64.tar.gz'
|
||||
// }
|
||||
|
||||
// Classes
|
||||
|
||||
/** Class representing a base asset. */
|
||||
@ -475,15 +468,15 @@ class JavaGuard extends EventEmitter {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if(verOb.major >= 16) {
|
||||
// TODO Make this logic better. Make java 16 required.
|
||||
// Java 9+
|
||||
if(Util.mcVersionAtLeast('1.13', this.mcVersion)){
|
||||
console.log('Java 9+ not yet tested.')
|
||||
/* meta.version = verOb
|
||||
if(Util.mcVersionAtLeast('1.17', this.mcVersion)){
|
||||
meta.version = verOb
|
||||
++checksum
|
||||
if(checksum === goal){
|
||||
break
|
||||
} */
|
||||
}
|
||||
}
|
||||
}
|
||||
// Space included so we get only the vendor.
|
||||
|
@ -10,10 +10,11 @@
|
||||
*/
|
||||
// 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')
|
||||
const { LoggerUtil } = require('helios-core')
|
||||
const { MojangRestAPI, mojangErrorDisplayable, MojangErrorCode } = require('helios-core/mojang')
|
||||
const { RestResponseStatus } = require('helios-core/common')
|
||||
|
||||
const log = LoggerUtil.getLogger('AuthManager')
|
||||
|
||||
// Functions
|
||||
|
||||
@ -28,20 +29,29 @@ const loggerSuccess = LoggerUtil('%c[AuthManager]', 'color: #209b07; font-weight
|
||||
*/
|
||||
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)
|
||||
const response = await MojangRestAPI.authenticate(username, password, ConfigManager.getClientToken())
|
||||
console.log(response)
|
||||
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||
|
||||
const session = response.data
|
||||
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 {
|
||||
return Promise.reject(mojangErrorDisplayable(MojangErrorCode.ERROR_NOT_PAID))
|
||||
}
|
||||
ConfigManager.save()
|
||||
return ret
|
||||
|
||||
} else {
|
||||
throw new Error('NotPaidAccount')
|
||||
return Promise.reject(mojangErrorDisplayable(response.mojangErrorCode))
|
||||
}
|
||||
|
||||
} catch (err){
|
||||
return Promise.reject(err)
|
||||
log.error(err)
|
||||
return Promise.reject(mojangErrorDisplayable(MojangErrorCode.UNKNOWN))
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,11 +65,17 @@ exports.addAccount = async function(username, password){
|
||||
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()
|
||||
const response = await MojangRestAPI.invalidate(authAcc.accessToken, ConfigManager.getClientToken())
|
||||
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||
ConfigManager.removeAuthAccount(uuid)
|
||||
ConfigManager.save()
|
||||
return Promise.resolve()
|
||||
} else {
|
||||
log.error('Error while removing account', response.error)
|
||||
return Promise.reject(response.error)
|
||||
}
|
||||
} catch (err){
|
||||
log.error('Error while removing account', err)
|
||||
return Promise.reject(err)
|
||||
}
|
||||
}
|
||||
@ -76,24 +92,27 @@ exports.removeAccount = async function(uuid){
|
||||
*/
|
||||
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?
|
||||
const response = await MojangRestAPI.validate(current.accessToken, ConfigManager.getClientToken())
|
||||
|
||||
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||
const isValid = response.data
|
||||
if(!isValid){
|
||||
const refreshResponse = await MojangRestAPI.refresh(current.accessToken, ConfigManager.getClientToken())
|
||||
if(refreshResponse.responseStatus === RestResponseStatus.SUCCESS) {
|
||||
const session = refreshResponse.data
|
||||
ConfigManager.updateAuthAccount(current.uuid, session.accessToken)
|
||||
ConfigManager.save()
|
||||
} else {
|
||||
log.error('Error while validating selected profile:', refreshResponse.error)
|
||||
log.info('Account access token is invalid.')
|
||||
return false
|
||||
}
|
||||
logger.log('Account access token is invalid.')
|
||||
return false
|
||||
log.info('Account access token validated.')
|
||||
return true
|
||||
} else {
|
||||
log.info('Account access token validated.')
|
||||
return true
|
||||
}
|
||||
loggerSuccess.log('Account access token validated.')
|
||||
return true
|
||||
} else {
|
||||
loggerSuccess.log('Account access token validated.')
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
// Work in progress
|
||||
const logger = require('./loggerutil')('%c[DiscordWrapper]', 'color: #7289da; font-weight: bold')
|
||||
|
||||
const {Client} = require('discord-rpc')
|
||||
const {Client} = require('discord-rpc-patch')
|
||||
|
||||
let client
|
||||
let activity
|
||||
|
@ -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: 'session.minecraft.net',
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
@ -96,6 +96,16 @@ class ProcessBuilder {
|
||||
return child
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the platform specific classpath separator. On windows, this is a semicolon.
|
||||
* On Unix, this is a colon.
|
||||
*
|
||||
* @returns {string} The classpath separator for the current operating system.
|
||||
*/
|
||||
static getClasspathSeparator() {
|
||||
return process.platform === 'win32' ? ';' : ':'
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an optional mod is enabled from its configuration value. If the
|
||||
* configuration value is null, the required object will be used to
|
||||
@ -339,7 +349,7 @@ class ProcessBuilder {
|
||||
|
||||
// Classpath Argument
|
||||
args.push('-cp')
|
||||
args.push(this.classpathArg(mods, tempNativePath).join(process.platform === 'win32' ? ';' : ':'))
|
||||
args.push(this.classpathArg(mods, tempNativePath).join(ProcessBuilder.getClasspathSeparator()))
|
||||
|
||||
// Java Arguments
|
||||
if(process.platform === 'darwin'){
|
||||
@ -377,6 +387,19 @@ class ProcessBuilder {
|
||||
// JVM Arguments First
|
||||
let args = this.versionData.arguments.jvm
|
||||
|
||||
// Debug securejarhandler
|
||||
// args.push('-Dbsl.debug=true')
|
||||
|
||||
if(this.forgeData.arguments.jvm != null) {
|
||||
for(const argStr of this.forgeData.arguments.jvm) {
|
||||
args.push(argStr
|
||||
.replaceAll('${library_directory}', this.libPath)
|
||||
.replaceAll('${classpath_separator}', ProcessBuilder.getClasspathSeparator())
|
||||
.replaceAll('${version_name}', this.forgeData.id)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//args.push('-Dlog4j.configurationFile=D:\\WesterosCraft\\game\\common\\assets\\log_configs\\client-1.12.xml')
|
||||
|
||||
// Java Arguments
|
||||
@ -489,7 +512,7 @@ class ProcessBuilder {
|
||||
val = args[i].replace(argDiscovery, this.launcherVersion)
|
||||
break
|
||||
case 'classpath':
|
||||
val = this.classpathArg(mods, tempNativePath).join(process.platform === 'win32' ? ';' : ':')
|
||||
val = this.classpathArg(mods, tempNativePath).join(ProcessBuilder.getClasspathSeparator())
|
||||
break
|
||||
}
|
||||
if(val != null){
|
||||
@ -647,9 +670,13 @@ class ProcessBuilder {
|
||||
classpathArg(mods, tempNativePath){
|
||||
let cpArgs = []
|
||||
|
||||
// Add the version.jar to the classpath.
|
||||
const version = this.versionData.id
|
||||
cpArgs.push(path.join(this.commonDir, 'versions', version, version + '.jar'))
|
||||
if(!Util.mcVersionAtLeast('1.17', this.server.getMinecraftVersion())) {
|
||||
// Add the version.jar to the classpath.
|
||||
// Must not be added to the classpath for Forge 1.17+.
|
||||
const version = this.versionData.id
|
||||
cpArgs.push(path.join(this.commonDir, 'versions', version, version + '.jar'))
|
||||
}
|
||||
|
||||
|
||||
if(this.usingLiteLoader){
|
||||
cpArgs.push(this.llPath)
|
||||
@ -788,6 +815,15 @@ class ProcessBuilder {
|
||||
let libs = []
|
||||
for(let sm of mdl.getSubModules()){
|
||||
if(sm.getType() === DistroManager.Types.Library){
|
||||
|
||||
// TODO Add as file or something.
|
||||
const x = sm.getIdentifier()
|
||||
console.log(x)
|
||||
if(x.includes(':universal') || x.includes(':slim') || x.includes(':extra') || x.includes(':srg') || x.includes(':client')) {
|
||||
console.log('SKIPPING ' + x)
|
||||
continue
|
||||
}
|
||||
|
||||
libs.push(sm.getArtifact().getPath())
|
||||
}
|
||||
// If this module has submodules, we need to resolve the libraries for those.
|
||||
|
@ -5,13 +5,12 @@
|
||||
const cp = require('child_process')
|
||||
const crypto = require('crypto')
|
||||
const { URL } = require('url')
|
||||
const { getServerStatus } = require('helios-core')
|
||||
const { MojangRestAPI, getServerStatus } = require('helios-core/mojang')
|
||||
|
||||
// Internal Requirements
|
||||
const DiscordWrapper = require('./assets/js/discordwrapper')
|
||||
const Mojang = require('./assets/js/mojang')
|
||||
const ProcessBuilder = require('./assets/js/processbuilder')
|
||||
const ServerStatus = require('./assets/js/serverstatus')
|
||||
const { RestResponseStatus } = require('helios-core/common')
|
||||
|
||||
// Launch Elements
|
||||
const launch_content = document.getElementById('launch_content')
|
||||
@ -166,55 +165,57 @@ const refreshMojangStatuses = async function(){
|
||||
let tooltipEssentialHTML = ''
|
||||
let tooltipNonEssentialHTML = ''
|
||||
|
||||
try {
|
||||
const statuses = await Mojang.status()
|
||||
greenCount = 0
|
||||
greyCount = 0
|
||||
|
||||
for(let i=0; i<statuses.length; i++){
|
||||
const service = statuses[i]
|
||||
|
||||
if(service.essential){
|
||||
tooltipEssentialHTML += `<div class="mojangStatusContainer">
|
||||
<span class="mojangStatusIcon" style="color: ${Mojang.statusToHex(service.status)};">•</span>
|
||||
<span class="mojangStatusName">${service.name}</span>
|
||||
</div>`
|
||||
} else {
|
||||
tooltipNonEssentialHTML += `<div class="mojangStatusContainer">
|
||||
<span class="mojangStatusIcon" style="color: ${Mojang.statusToHex(service.status)};">•</span>
|
||||
<span class="mojangStatusName">${service.name}</span>
|
||||
</div>`
|
||||
}
|
||||
|
||||
if(service.status === 'yellow' && status !== 'red'){
|
||||
status = 'yellow'
|
||||
} else if(service.status === 'red'){
|
||||
status = 'red'
|
||||
} else {
|
||||
if(service.status === 'grey'){
|
||||
++greyCount
|
||||
}
|
||||
++greenCount
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(greenCount === statuses.length){
|
||||
if(greyCount === statuses.length){
|
||||
status = 'grey'
|
||||
} else {
|
||||
status = 'green'
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
const response = await MojangRestAPI.status()
|
||||
let statuses
|
||||
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||
statuses = response.data
|
||||
} else {
|
||||
loggerLanding.warn('Unable to refresh Mojang service status.')
|
||||
loggerLanding.debug(err)
|
||||
statuses = MojangRestAPI.getDefaultStatuses()
|
||||
}
|
||||
|
||||
greenCount = 0
|
||||
greyCount = 0
|
||||
|
||||
for(let i=0; i<statuses.length; i++){
|
||||
const service = statuses[i]
|
||||
|
||||
if(service.essential){
|
||||
tooltipEssentialHTML += `<div class="mojangStatusContainer">
|
||||
<span class="mojangStatusIcon" style="color: ${MojangRestAPI.statusToHex(service.status)};">•</span>
|
||||
<span class="mojangStatusName">${service.name}</span>
|
||||
</div>`
|
||||
} else {
|
||||
tooltipNonEssentialHTML += `<div class="mojangStatusContainer">
|
||||
<span class="mojangStatusIcon" style="color: ${MojangRestAPI.statusToHex(service.status)};">•</span>
|
||||
<span class="mojangStatusName">${service.name}</span>
|
||||
</div>`
|
||||
}
|
||||
|
||||
if(service.status === 'yellow' && status !== 'red'){
|
||||
status = 'yellow'
|
||||
} else if(service.status === 'red'){
|
||||
status = 'red'
|
||||
} else {
|
||||
if(service.status === 'grey'){
|
||||
++greyCount
|
||||
}
|
||||
++greenCount
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(greenCount === statuses.length){
|
||||
if(greyCount === statuses.length){
|
||||
status = 'grey'
|
||||
} else {
|
||||
status = 'green'
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('mojangStatusEssentialContainer').innerHTML = tooltipEssentialHTML
|
||||
document.getElementById('mojangStatusNonEssentialContainer').innerHTML = tooltipNonEssentialHTML
|
||||
document.getElementById('mojang_status_icon').style.color = Mojang.statusToHex(status)
|
||||
document.getElementById('mojang_status_icon').style.color = MojangRestAPI.statusToHex(status)
|
||||
}
|
||||
|
||||
const refreshServerStatus = async function(fade = false){
|
||||
|
@ -154,79 +154,6 @@ function formDisabled(v){
|
||||
loginRememberOption.disabled = v
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an error and returns a user-friendly title and description
|
||||
* for our error overlay.
|
||||
*
|
||||
* @param {Error | {cause: string, error: string, errorMessage: string}} err A Node.js
|
||||
* error or Mojang error response.
|
||||
*/
|
||||
function resolveError(err){
|
||||
// Mojang Response => err.cause | err.error | err.errorMessage
|
||||
// Node error => err.code | err.message
|
||||
if(err.cause != null && err.cause === 'UserMigratedException') {
|
||||
return {
|
||||
title: Lang.queryJS('login.error.userMigrated.title'),
|
||||
desc: Lang.queryJS('login.error.userMigrated.desc')
|
||||
}
|
||||
} else {
|
||||
if(err.error != null){
|
||||
if(err.error === 'ForbiddenOperationException'){
|
||||
if(err.errorMessage != null){
|
||||
if(err.errorMessage === 'Invalid credentials. Invalid username or password.'){
|
||||
return {
|
||||
title: Lang.queryJS('login.error.invalidCredentials.title'),
|
||||
desc: Lang.queryJS('login.error.invalidCredentials.desc')
|
||||
}
|
||||
} else if(err.errorMessage === 'Invalid credentials.'){
|
||||
return {
|
||||
title: Lang.queryJS('login.error.rateLimit.title'),
|
||||
desc: Lang.queryJS('login.error.rateLimit.desc')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Request errors (from Node).
|
||||
if(err.code != null){
|
||||
if(err.code === 'ENOENT'){
|
||||
// No Internet.
|
||||
return {
|
||||
title: Lang.queryJS('login.error.noInternet.title'),
|
||||
desc: Lang.queryJS('login.error.noInternet.desc')
|
||||
}
|
||||
} else if(err.code === 'ENOTFOUND'){
|
||||
// Could not reach server.
|
||||
return {
|
||||
title: Lang.queryJS('login.error.authDown.title'),
|
||||
desc: Lang.queryJS('login.error.authDown.desc')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(err.message != null){
|
||||
if(err.message === 'NotPaidAccount'){
|
||||
return {
|
||||
title: Lang.queryJS('login.error.notPaid.title'),
|
||||
desc: Lang.queryJS('login.error.notPaid.desc')
|
||||
}
|
||||
} else {
|
||||
// Unknown error with request.
|
||||
return {
|
||||
title: Lang.queryJS('login.error.unknown.title'),
|
||||
desc: err.message
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unknown Mojang error.
|
||||
return {
|
||||
title: err.error,
|
||||
desc: err.errorMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let loginViewOnSuccess = VIEWS.landing
|
||||
let loginViewOnCancel = VIEWS.settings
|
||||
let loginViewCancelHandler
|
||||
@ -285,16 +212,15 @@ loginButton.addEventListener('click', () => {
|
||||
formDisabled(false)
|
||||
})
|
||||
}, 1000)
|
||||
}).catch((err) => {
|
||||
}).catch((displayableError) => {
|
||||
loginLoading(false)
|
||||
const errF = resolveError(err)
|
||||
setOverlayContent(errF.title, errF.desc, Lang.queryJS('login.tryAgain'))
|
||||
setOverlayContent(displayableError.title, displayableError.desc, Lang.queryJS('login.tryAgain'))
|
||||
setOverlayHandler(() => {
|
||||
formDisabled(false)
|
||||
toggleOverlay(false)
|
||||
})
|
||||
toggleOverlay(true)
|
||||
loggerLogin.log('Error while logging in.', err)
|
||||
loggerLogin.log('Error while logging in.', displayableError)
|
||||
})
|
||||
|
||||
})
|
2788
package-lock.json
generated
2788
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -23,16 +23,17 @@
|
||||
"node": "16.x.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.0.1",
|
||||
"@electron/remote": "^2.0.4",
|
||||
"adm-zip": "^0.5.9",
|
||||
"async": "^3.2.1",
|
||||
"discord-rpc": "^3.2.0",
|
||||
"async": "^3.2.3",
|
||||
"discord-rpc-patch": "^4.0.1",
|
||||
"ejs": "^3.1.6",
|
||||
"ejs-electron": "^2.1.1",
|
||||
"electron-updater": "^4.3.9",
|
||||
"electron-updater": "^4.6.5",
|
||||
"fs-extra": "^10.0.0",
|
||||
"github-syntax-dark": "^0.5.0",
|
||||
"helios-core": "^0.1.0-alpha.3",
|
||||
"got": "^11.8.3",
|
||||
"helios-core": "^0.1.0-alpha.5",
|
||||
"jquery": "^3.6.0",
|
||||
"node-stream-zip": "^1.15.0",
|
||||
"request": "^2.88.2",
|
||||
@ -41,9 +42,9 @@
|
||||
"winreg": "^1.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^15.2.0",
|
||||
"electron-builder": "^22.13.1",
|
||||
"eslint": "^8.0.1"
|
||||
"electron": "^16.0.8",
|
||||
"electron-builder": "^22.14.13",
|
||||
"eslint": "^8.8.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
Loading…
Reference in New Issue
Block a user