initial-offline login

This commit is contained in:
cyber-dream 2022-07-30 16:07:51 +03:00
parent e7dd171cea
commit a4204d1f07
13 changed files with 5258 additions and 4794 deletions

View File

@ -1,55 +1,56 @@
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<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>Helios Launcher</title> <title>Helios Launcher</title>
<script src="./assets/js/scripts/uicore.js"></script> <script src="./assets/js/scripts/uicore.js"></script>
<script src="./assets/js/scripts/uibinder.js"></script> <script src="./assets/js/scripts/uibinder.js"></script>
<link type="text/css" rel="stylesheet" href="./assets/css/launcher.css"> <link type="text/css" rel="stylesheet" href="./assets/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;*/
transition: background-image 1s ease; transition: background-image 1s ease;
background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2ODApLCBkZWZhdWx0IHF1YWxpdHkK/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAPwBwAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A8VooopDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9k='); background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2ODApLCBkZWZhdWx0IHF1YWxpdHkK/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAPwBwAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A8VooopDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9k=');
background-size: cover; background-size: cover;
-webkit-user-select: none; -webkit-user-select: none;
} }
#main { #main {
display: none; display: none;
height: calc(100% - 22px); height: calc(100% - 22px);
background: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0) 100%); background: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0) 100%);
width: 100%; width: 100%;
position: absolute; position: absolute;
z-index: 10; z-index: 10;
} }
#main[overlay] { #main[overlay] {
filter: blur(3px) contrast(0.9) brightness(1.0); filter: blur(3px) contrast(0.9) brightness(1.0);
} }
</style> </style>
</head> </head>
<body bkid="<%=bkid%>"> <body bkid="<%=bkid%>">
<%- include('frame') %> <%- include('frame') %>
<div id="main"> <div id="main">
<%- include('welcome') %> <%- include('welcome') %>
<%- include('login') %> <%- include('login') %>
<%- include('waiting') %> <%- include('loginOffline') %>
<%- include('loginOptions') %> <%- include('waiting') %>
<%- include('settings') %> <%- include('loginOptions') %>
<%- include('landing') %> <%- include('settings') %>
</div> <%- include('landing') %>
<%- include('overlay') %> </div>
<div id="loadingContainer"> <%- include('overlay') %>
<div id="loadingContent"> <div id="loadingContainer">
<div id="loadSpinnerContainer"> <div id="loadingContent">
<img id="loadCenterImage" src="assets/images/LoadingSeal.png"> <div id="loadSpinnerContainer">
<img id="loadSpinnerImage" class="rotating" src="assets/images/LoadingText.png"> <img id="loadCenterImage" src="assets/images/LoadingSeal.png">
</div> <img id="loadSpinnerImage" class="rotating" src="assets/images/LoadingText.png">
</div> </div>
</div> </div>
<script> </div>
// Load language <script>
for(let key of Object.keys(Lang.query('html'))){ // Load language
document.getElementById(key).innerHTML = Lang.query(`html.${key}`) for(let key of Object.keys(Lang.query('html'))){
} document.getElementById(key).innerHTML = Lang.query(`html.${key}`)
</script> }
</body> </script>
</body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,298 @@
{
"version": "1.0.0",
"discord": {
"clientId": "385581240906022916",
"smallImageText": "WesterosCraft",
"smallImageKey": "seal-circle"
},
"java": {
"oracle": "http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html"
},
"rss": "https://westeroscraft.com/articles/index.rss",
"servers": [
{
"id": "SkirdaTesting-1.12.2",
"name": "Skirda Test Server",
"description": "Сплошные баги блять",
"icon": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/server-prod.png",
"version": "4.6.5",
"address": "192.168.88.228:35012",
"minecraftVersion": "1.12.2",
"discord": {
"shortId": "Skirda Minecraft Server",
"largeImageText": "Skirda 1.12.2 Minecraft Server",
"largeImageKey": "skirda-testing"
},
"mainServer": false,
"autoconnect": true,
"modules": [
{
"id": "net.minecraftforge:forge:1.12.2-14.23.5.2847",
"name": "Minecraft Forge",
"type": "ForgeHosted",
"artifact": {
"size": 4884700,
"MD5": "90734a5a713e24902d24c45c15caa42c",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/forge-1.12.2-14.23.5.2847-universal.jar"
},
"subModules": [
{
"id": "net.minecraft:launchwrapper:1.12",
"name": "Mojang (LaunchWrapper)",
"type": "Library",
"artifact": {
"size": 32999,
"MD5": "934b2d91c7c5be4a49577c9e6b40e8da",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/launchwrapper-1.12.jar"
}
},
{
"id": "org.ow2.asm:asm-all:5.2",
"name": "Mojang (ASM)",
"type": "Library",
"artifact": {
"size": 247787,
"MD5": "f5ad16c7f0338b541978b0430d51dc83",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/asm-all-5.2.jar"
}
},
{
"id": "jline:jline:2.13",
"name": "Mojang (jline)",
"type": "Library",
"artifact": {
"size": 248566,
"MD5": "f251ba666cccb260ff7215b2cbeee8d4",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/jline-2.13.jar"
}
},
{
"id": "org.scala-lang:scala-library:2.11.1@jar.pack.xz",
"name": "Minecraft Forge (scala-library)",
"type": "Library",
"artifact": {
"size": 1474672,
"MD5": "379c15c4f724421c6d5d7aecedaf87a6",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-library-2.11.1.jar.pack.xz"
}
},
{
"id": "org.scala-lang:scala-compiler:2.11.1@jar.pack.xz",
"name": "Minecraft Forge (scala-compiler)",
"type": "Library",
"artifact": {
"size": 3076920,
"MD5": "7d89e952f2d5c74577310cd2c28e3f20",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-compiler-2.11.1.jar.pack.xz"
}
},
{
"id": "org.scala-lang:scala-actors-migration_2.11:1.1.0@jar.pack.xz",
"name": "Minecraft Forge (scala-actors-migration)",
"type": "Library",
"artifact": {
"size": 21324,
"MD5": "04e3428b2600ace33c7ae2bf1f6c0a4c",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-actors-migration_2.11-1.1.0.jar.pack.xz"
}
},
{
"id": "org.scala-lang.plugins:scala-continuations-library_2.11:1.0.2@jar.pack.xz",
"name": "Minecraft Forge (scala-continuations-library)",
"type": "Library",
"artifact": {
"size": 7956,
"MD5": "ed9b1d27aba8ac4090a3749c4dfc895a",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-continuations-library_2.11-1.0.2.jar.pack.xz"
}
},
{
"id": "org.scala-lang.plugins:scala-continuations-plugin_2.11.1:1.0.2@jar.pack.xz",
"name": "Minecraft Forge (scala-continuations-plugin)",
"type": "Library",
"artifact": {
"size": 46140,
"MD5": "a8232db22a72a981de6b1399eb86dff7",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-continuations-plugin_2.11.1-1.0.2.jar.pack.xz"
}
},
{
"id": "org.scala-lang:scala-parser-combinators_2.11:1.0.1@jar.pack.xz",
"name": "Minecraft Forge (scala-parser-combinators)",
"type": "Library",
"artifact": {
"size": 85568,
"MD5": "2e50a7df17680daadacca69f07f8a16d",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-parser-combinators_2.11-1.0.1.jar.pack.xz"
}
},
{
"id": "org.scala-lang:scala-reflect:2.11.1@jar.pack.xz",
"name": "Minecraft Forge (scala-reflect)",
"type": "Library",
"artifact": {
"size": 1070312,
"MD5": "84e5dc81c10e2bd74c579c9d0332fdd9",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-reflect-2.11.1.jar.pack.xz"
}
},
{
"id": "org.scala-lang:scala-swing_2.11:1.0.1",
"name": "Minecraft Forge (scala-swing)",
"type": "Library",
"artifact": {
"size": 736795,
"MD5": "1d360289e697022a3f57abaad344b28f",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-swing_2.11-1.0.1.jar"
}
},
{
"id": "org.scala-lang:scala-xml_2.11:1.0.2@jar.pack.xz",
"name": "Minecraft Forge (scala-xml)",
"type": "Library",
"artifact": {
"size": 217812,
"MD5": "cc891b094a4c32dedc56bfefe9b072ff",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/scala-xml_2.11-1.0.2.jar.pack.xz"
}
},
{
"id": "com.typesafe.akka:akka-actor_2.11:2.3.3@jar.pack.xz",
"name": "Minecraft Forge (akka-actor)",
"type": "Library",
"artifact": {
"size": 746612,
"MD5": "25cb22c3078e9fb3f7a861c912924862",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/akka-actor_2.11-2.3.3.jar.pack.xz"
}
},
{
"id": "com.typesafe:config:1.2.1@jar.pack.xz",
"name": "Minecraft Forge (typesafe-config)",
"type": "Library",
"artifact": {
"size": 56636,
"MD5": "10ec4ccabc4e68aac9cf87165ead5d7d",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/config-1.2.1.jar.pack.xz"
}
},
{
"id": "lzma:lzma:0.0.1",
"name": "Mojang (LZMA)",
"type": "Library",
"artifact": {
"size": 5762,
"MD5": "a3e3c3186e41c4a1a3027ba2bb23cdc6",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/lzma-0.0.1.jar"
}
},
{
"id": "net.sf.trove4j:trove4j:3.0.3",
"name": "Trove4J",
"type": "Library",
"artifact": {
"size": 2523218,
"MD5": "8fc4d4e0129244f9fd39650c5f30feb2",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/trove4j-3.0.3.jar"
}
},
{
"id": "java3d:vecmath:1.5.2",
"name": "Vecmath",
"type": "Library",
"artifact": {
"size": 318956,
"MD5": "e5d2b7f46c4800a32f62ce75676a5710",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/vecmath-1.5.2.jar"
}
},
{
"id": "net.sf.jopt-simple:jopt-simple:5.0.3",
"name": "Jopt-simple",
"type": "Library",
"artifact": {
"size": 78175,
"MD5": "0a5ec84e23df9d7cfb4063bc55f2744c",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/jopt-simple-5.0.3.jar"
}
},
{
"id": "org.apache.maven:maven-artifact:3.5.3",
"name": "maven-artifact",
"type": "Library",
"artifact": {
"size": 54961,
"MD5": "7741ebf29690ee7d9dde9cf4376347fc",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/maven-artifact-3.5.3.jar"
}
},
{
"id": "net.minecraftforge:MercuriusUpdater:1.12.2",
"name": "MercuriusUpdater",
"type": "Library",
"artifact": {
"size": 15098,
"MD5": "6eb9e61097bee3103a2fdc42746b76a4",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/1.12.2/MercuriusUpdater-1.12.2.jar"
}
}
]
},
{
"id": "net.optifine:optifine:1.12.2_HD_U_F5",
"name": "Optifine",
"type": "ForgeMod",
"artifact": {
"size": 2598821,
"MD5": "043ac1db6f7441ea4cf31bcb621aff0b",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.12.2/mods/OptiFine.jar"
}
},
{
"id": "mezz:jei:1.12.2-4.14.3.242",
"name": "JustEnoughItems",
"type": "ForgeMod",
"artifact": {
"size": 620682,
"MD5": "ae6d0e6e873ef6c20f41097dc7fee8c6",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.12.2/mods/jei.jar"
}
},
{
"id": "xaeros:minimap:1.12.2-20.15.0",
"name": "XaerosMinimap",
"type": "ForgeMod",
"required": {
"value": false
},
"artifact": {
"size": 528849,
"MD5": "cc12cfe20febd1404345f5339e522cda",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.12.2/mods/Xaeros_Minimap.jar"
}
},
{
"id": "options.txt",
"name": "Default Client Options",
"type": "File",
"artifact": {
"size": 1973,
"path": "options.txt",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/files/options-1.12.2.txt"
}
},
{
"id": "servers.dat",
"name": "Saved Client Servers",
"type": "File",
"artifact": {
"size": 84,
"MD5": "71d99e229d7d2b8d2a6423e46832a4b8",
"path": "servers.dat",
"url": "http://mc.westeroscraft.com/WesterosCraftLauncher/prod-1.12.2/servers.dat"
}
}
]
}
]
}

View File

@ -15,6 +15,7 @@ const { RestResponseStatus } = require('helios-core/common')
const { MojangRestAPI, mojangErrorDisplayable, MojangErrorCode } = require('helios-core/mojang') const { MojangRestAPI, mojangErrorDisplayable, MojangErrorCode } = require('helios-core/mojang')
const { MicrosoftAuth, microsoftErrorDisplayable, MicrosoftErrorCode } = require('helios-core/microsoft') const { MicrosoftAuth, microsoftErrorDisplayable, MicrosoftErrorCode } = require('helios-core/microsoft')
const { AZURE_CLIENT_ID } = require('./ipcconstants') const { AZURE_CLIENT_ID } = require('./ipcconstants')
const { async } = require('node-stream-zip')
const log = LoggerUtil.getLogger('AuthManager') const log = LoggerUtil.getLogger('AuthManager')
@ -57,6 +58,10 @@ exports.addMojangAccount = async function(username, password) {
} }
} }
exports.addOfflineAccount = async function(usernameOffline){
ConfigManager.addOfflineAccount('0456456413213', '-', "Megatraher")
}
const AUTH_MODE = { FULL: 0, MS_REFRESH: 1, MC_REFRESH: 2 } const AUTH_MODE = { FULL: 0, MS_REFRESH: 1, MC_REFRESH: 2 }
/** /**

View File

@ -353,6 +353,17 @@ exports.addMojangAuthAccount = function(uuid, accessToken, username, displayName
return config.authenticationDatabase[uuid] return config.authenticationDatabase[uuid]
} }
exports.addOfflineAccount = function(uuid, accessToken, usernameOffline){
config.selectedAccount = uuid
config.authenticationDatabase[uuid] = {
type: 'offline',
accessToken,
username: usernameOffline.trim(),
uuid: uuid.trim(),
displayName: usernameOffline.trim()
}
return config.authenticationDatabase[uuid]
}
/** /**
* Update the tokens of an authenticated microsoft account. * Update the tokens of an authenticated microsoft account.
* *

View File

@ -537,7 +537,8 @@ exports.pullRemote = function(){
return exports.pullLocal() return exports.pullLocal()
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const distroURL = 'http://mc.westeroscraft.com/WesterosCraftLauncher/distribution.json' //const distroURL = 'http://mc.westeroscraft.com/WesterosCraftLauncher/distribution.json'
const distroURL = "http://192.168.88.228:45789/distr.json"
//const distroURL = 'https://gist.githubusercontent.com/dscalzi/53b1ba7a11d26a5c353f9d5ae484b71b/raw/' //const distroURL = 'https://gist.githubusercontent.com/dscalzi/53b1ba7a11d26a5c353f9d5ae484b71b/raw/'
const opts = { const opts = {
url: distroURL, url: distroURL,

View File

@ -108,6 +108,7 @@ document.getElementById('launch_button').addEventListener('click', function(e){
} }
}) })
// Bind settings button // Bind settings button
document.getElementById('settingsMediaButton').onclick = (e) => { document.getElementById('settingsMediaButton').onclick = (e) => {
prepareSettings() prepareSettings()
@ -484,12 +485,12 @@ function dlAsync(login = true){
// Login parameter is temporary for debug purposes. Allows testing the validation/downloads without // Login parameter is temporary for debug purposes. Allows testing the validation/downloads without
// launching the game. // launching the game.
if(login) { /* if(login) {
if(ConfigManager.getSelectedAccount() == null){ if(ConfigManager.getSelectedAccount() == null){
loggerLanding.error('You must be logged into an account.') loggerLanding.error('You must be logged into an account.')
return return
} }
} }*/
setLaunchDetails('Please wait..') setLaunchDetails('Please wait..')
toggleLaunchArea(true) toggleLaunchArea(true)

View File

@ -1,239 +1,239 @@
/** /**
* Script for login.ejs * Script for login.ejs
*/ */
// Validation Regexes. // Validation Regexes.
const validUsername = /^[a-zA-Z0-9_]{1,16}$/ const validUsername = /^[a-zA-Z0-9_]{1,16}$/
const basicEmail = /^\S+@\S+\.\S+$/ const basicEmail = /^\S+@\S+\.\S+$/
//const validEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i //const validEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
// Login Elements // Login Elements
const loginCancelContainer = document.getElementById('loginCancelContainer') const loginCancelContainer = document.getElementById('loginCancelContainer')
const loginCancelButton = document.getElementById('loginCancelButton') const loginCancelButton = document.getElementById('loginCancelButton')
const loginEmailError = document.getElementById('loginEmailError') const loginEmailError = document.getElementById('loginEmailError')
const loginUsername = document.getElementById('loginUsername') const loginUsername = document.getElementById('loginUsername')
const loginPasswordError = document.getElementById('loginPasswordError') const loginPasswordError = document.getElementById('loginPasswordError')
const loginPassword = document.getElementById('loginPassword') const loginPassword = document.getElementById('loginPassword')
const checkmarkContainer = document.getElementById('checkmarkContainer') const checkmarkContainer = document.getElementById('checkmarkContainer')
const loginRememberOption = document.getElementById('loginRememberOption') const loginRememberOption = document.getElementById('loginRememberOption')
const loginButton = document.getElementById('loginButton') const loginButton = document.getElementById('loginButton')
const loginForm = document.getElementById('loginForm') const loginForm = document.getElementById('loginForm')
// Control variables. // Control variables.
let lu = false, lp = false let lu = false, lp = false
const loggerLogin = LoggerUtil1('%c[Login]', 'color: #000668; font-weight: bold') const loggerLogin = LoggerUtil1('%c[Login]', 'color: #000668; font-weight: bold')
/** /**
* Show a login error. * Show a login error.
* *
* @param {HTMLElement} element The element on which to display the error. * @param {HTMLElement} element The element on which to display the error.
* @param {string} value The error text. * @param {string} value The error text.
*/ */
function showError(element, value){ function showError(element, value){
element.innerHTML = value element.innerHTML = value
element.style.opacity = 1 element.style.opacity = 1
} }
/** /**
* Shake a login error to add emphasis. * Shake a login error to add emphasis.
* *
* @param {HTMLElement} element The element to shake. * @param {HTMLElement} element The element to shake.
*/ */
function shakeError(element){ function shakeError(element){
if(element.style.opacity == 1){ if(element.style.opacity == 1){
element.classList.remove('shake') element.classList.remove('shake')
void element.offsetWidth void element.offsetWidth
element.classList.add('shake') element.classList.add('shake')
} }
} }
/** /**
* Validate that an email field is neither empty nor invalid. * Validate that an email field is neither empty nor invalid.
* *
* @param {string} value The email value. * @param {string} value The email value.
*/ */
function validateEmail(value){ function validateEmail(value){
if(value){ if(value){
if(!basicEmail.test(value) && !validUsername.test(value)){ if(!basicEmail.test(value) && !validUsername.test(value)){
showError(loginEmailError, Lang.queryJS('login.error.invalidValue')) showError(loginEmailError, Lang.queryJS('login.error.invalidValue'))
loginDisabled(true) loginDisabled(true)
lu = false lu = false
} else { } else {
loginEmailError.style.opacity = 0 loginEmailError.style.opacity = 0
lu = true lu = true
if(lp){ if(lp){
loginDisabled(false) loginDisabled(false)
} }
} }
} else { } else {
lu = false lu = false
showError(loginEmailError, Lang.queryJS('login.error.requiredValue')) showError(loginEmailError, Lang.queryJS('login.error.requiredValue'))
loginDisabled(true) loginDisabled(true)
} }
} }
/** /**
* Validate that the password field is not empty. * Validate that the password field is not empty.
* *
* @param {string} value The password value. * @param {string} value The password value.
*/ */
function validatePassword(value){ function validatePassword(value){
if(value){ if(value){
loginPasswordError.style.opacity = 0 loginPasswordError.style.opacity = 0
lp = true lp = true
if(lu){ if(lu){
loginDisabled(false) loginDisabled(false)
} }
} else { } else {
lp = false lp = false
showError(loginPasswordError, Lang.queryJS('login.error.invalidValue')) showError(loginPasswordError, Lang.queryJS('login.error.invalidValue'))
loginDisabled(true) loginDisabled(true)
} }
} }
// Emphasize errors with shake when focus is lost. // Emphasize errors with shake when focus is lost.
loginUsername.addEventListener('focusout', (e) => { loginUsername.addEventListener('focusout', (e) => {
validateEmail(e.target.value) validateEmail(e.target.value)
shakeError(loginEmailError) shakeError(loginEmailError)
}) })
loginPassword.addEventListener('focusout', (e) => { loginPassword.addEventListener('focusout', (e) => {
validatePassword(e.target.value) validatePassword(e.target.value)
shakeError(loginPasswordError) shakeError(loginPasswordError)
}) })
// Validate input for each field. // Validate input for each field.
loginUsername.addEventListener('input', (e) => { loginUsername.addEventListener('input', (e) => {
validateEmail(e.target.value) validateEmail(e.target.value)
}) })
loginPassword.addEventListener('input', (e) => { loginPassword.addEventListener('input', (e) => {
validatePassword(e.target.value) validatePassword(e.target.value)
}) })
/** /**
* Enable or disable the login button. * Enable or disable the login button.
* *
* @param {boolean} v True to enable, false to disable. * @param {boolean} v True to enable, false to disable.
*/ */
function loginDisabled(v){ function loginDisabled(v){
if(loginButton.disabled !== v){ if(loginButton.disabled !== v){
loginButton.disabled = v loginButton.disabled = v
} }
} }
/** /**
* Enable or disable loading elements. * Enable or disable loading elements.
* *
* @param {boolean} v True to enable, false to disable. * @param {boolean} v True to enable, false to disable.
*/ */
function loginLoading(v){ function loginLoading(v){
if(v){ if(v){
loginButton.setAttribute('loading', v) loginButton.setAttribute('loading', v)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.login'), Lang.queryJS('login.loggingIn')) loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.login'), Lang.queryJS('login.loggingIn'))
} else { } else {
loginButton.removeAttribute('loading') loginButton.removeAttribute('loading')
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.login')) loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.login'))
} }
} }
/** /**
* Enable or disable login form. * Enable or disable login form.
* *
* @param {boolean} v True to enable, false to disable. * @param {boolean} v True to enable, false to disable.
*/ */
function formDisabled(v){ function formDisabled(v){
loginDisabled(v) loginDisabled(v)
loginCancelButton.disabled = v loginCancelButton.disabled = v
loginUsername.disabled = v loginUsername.disabled = v
loginPassword.disabled = v loginPassword.disabled = v
if(v){ if(v){
checkmarkContainer.setAttribute('disabled', v) checkmarkContainer.setAttribute('disabled', v)
} else { } else {
checkmarkContainer.removeAttribute('disabled') checkmarkContainer.removeAttribute('disabled')
} }
loginRememberOption.disabled = v loginRememberOption.disabled = v
} }
let loginViewOnSuccess = VIEWS.landing let loginViewOnSuccess = VIEWS.landing
let loginViewOnCancel = VIEWS.settings let loginViewOnCancel = VIEWS.settings
let loginViewCancelHandler let loginViewCancelHandler
function loginCancelEnabled(val){ function loginCancelEnabled(val){
if(val){ if(val){
$(loginCancelContainer).show() $(loginCancelContainer).show()
} else { } else {
$(loginCancelContainer).hide() $(loginCancelContainer).hide()
} }
} }
loginCancelButton.onclick = (e) => { loginCancelButton.onclick = (e) => {
switchView(getCurrentView(), loginViewOnCancel, 500, 500, () => { switchView(getCurrentView(), loginViewOnCancel, 500, 500, () => {
loginUsername.value = '' loginUsername.value = ''
loginPassword.value = '' loginPassword.value = ''
loginCancelEnabled(false) loginCancelEnabled(false)
if(loginViewCancelHandler != null){ if(loginViewCancelHandler != null){
loginViewCancelHandler() loginViewCancelHandler()
loginViewCancelHandler = null loginViewCancelHandler = null
} }
}) })
} }
// Disable default form behavior. // Disable default form behavior.
loginForm.onsubmit = () => { return false } loginForm.onsubmit = () => { return false }
// Bind login button behavior. // Bind login button behavior.
loginButton.addEventListener('click', () => { loginButton.addEventListener('click', () => {
// Disable form. // Disable form.
formDisabled(true) formDisabled(true)
// Show loading stuff. // Show loading stuff.
loginLoading(true) loginLoading(true)
AuthManager.addMojangAccount(loginUsername.value, loginPassword.value).then((value) => { AuthManager.addMojangAccount(loginUsername.value, loginPassword.value).then((value) => {
updateSelectedAccount(value) updateSelectedAccount(value)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.success')) loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.success'))
$('.circle-loader').toggleClass('load-complete') $('.circle-loader').toggleClass('load-complete')
$('.checkmark').toggle() $('.checkmark').toggle()
setTimeout(() => { setTimeout(() => {
switchView(VIEWS.login, loginViewOnSuccess, 500, 500, () => { switchView(VIEWS.login, loginViewOnSuccess, 500, 500, () => {
// Temporary workaround // Temporary workaround
if(loginViewOnSuccess === VIEWS.settings){ if(loginViewOnSuccess === VIEWS.settings){
prepareSettings() prepareSettings()
} }
loginViewOnSuccess = VIEWS.landing // Reset this for good measure. loginViewOnSuccess = VIEWS.landing // Reset this for good measure.
loginCancelEnabled(false) // Reset this for good measure. loginCancelEnabled(false) // Reset this for good measure.
loginViewCancelHandler = null // Reset this for good measure. loginViewCancelHandler = null // Reset this for good measure.
loginUsername.value = '' loginUsername.value = ''
loginPassword.value = '' loginPassword.value = ''
$('.circle-loader').toggleClass('load-complete') $('.circle-loader').toggleClass('load-complete')
$('.checkmark').toggle() $('.checkmark').toggle()
loginLoading(false) loginLoading(false)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.success'), Lang.queryJS('login.login')) loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.success'), Lang.queryJS('login.login'))
formDisabled(false) formDisabled(false)
}) })
}, 1000) }, 1000)
}).catch((displayableError) => { }).catch((displayableError) => {
loginLoading(false) loginLoading(false)
let actualDisplayableError let actualDisplayableError
if(isDisplayableError(displayableError)) { if(isDisplayableError(displayableError)) {
msftLoginLogger.error('Error while logging in.', displayableError) msftLoginLogger.error('Error while logging in.', displayableError)
actualDisplayableError = displayableError actualDisplayableError = displayableError
} else { } else {
// Uh oh. // Uh oh.
msftLoginLogger.error('Unhandled error during login.', displayableError) msftLoginLogger.error('Unhandled error during login.', displayableError)
actualDisplayableError = { actualDisplayableError = {
title: 'Unknown Error During Login', title: 'Unknown Error During Login',
desc: 'An unknown error has occurred. Please see the console for details.' desc: 'An unknown error has occurred. Please see the console for details.'
} }
} }
setOverlayContent(actualDisplayableError.title, actualDisplayableError.desc, Lang.queryJS('login.tryAgain')) setOverlayContent(actualDisplayableError.title, actualDisplayableError.desc, Lang.queryJS('login.tryAgain'))
setOverlayHandler(() => { setOverlayHandler(() => {
formDisabled(false) formDisabled(false)
toggleOverlay(false) toggleOverlay(false)
}) })
toggleOverlay(true) toggleOverlay(true)
}) })
}) })

View File

@ -0,0 +1,30 @@
const loggerOfflineLogin = LoggerUtil1('%c[LoginOffline]', 'color: #000668; font-weight: bold')
const loginOfflineButton = document.getElementById('loginOfflineButton')
function loginOfflineDisabled(v){
if(loginOfflineButton.disabled !== v){
loginOfflineButton.disabled = v
}
}
function formOfflineDisabled(v){
loginOfflineDisabled(v)
//loginCancelButton.disabled = v
loginOfflineUsername.disabled = v
/*loginPassword.disabled = v
if(v){
checkmarkContainer.setAttribute('disabled', v)
} else {
checkmarkContainer.removeAttribute('disabled')
}
loginRememberOption.disabled = v*/
}
loginOfflineButton.addEventListener('click', () => {
formOfflineDisabled(true)
AuthManager.addOfflineAccount(loginOfflineUsername.value).then((value) =>{
switchView(VIEWS.loginOffline, VIEWS.landing, 500, 500)
})
})

View File

@ -1,50 +1,45 @@
const loginOptionsCancelContainer = document.getElementById('loginOptionCancelContainer') const loginOptionsCancelContainer = document.getElementById('loginOptionCancelContainer')
const loginOptionMicrosoft = document.getElementById('loginOptionMicrosoft') const loginOptionMicrosoft = document.getElementById('loginOptionMicrosoft')
const loginOptionMojang = document.getElementById('loginOptionMojang') const loginOptionMojang = document.getElementById('loginOptionMojang')
const loginOptionsCancelButton = document.getElementById('loginOptionCancelButton') const loginOptionsCancelButton = document.getElementById('loginOptionCancelButton')
let loginOptionsCancellable = false let loginOptionsCancellable = true
let loginOptionsViewOnLoginSuccess let loginOptionsViewOnLoginSuccess
let loginOptionsViewOnLoginCancel let loginOptionsViewOnLoginCancel
let loginOptionsViewOnCancel let loginOptionsViewOnCancel
let loginOptionsViewCancelHandler let loginOptionsViewCancelHandler
function loginOptionsCancelEnabled(val){ function loginOptionsCancelEnabled(val){
if(val){ if(val){
$(loginOptionsCancelContainer).show() $(loginOptionsCancelContainer).show()
} else { } else {
$(loginOptionsCancelContainer).hide() $(loginOptionsCancelContainer).hide()
} }
} }
loginOptionMicrosoft.onclick = (e) => { loginOptionMicrosoft.onclick = (e) => {
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => { switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
ipcRenderer.send( ipcRenderer.send(
MSFT_OPCODE.OPEN_LOGIN, MSFT_OPCODE.OPEN_LOGIN,
loginOptionsViewOnLoginSuccess, loginOptionsViewOnLoginSuccess,
loginOptionsViewOnLoginCancel loginOptionsViewOnLoginCancel
) )
}) })
} }
loginOptionMojang.onclick = (e) => { loginOptionMojang.onclick = (e) => {
switchView(getCurrentView(), VIEWS.login, 500, 500, () => { switchView(getCurrentView(), VIEWS.login, 500, 500, () => {
loginViewOnSuccess = loginOptionsViewOnLoginSuccess loginViewOnSuccess = loginOptionsViewOnLoginSuccess
loginViewOnCancel = loginOptionsViewOnLoginCancel loginViewOnCancel = loginOptionsViewOnLoginCancel
loginCancelEnabled(true) loginCancelEnabled(true)
}) })
} }
loginOptionsCancelButton.onclick = (e) => { loginOptionOffline.onclick = (e) => {
switchView(getCurrentView(), loginOptionsViewOnCancel, 500, 500, () => { switchView(getCurrentView(), VIEWS.loginOffline, 500, 500, () => {
// Clear login values (Mojang login) loginViewOnSuccess = loginOptionsViewOnLoginSuccess
// No cleanup needed for Microsoft. loginViewOnCancel = loginOptionsViewOnLoginCancel
loginUsername.value = '' loginCancelEnabled(true)
loginPassword.value = '' })
if(loginOptionsViewCancelHandler != null){ }
loginOptionsViewCancelHandler()
loginOptionsViewCancelHandler = null
}
})
}

View File

@ -1,450 +1,451 @@
/** /**
* Initialize UI functions which depend on internal modules. * Initialize UI functions which depend on internal modules.
* Loaded after core UI functions are initialized in uicore.js. * Loaded after core UI functions are initialized in uicore.js.
*/ */
// Requirements // Requirements
const path = require('path') const path = require('path')
const AuthManager = require('./assets/js/authmanager') const AuthManager = require('./assets/js/authmanager')
const ConfigManager = require('./assets/js/configmanager') const ConfigManager = require('./assets/js/configmanager')
const DistroManager = require('./assets/js/distromanager') const DistroManager = require('./assets/js/distromanager')
const Lang = require('./assets/js/langloader') const Lang = require('./assets/js/langloader')
let rscShouldLoad = false let rscShouldLoad = false
let fatalStartupError = false let fatalStartupError = false
// Mapping of each view to their container IDs. // Mapping of each view to their container IDs.
const VIEWS = { const VIEWS = {
landing: '#landingContainer', landing: '#landingContainer',
loginOptions: '#loginOptionsContainer', loginOptions: '#loginOptionsContainer',
login: '#loginContainer', login: '#loginContainer',
settings: '#settingsContainer', loginOffline: '#loginOfflineContainer',
welcome: '#welcomeContainer', settings: '#settingsContainer',
waiting: '#waitingContainer' welcome: '#welcomeContainer',
} waiting: '#waitingContainer'
}
// The currently shown view container.
let currentView // The currently shown view container.
let currentView
/**
* Switch launcher views. /**
* * Switch launcher views.
* @param {string} current The ID of the current view container. *
* @param {*} next The ID of the next view container. * @param {string} current The ID of the current view container.
* @param {*} currentFadeTime Optional. The fade out time for the current view. * @param {*} next The ID of the next view container.
* @param {*} nextFadeTime Optional. The fade in time for the next view. * @param {*} currentFadeTime Optional. The fade out time for the current view.
* @param {*} onCurrentFade Optional. Callback function to execute when the current * @param {*} nextFadeTime Optional. The fade in time for the next view.
* view fades out. * @param {*} onCurrentFade Optional. Callback function to execute when the current
* @param {*} onNextFade Optional. Callback function to execute when the next view * view fades out.
* fades in. * @param {*} onNextFade Optional. Callback function to execute when the next view
*/ * fades in.
function switchView(current, next, currentFadeTime = 500, nextFadeTime = 500, onCurrentFade = () => {}, onNextFade = () => {}){ */
currentView = next function switchView(current, next, currentFadeTime = 500, nextFadeTime = 500, onCurrentFade = () => {}, onNextFade = () => {}){
$(`${current}`).fadeOut(currentFadeTime, () => { currentView = next
onCurrentFade() $(`${current}`).fadeOut(currentFadeTime, () => {
$(`${next}`).fadeIn(nextFadeTime, () => { onCurrentFade()
onNextFade() $(`${next}`).fadeIn(nextFadeTime, () => {
}) onNextFade()
}) })
} })
}
/**
* Get the currently shown view container. /**
* * Get the currently shown view container.
* @returns {string} The currently shown view container. *
*/ * @returns {string} The currently shown view container.
function getCurrentView(){ */
return currentView function getCurrentView(){
} return currentView
}
function showMainUI(data){
function showMainUI(data){
if(!isDev){
loggerAutoUpdater.log('Initializing..') if(!isDev){
ipcRenderer.send('autoUpdateAction', 'initAutoUpdater', ConfigManager.getAllowPrerelease()) loggerAutoUpdater.log('Initializing..')
} ipcRenderer.send('autoUpdateAction', 'initAutoUpdater', ConfigManager.getAllowPrerelease())
}
prepareSettings(true)
updateSelectedServer(data.getServer(ConfigManager.getSelectedServer())) prepareSettings(true)
refreshServerStatus() updateSelectedServer(data.getServer(ConfigManager.getSelectedServer()))
setTimeout(() => { refreshServerStatus()
document.getElementById('frameBar').style.backgroundColor = 'rgba(0, 0, 0, 0.5)' setTimeout(() => {
document.body.style.backgroundImage = `url('assets/images/backgrounds/${document.body.getAttribute('bkid')}.jpg')` document.getElementById('frameBar').style.backgroundColor = 'rgba(0, 0, 0, 0.5)'
$('#main').show() document.body.style.backgroundImage = `url('assets/images/backgrounds/${document.body.getAttribute('bkid')}.jpg')`
$('#main').show()
const isLoggedIn = Object.keys(ConfigManager.getAuthAccounts()).length > 0
const isLoggedIn = Object.keys(ConfigManager.getAuthAccounts()).length > 0
// If this is enabled in a development environment we'll get ratelimited.
// The relaunch frequency is usually far too high. // If this is enabled in a development environment we'll get ratelimited.
if(!isDev && isLoggedIn){ // The relaunch frequency is usually far too high.
validateSelectedAccount() if(!isDev && isLoggedIn){
} validateSelectedAccount()
}
if(ConfigManager.isFirstLaunch()){
currentView = VIEWS.welcome if(ConfigManager.isFirstLaunch()){
$(VIEWS.welcome).fadeIn(1000) currentView = VIEWS.welcome
} else { $(VIEWS.welcome).fadeIn(1000)
if(isLoggedIn){ } else {
currentView = VIEWS.landing if(isLoggedIn){
$(VIEWS.landing).fadeIn(1000) currentView = VIEWS.landing
} else { $(VIEWS.landing).fadeIn(1000)
loginOptionsCancelEnabled(false) } else {
loginOptionsViewOnLoginSuccess = VIEWS.landing loginOptionsCancelEnabled(false)
loginOptionsViewOnLoginCancel = VIEWS.loginOptions loginOptionsViewOnLoginSuccess = VIEWS.landing
currentView = VIEWS.loginOptions loginOptionsViewOnLoginCancel = VIEWS.loginOptions
$(VIEWS.loginOptions).fadeIn(1000) currentView = VIEWS.loginOptions
} $(VIEWS.loginOptions).fadeIn(1000)
} }
}
setTimeout(() => {
$('#loadingContainer').fadeOut(500, () => { setTimeout(() => {
$('#loadSpinnerImage').removeClass('rotating') $('#loadingContainer').fadeOut(500, () => {
}) $('#loadSpinnerImage').removeClass('rotating')
}, 250) })
}, 250)
}, 750)
// Disable tabbing to the news container. }, 750)
initNews().then(() => { // Disable tabbing to the news container.
$('#newsContainer *').attr('tabindex', '-1') initNews().then(() => {
}) $('#newsContainer *').attr('tabindex', '-1')
} })
}
function showFatalStartupError(){
setTimeout(() => { function showFatalStartupError(){
$('#loadingContainer').fadeOut(250, () => { setTimeout(() => {
document.getElementById('overlayContainer').style.background = 'none' $('#loadingContainer').fadeOut(250, () => {
setOverlayContent( document.getElementById('overlayContainer').style.background = 'none'
'Fatal Error: Unable to Load Distribution Index', setOverlayContent(
'A connection could not be established to our servers to download the distribution index. No local copies were available to load. <br><br>The distribution index is an essential file which provides the latest server information. The launcher is unable to start without it. Ensure you are connected to the internet and relaunch the application.', 'Fatal Error: Unable to Load Distribution Index',
'Close' 'A connection could not be established to our servers to download the distribution index. No local copies were available to load. <br><br>The distribution index is an essential file which provides the latest server information. The launcher is unable to start without it. Ensure you are connected to the internet and relaunch the application.',
) 'Close'
setOverlayHandler(() => { )
const window = remote.getCurrentWindow() setOverlayHandler(() => {
window.close() const window = remote.getCurrentWindow()
}) window.close()
toggleOverlay(true) })
}) toggleOverlay(true)
}, 750) })
} }, 750)
}
/**
* Common functions to perform after refreshing the distro index. /**
* * Common functions to perform after refreshing the distro index.
* @param {Object} data The distro index object. *
*/ * @param {Object} data The distro index object.
function onDistroRefresh(data){ */
updateSelectedServer(data.getServer(ConfigManager.getSelectedServer())) function onDistroRefresh(data){
refreshServerStatus() updateSelectedServer(data.getServer(ConfigManager.getSelectedServer()))
initNews() refreshServerStatus()
syncModConfigurations(data) initNews()
} syncModConfigurations(data)
}
/**
* Sync the mod configurations with the distro index. /**
* * Sync the mod configurations with the distro index.
* @param {Object} data The distro index object. *
*/ * @param {Object} data The distro index object.
function syncModConfigurations(data){ */
function syncModConfigurations(data){
const syncedCfgs = []
const syncedCfgs = []
for(let serv of data.getServers()){
for(let serv of data.getServers()){
const id = serv.getID()
const mdls = serv.getModules() const id = serv.getID()
const cfg = ConfigManager.getModConfiguration(id) const mdls = serv.getModules()
const cfg = ConfigManager.getModConfiguration(id)
if(cfg != null){
if(cfg != null){
const modsOld = cfg.mods
const mods = {} const modsOld = cfg.mods
const mods = {}
for(let mdl of mdls){
const type = mdl.getType() for(let mdl of mdls){
const type = mdl.getType()
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
if(!mdl.getRequired().isRequired()){ if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
const mdlID = mdl.getVersionlessID() if(!mdl.getRequired().isRequired()){
if(modsOld[mdlID] == null){ const mdlID = mdl.getVersionlessID()
mods[mdlID] = scanOptionalSubModules(mdl.getSubModules(), mdl) if(modsOld[mdlID] == null){
} else { mods[mdlID] = scanOptionalSubModules(mdl.getSubModules(), mdl)
mods[mdlID] = mergeModConfiguration(modsOld[mdlID], scanOptionalSubModules(mdl.getSubModules(), mdl), false) } else {
} mods[mdlID] = mergeModConfiguration(modsOld[mdlID], scanOptionalSubModules(mdl.getSubModules(), mdl), false)
} else { }
if(mdl.hasSubModules()){ } else {
const mdlID = mdl.getVersionlessID() if(mdl.hasSubModules()){
const v = scanOptionalSubModules(mdl.getSubModules(), mdl) const mdlID = mdl.getVersionlessID()
if(typeof v === 'object'){ const v = scanOptionalSubModules(mdl.getSubModules(), mdl)
if(modsOld[mdlID] == null){ if(typeof v === 'object'){
mods[mdlID] = v if(modsOld[mdlID] == null){
} else { mods[mdlID] = v
mods[mdlID] = mergeModConfiguration(modsOld[mdlID], v, true) } else {
} mods[mdlID] = mergeModConfiguration(modsOld[mdlID], v, true)
} }
} }
} }
} }
} }
}
syncedCfgs.push({
id, syncedCfgs.push({
mods id,
}) mods
})
} else {
} else {
const mods = {}
const mods = {}
for(let mdl of mdls){
const type = mdl.getType() for(let mdl of mdls){
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){ const type = mdl.getType()
if(!mdl.getRequired().isRequired()){ if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl) if(!mdl.getRequired().isRequired()){
} else { mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl)
if(mdl.hasSubModules()){ } else {
const v = scanOptionalSubModules(mdl.getSubModules(), mdl) if(mdl.hasSubModules()){
if(typeof v === 'object'){ const v = scanOptionalSubModules(mdl.getSubModules(), mdl)
mods[mdl.getVersionlessID()] = v if(typeof v === 'object'){
} mods[mdl.getVersionlessID()] = v
} }
} }
} }
} }
}
syncedCfgs.push({
id, syncedCfgs.push({
mods id,
}) mods
})
}
} }
}
ConfigManager.setModConfigurations(syncedCfgs)
ConfigManager.save() ConfigManager.setModConfigurations(syncedCfgs)
} ConfigManager.save()
}
/**
* Recursively scan for optional sub modules. If none are found, /**
* this function returns a boolean. If optional sub modules do exist, * Recursively scan for optional sub modules. If none are found,
* a recursive configuration object is returned. * this function returns a boolean. If optional sub modules do exist,
* * a recursive configuration object is returned.
* @returns {boolean | Object} The resolved mod configuration. *
*/ * @returns {boolean | Object} The resolved mod configuration.
function scanOptionalSubModules(mdls, origin){ */
if(mdls != null){ function scanOptionalSubModules(mdls, origin){
const mods = {} if(mdls != null){
const mods = {}
for(let mdl of mdls){
const type = mdl.getType() for(let mdl of mdls){
// Optional types. const type = mdl.getType()
if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){ // Optional types.
// It is optional. if(type === DistroManager.Types.ForgeMod || type === DistroManager.Types.LiteMod || type === DistroManager.Types.LiteLoader){
if(!mdl.getRequired().isRequired()){ // It is optional.
mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl) if(!mdl.getRequired().isRequired()){
} else { mods[mdl.getVersionlessID()] = scanOptionalSubModules(mdl.getSubModules(), mdl)
if(mdl.hasSubModules()){ } else {
const v = scanOptionalSubModules(mdl.getSubModules(), mdl) if(mdl.hasSubModules()){
if(typeof v === 'object'){ const v = scanOptionalSubModules(mdl.getSubModules(), mdl)
mods[mdl.getVersionlessID()] = v if(typeof v === 'object'){
} mods[mdl.getVersionlessID()] = v
} }
} }
} }
} }
}
if(Object.keys(mods).length > 0){
const ret = { if(Object.keys(mods).length > 0){
mods const ret = {
} mods
if(!origin.getRequired().isRequired()){ }
ret.value = origin.getRequired().isDefault() if(!origin.getRequired().isRequired()){
} ret.value = origin.getRequired().isDefault()
return ret }
} return ret
} }
return origin.getRequired().isDefault() }
} return origin.getRequired().isDefault()
}
/**
* Recursively merge an old configuration into a new configuration. /**
* * Recursively merge an old configuration into a new configuration.
* @param {boolean | Object} o The old configuration value. *
* @param {boolean | Object} n The new configuration value. * @param {boolean | Object} o The old configuration value.
* @param {boolean} nReq If the new value is a required mod. * @param {boolean | Object} n The new configuration value.
* * @param {boolean} nReq If the new value is a required mod.
* @returns {boolean | Object} The merged configuration. *
*/ * @returns {boolean | Object} The merged configuration.
function mergeModConfiguration(o, n, nReq = false){ */
if(typeof o === 'boolean'){ function mergeModConfiguration(o, n, nReq = false){
if(typeof n === 'boolean') return o if(typeof o === 'boolean'){
else if(typeof n === 'object'){ if(typeof n === 'boolean') return o
if(!nReq){ else if(typeof n === 'object'){
n.value = o if(!nReq){
} n.value = o
return n }
} return n
} else if(typeof o === 'object'){ }
if(typeof n === 'boolean') return typeof o.value !== 'undefined' ? o.value : true } else if(typeof o === 'object'){
else if(typeof n === 'object'){ if(typeof n === 'boolean') return typeof o.value !== 'undefined' ? o.value : true
if(!nReq){ else if(typeof n === 'object'){
n.value = typeof o.value !== 'undefined' ? o.value : true if(!nReq){
} n.value = typeof o.value !== 'undefined' ? o.value : true
}
const newMods = Object.keys(n.mods)
for(let i=0; i<newMods.length; i++){ const newMods = Object.keys(n.mods)
for(let i=0; i<newMods.length; i++){
const mod = newMods[i]
if(o.mods[mod] != null){ const mod = newMods[i]
n.mods[mod] = mergeModConfiguration(o.mods[mod], n.mods[mod]) if(o.mods[mod] != null){
} n.mods[mod] = mergeModConfiguration(o.mods[mod], n.mods[mod])
} }
}
return n
} return n
} }
// If for some reason we haven't been able to merge, }
// wipe the old value and use the new one. Just to be safe // If for some reason we haven't been able to merge,
return n // wipe the old value and use the new one. Just to be safe
} return n
}
function refreshDistributionIndex(remote, onSuccess, onError){
if(remote){ function refreshDistributionIndex(remote, onSuccess, onError){
DistroManager.pullRemote() if(remote){
.then(onSuccess) DistroManager.pullRemote()
.catch(onError) .then(onSuccess)
} else { .catch(onError)
DistroManager.pullLocal() } else {
.then(onSuccess) DistroManager.pullLocal()
.catch(onError) .then(onSuccess)
} .catch(onError)
} }
}
async function validateSelectedAccount(){
const selectedAcc = ConfigManager.getSelectedAccount() async function validateSelectedAccount(){
if(selectedAcc != null){ const selectedAcc = ConfigManager.getSelectedAccount()
const val = await AuthManager.validateSelected() if(selectedAcc != null){
if(!val){ const val = await AuthManager.validateSelected()
ConfigManager.removeAuthAccount(selectedAcc.uuid) if(!val){
ConfigManager.save() ConfigManager.removeAuthAccount(selectedAcc.uuid)
const accLen = Object.keys(ConfigManager.getAuthAccounts()).length ConfigManager.save()
setOverlayContent( const accLen = Object.keys(ConfigManager.getAuthAccounts()).length
'Failed to Refresh Login', setOverlayContent(
`We were unable to refresh the login for <strong>${selectedAcc.displayName}</strong>. Please ${accLen > 0 ? 'select another account or ' : ''} login again.`, 'Failed to Refresh Login',
'Login', `We were unable to refresh the login for <strong>${selectedAcc.displayName}</strong>. Please ${accLen > 0 ? 'select another account or ' : ''} login again.`,
'Select Another Account' 'Login',
) 'Select Another Account'
setOverlayHandler(() => { )
setOverlayHandler(() => {
const isMicrosoft = selectedAcc.type === 'microsoft'
const isMicrosoft = selectedAcc.type === 'microsoft'
if(isMicrosoft) {
// Empty for now if(isMicrosoft) {
} else { // Empty for now
// Mojang } else {
// For convenience, pre-populate the username of the account. // Mojang
document.getElementById('loginUsername').value = selectedAcc.username // For convenience, pre-populate the username of the account.
validateEmail(selectedAcc.username) document.getElementById('loginUsername').value = selectedAcc.username
} validateEmail(selectedAcc.username)
}
loginOptionsViewOnLoginSuccess = getCurrentView()
loginOptionsViewOnLoginCancel = VIEWS.loginOptions loginOptionsViewOnLoginSuccess = getCurrentView()
loginOptionsViewOnLoginCancel = VIEWS.loginOptions
if(accLen > 0) {
loginOptionsViewOnCancel = getCurrentView() if(accLen > 0) {
loginOptionsViewCancelHandler = () => { loginOptionsViewOnCancel = getCurrentView()
if(isMicrosoft) { loginOptionsViewCancelHandler = () => {
ConfigManager.addMicrosoftAuthAccount( if(isMicrosoft) {
selectedAcc.uuid, ConfigManager.addMicrosoftAuthAccount(
selectedAcc.accessToken, selectedAcc.uuid,
selectedAcc.username, selectedAcc.accessToken,
selectedAcc.expiresAt, selectedAcc.username,
selectedAcc.microsoft.access_token, selectedAcc.expiresAt,
selectedAcc.microsoft.refresh_token, selectedAcc.microsoft.access_token,
selectedAcc.microsoft.expires_at selectedAcc.microsoft.refresh_token,
) selectedAcc.microsoft.expires_at
} else { )
ConfigManager.addMojangAuthAccount(selectedAcc.uuid, selectedAcc.accessToken, selectedAcc.username, selectedAcc.displayName) } else {
} ConfigManager.addMojangAuthAccount(selectedAcc.uuid, selectedAcc.accessToken, selectedAcc.username, selectedAcc.displayName)
ConfigManager.save() }
validateSelectedAccount() ConfigManager.save()
} validateSelectedAccount()
loginOptionsCancelEnabled(true) }
} else { loginOptionsCancelEnabled(true)
loginOptionsCancelEnabled(false) } else {
} loginOptionsCancelEnabled(false)
toggleOverlay(false) }
switchView(getCurrentView(), VIEWS.loginOptions) toggleOverlay(false)
}) switchView(getCurrentView(), VIEWS.loginOptions)
setDismissHandler(() => { })
if(accLen > 1){ setDismissHandler(() => {
prepareAccountSelectionList() if(accLen > 1){
$('#overlayContent').fadeOut(250, () => { prepareAccountSelectionList()
bindOverlayKeys(true, 'accountSelectContent', true) $('#overlayContent').fadeOut(250, () => {
$('#accountSelectContent').fadeIn(250) bindOverlayKeys(true, 'accountSelectContent', true)
}) $('#accountSelectContent').fadeIn(250)
} else { })
const accountsObj = ConfigManager.getAuthAccounts() } else {
const accounts = Array.from(Object.keys(accountsObj), v => accountsObj[v]) const accountsObj = ConfigManager.getAuthAccounts()
// This function validates the account switch. const accounts = Array.from(Object.keys(accountsObj), v => accountsObj[v])
setSelectedAccount(accounts[0].uuid) // This function validates the account switch.
toggleOverlay(false) setSelectedAccount(accounts[0].uuid)
} toggleOverlay(false)
}) }
toggleOverlay(true, accLen > 0) })
} else { toggleOverlay(true, accLen > 0)
return true } else {
} return true
} else { }
return true } else {
} return true
} }
}
/**
* Temporary function to update the selected account along /**
* with the relevent UI elements. * Temporary function to update the selected account along
* * with the relevent UI elements.
* @param {string} uuid The UUID of the account. *
*/ * @param {string} uuid The UUID of the account.
function setSelectedAccount(uuid){ */
const authAcc = ConfigManager.setSelectedAccount(uuid) function setSelectedAccount(uuid){
ConfigManager.save() const authAcc = ConfigManager.setSelectedAccount(uuid)
updateSelectedAccount(authAcc) ConfigManager.save()
validateSelectedAccount() updateSelectedAccount(authAcc)
} validateSelectedAccount()
}
// Synchronous Listener
document.addEventListener('readystatechange', function(){ // Synchronous Listener
document.addEventListener('readystatechange', function(){
if (document.readyState === 'interactive' || document.readyState === 'complete'){
if(rscShouldLoad){ if (document.readyState === 'interactive' || document.readyState === 'complete'){
rscShouldLoad = false if(rscShouldLoad){
if(!fatalStartupError){ rscShouldLoad = false
const data = DistroManager.getDistribution() if(!fatalStartupError){
showMainUI(data) const data = DistroManager.getDistribution()
} else { showMainUI(data)
showFatalStartupError() } else {
} showFatalStartupError()
} }
} }
}
}, false)
}, false)
// Actions that must be performed after the distribution index is downloaded.
ipcRenderer.on('distributionIndexDone', (event, res) => { // Actions that must be performed after the distribution index is downloaded.
if(res) { ipcRenderer.on('distributionIndexDone', (event, res) => {
const data = DistroManager.getDistribution() if(res) {
syncModConfigurations(data) const data = DistroManager.getDistribution()
if(document.readyState === 'interactive' || document.readyState === 'complete'){ syncModConfigurations(data)
showMainUI(data) if(document.readyState === 'interactive' || document.readyState === 'complete'){
} else { showMainUI(data)
rscShouldLoad = true } else {
} rscShouldLoad = true
} else { }
fatalStartupError = true } else {
if(document.readyState === 'interactive' || document.readyState === 'complete'){ fatalStartupError = true
showFatalStartupError() if(document.readyState === 'interactive' || document.readyState === 'complete'){
} else { showFatalStartupError()
rscShouldLoad = true } else {
} rscShouldLoad = true
} }
}) }
})

25
app/loginOffline.ejs Normal file
View File

@ -0,0 +1,25 @@
<div id="loginOfflineContainer" style="display: none;">
<div id="loginContent">
<form id="loginForm">
<span id="loginSubheader">OFFLINE LOGIN</span>
<div class="loginFieldContainer">
<input id="loginOfflineUsername" class="loginField" type="text" placeholder="USERNAME"/>
</div>
<button id="loginOfflineButton" enabled>
<div id="loginOfflineButtonContent">
LOGIN
<svg id="loginSVG" viewBox="0 0 24.87 13.97">
<defs>
<style>.arrowLine{fill:none;stroke:#FFF;stroke-width:2px;transition: 0.25s ease;}</style>
</defs>
<polyline class="arrowLine" points="0.71 13.26 12.56 1.41 24.16 13.02"/>
</svg>
<div class="circle-loader">
<div class="checkmark draw"></div>
</div>
</div>
</button>
</form>
</div>
<script src="./assets/js/scripts/loginOffline.js"></script>
</div>

View File

@ -1,34 +1,39 @@
<div id="loginOptionsContainer" style="display: none;"> <div id="loginOptionsContainer" style="display: none;">
<div id="loginOptionsContent"> <div id="loginOptionsContent">
<div class="loginOptionsMainContent"> <div class="loginOptionsMainContent">
<h2>Login Options</h2> <h2>Login Options</h2>
<div class="loginOptionActions"> <div class="loginOptionActions">
<div class="loginOptionButtonContainer"> <div class="loginOptionButtonContainer">
<button id="loginOptionMicrosoft" class="loginOptionButton"> <button id="loginOptionMicrosoft" class="loginOptionButton">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 23 23"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 23 23">
<path fill="#f35325" d="M1 1h10v10H1z" /> <path fill="#f35325" d="M1 1h10v10H1z" />
<path fill="#81bc06" d="M12 1h10v10H12z" /> <path fill="#81bc06" d="M12 1h10v10H12z" />
<path fill="#05a6f0" d="M1 12h10v10H1z" /> <path fill="#05a6f0" d="M1 12h10v10H1z" />
<path fill="#ffba08" d="M12 12h10v10H12z" /> <path fill="#ffba08" d="M12 12h10v10H12z" />
</svg> </svg>
<span>Login with Microsoft</span> <span>Login with Microsoft</span>
</button> </button>
</div> </div>
<div class="loginOptionButtonContainer"> <div class="loginOptionButtonContainer">
<button id="loginOptionMojang" class="loginOptionButton"> <button id="loginOptionMojang" class="loginOptionButton">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 9.677 9.667"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 9.677 9.667">
<path d="M-26.332-12.098h2.715c-1.357.18-2.574 1.23-2.715 2.633z" fill="#fff" /> <path d="M-26.332-12.098h2.715c-1.357.18-2.574 1.23-2.715 2.633z" fill="#fff" />
<path d="M2.598.022h7.07L9.665 7c-.003 1.334-1.113 2.46-2.402 2.654H0V2.542C.134 1.2 1.3.195 2.598.022z" fill="#db2331" /> <path d="M2.598.022h7.07L9.665 7c-.003 1.334-1.113 2.46-2.402 2.654H0V2.542C.134 1.2 1.3.195 2.598.022z" fill="#db2331" />
<path d="M1.54 2.844c.314-.76 1.31-.46 1.954-.528.785-.083 1.503.272 2.1.758l.164-.9c.327.345.587.756.964 1.052.28.254.655-.342.86-.013.42.864.408 1.86.54 2.795l-.788-.373C6.9 4.17 5.126 3.052 3.656 3.685c-1.294.592-1.156 2.65.06 3.255 1.354.703 2.953.51 4.405.292-.07.42-.34.87-.834.816l-4.95.002c-.5.055-.886-.413-.838-.89l.04-4.315z" fill="#fff" /> <path d="M1.54 2.844c.314-.76 1.31-.46 1.954-.528.785-.083 1.503.272 2.1.758l.164-.9c.327.345.587.756.964 1.052.28.254.655-.342.86-.013.42.864.408 1.86.54 2.795l-.788-.373C6.9 4.17 5.126 3.052 3.656 3.685c-1.294.592-1.156 2.65.06 3.255 1.354.703 2.953.51 4.405.292-.07.42-.34.87-.834.816l-4.95.002c-.5.055-.886-.413-.838-.89l.04-4.315z" fill="#fff" />
</svg> </svg>
<span>Login with Mojang</span> <span>Login with Mojang</span>
</button> </button>
</div> </div>
</div> <div class="loginOptionButtonContainer">
<div id="loginOptionCancelContainer" style="display: none;"> <button id="loginOptionOffline" class="loginOptionButton">
<button id="loginOptionCancelButton">Cancel</button> <span>Proceed Offline</span>
</div> </button>
</div> </div>
</div> </div>
<script src="./assets/js/scripts/loginOptions.js"></script> <!--<div id="loginOptionCancelContainer" style="display: none;">
<button id="loginOptionCancelButton">Cancel</button>
</div>-->
</div>
</div>
<script src="./assets/js/scripts/loginOptions.js"></script>
</div> </div>