Compare commits

...

13 Commits

20 changed files with 9769 additions and 9221 deletions

View File

@ -1,55 +1,57 @@
<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') %> <%- include('dynmap') %>
<div id="loadingContainer"> </div>
<div id="loadingContent"> <%- include('overlay') %>
<div id="loadSpinnerContainer"> <div id="loadingContainer">
<img id="loadCenterImage" src="assets/images/LoadingSeal.png"> <div id="loadingContent">
<img id="loadSpinnerImage" class="rotating" src="assets/images/LoadingText.png"> <div id="loadSpinnerContainer">
</div> <img id="loadCenterImage" src="assets/images/LoadingSeal.png">
</div> <img id="loadSpinnerImage" class="rotating" src="assets/images/LoadingText.png">
</div> </div>
<script> </div>
// Load language </div>
for(let key of Object.keys(Lang.query('html'))){ <script>
document.getElementById(key).innerHTML = Lang.query(`html.${key}`) // Load language
} for(let key of Object.keys(Lang.query('html'))){
</script> document.getElementById(key).innerHTML = Lang.query(`html.${key}`)
</body> }
</script>
</body>
</html> </html>

View File

@ -498,7 +498,7 @@ body, button {
/* Header on login view. */ /* Header on login view. */
#loginSubheader { #loginSubheader {
font-family: 'Avenir Medium'; font-family: 'Avenir Medium';
margin-bottom: 25px; margin-bottom: 15px;
font-size: 12px; font-size: 12px;
letter-spacing: 1px; letter-spacing: 1px;
font-weight: bold; font-weight: bold;
@ -518,6 +518,7 @@ body, button {
fill: #fff; fill: #fff;
height: 20px; height: 20px;
width: 20px; width: 20px;
padding-bottom: 0.7em;
} }
/* Span which displays errors related to login field content. */ /* Span which displays errors related to login field content. */
@ -560,17 +561,19 @@ body, button {
.loginField { .loginField {
font-family: 'Avenir Book'; font-family: 'Avenir Book';
background: none; background: none;
border-width: 1.5px 0px 0px 0px; border-width: 1px 1px 1px 1px;
border-style: solid; border-style: solid;
border-radius: 5px;
backdrop-filter: blur(5px);
width: 250px; width: 250px;
margin-bottom: 20px; margin-bottom: 15px;
border-color: #fff; border-color: #3e3e3e;
color: rgba(255, 255, 255, 0.75); color: rgba(255, 255, 255, 0.75);
font-weight: bold; font-weight: bold;
text-align: center; text-align: center;
box-sizing: border-box; box-sizing: border-box;
padding: 7.5px; padding: 7.5px;
font-size: 10px; font-size: 14px;
letter-spacing: 1px; letter-spacing: 1px;
} }
.loginField:focus { .loginField:focus {
@ -674,6 +677,72 @@ body, button {
display: initial; display: initial;
} }
/* Login Offline button styles. */
#loginOfflineButton {
background: none;
font-weight: bold;
letter-spacing: 2px;
border: none;
padding: 15px 5px;
margin: 10px 0px;
cursor: pointer;
position: relative;
right: -20px;
transition: 0.5s ease;
}
#loginOfflineutton:disabled {
color: rgba(255, 255, 255, 0.75);
pointer-events: none;
}
#loginOfflineButton[loading] {
color: #fff;
}
#loginOfflineButton:hover,
#loginOfflineButton:focus {
text-shadow: 0px 0px 20px #fff;
outline: none;
}
#loginOfflineButton:active {
color: #c7c7c7;
text-shadow: 0px 0px 20px #c7c7c7;
}
#loginSVG {
-webkit-transform: translate3d(0, 0, 0);
overflow: visible;
transform: rotate(90deg);
margin-left: 20px;
transition: 0.25s ease;
width: 20px;
height: 20px;
}
#loginOfflineButton:hover #loginSVG,
#loginOfflineButton:focus #loginSVG {
-webkit-filter: drop-shadow(0px 0px 2px #fff);
}
#loginOfflineButton:active #loginSVG .arrowLine {
stroke: #c7c7c7;
}
#loginOfflineButton:active #loginSVG {
-webkit-filter: drop-shadow(0px 0px 2px #c7c7c7);
}
#loginOfflineButton:disabled #loginSVG .arrowLine {
stroke: rgba(255, 255, 255, 0.75);
}
#loginOfflineButtonContent {
display: flex;
align-items: center;
}
#loginOfflineButton .circle-loader,
#loginOfflineButton[loading] #loginSVG {
display: none;
}
#loginOfflineButton[loading] .circle-loader,
#loginOfflineButton #loginSVG {
display: initial;
}
.circle-loader { .circle-loader {
margin-left: 20px; margin-left: 20px;
@ -3964,4 +4033,23 @@ input:checked + .toggleSwitchSlider:before {
/* Class which is applied when the spinner image is spinning. */ /* Class which is applied when the spinner image is spinning. */
.rotating { .rotating {
animation: rotating 10s linear infinite; animation: rotating 10s linear infinite;
} }
/*******************************************************************************
* *
* Custom patches *
* *
******************************************************************************/
/* iframe patch for full size window. */
#iframecontainer {
width: 100%;
}
#dynmapiframe {
width: 100%;
height: 100%;
user-select: none;
}

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,11 @@ exports.addMojangAccount = async function(username, password) {
} }
} }
exports.addOfflineAccount = async function(usernameOffline){
//TODO: check for forbidden symbols and lenght!!
ConfigManager.addOfflineAccount(usernameOffline)
}
const AUTH_MODE = { FULL: 0, MS_REFRESH: 1, MC_REFRESH: 2 } const AUTH_MODE = { FULL: 0, MS_REFRESH: 1, MC_REFRESH: 2 }
/** /**
@ -181,6 +187,17 @@ exports.removeMojangAccount = async function(uuid){
} }
} }
exports.removeOfflineAccount = async function(uuid){
try {
ConfigManager.removeAuthAccount(uuid)
ConfigManager.save()
return Promise.resolve()
} catch (err){
log.error('Error while removing account', err)
return Promise.reject(err)
}
}
/** /**
* Remove a Microsoft account. It is expected that the caller will invoke the OAuth logout * Remove a Microsoft account. It is expected that the caller will invoke the OAuth logout
* through the ipc renderer. * through the ipc renderer.

View File

@ -1,3 +1,4 @@
const { uuid } = require('discord-rpc-patch/src/util')
const fs = require('fs-extra') const fs = require('fs-extra')
const os = require('os') const os = require('os')
const path = require('path') const path = require('path')
@ -353,6 +354,19 @@ exports.addMojangAuthAccount = function(uuid, accessToken, username, displayName
return config.authenticationDatabase[uuid] return config.authenticationDatabase[uuid]
} }
exports.addOfflineAccount = function(usernameOffline){
fake_uuid = usernameOffline
config.selectedAccount = fake_uuid
accessToken = ""
config.authenticationDatabase[fake_uuid] = {
type: 'offline',
accessToken,
username: usernameOffline.trim(),
uuid: fake_uuid.trim(),
displayName: usernameOffline.trim()
}
return config.authenticationDatabase[fake_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 = "https://skirda.gregbrzezinski.com/distribution.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

@ -0,0 +1,3 @@
dynmapDoneButton.addEventListener('click', () => {
switchView(getCurrentView(), VIEWS.landing)
})

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()
@ -325,7 +326,7 @@ function asyncSystemScan(mcVersion, launchAfter = true){
// Show this information to the user. // Show this information to the user.
setOverlayContent( setOverlayContent(
'No Compatible<br>Java Installation Found', 'No Compatible<br>Java Installation Found',
'In order to join WesterosCraft, you need a 64-bit installation of Java 8. Would you like us to install a copy?', 'In order to join Skirda, you need a 64-bit installation of Java 8. Would you like us to install a copy?',
'Install Java', 'Install Java',
'Install Manually' 'Install Manually'
) )
@ -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)
@ -808,6 +809,8 @@ function slide_(up){
//date.toLocaleDateString('en-US', {month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric'}) //date.toLocaleDateString('en-US', {month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric'})
//landingContainer.style.background = 'rgba(29, 29, 29, 0.55)' //landingContainer.style.background = 'rgba(29, 29, 29, 0.55)'
landingContainer.style.background = 'rgba(0, 0, 0, 0.50)' landingContainer.style.background = 'rgba(0, 0, 0, 0.50)'
//Frame bar dimming (map patch)
frameBar.style.background = 'rgba(0, 0, 0)'
setTimeout(() => { setTimeout(() => {
if(newsGlideCount === 1){ if(newsGlideCount === 1){
lCLCenter.style.transition = 'none' lCLCenter.style.transition = 'none'
@ -820,6 +823,8 @@ function slide_(up){
newsGlideCount-- newsGlideCount--
}, 2000) }, 2000)
landingContainer.style.background = null landingContainer.style.background = null
//Frame bar dimming revert (map patch)
frameBar.style.background = 'rgba(0, 0, 0, 0.50)'
lCLCenter.style.transition = null lCLCenter.style.transition = null
newsBtn.style.transition = null newsBtn.style.transition = null
newsContainer.style.top = '100%' newsContainer.style.top = '100%'
@ -833,8 +838,9 @@ function slide_(up){
// Bind news button. // Bind news button.
document.getElementById('newsButton').onclick = () => { document.getElementById('newsButton').onclick = () => {
switchView(getCurrentView(), VIEWS.dynmap, 500, 500)
// Toggle tabbing. // Toggle tabbing.
if(newsActive){ /*if(newsActive){
$('#landingContainer *').removeAttr('tabindex') $('#landingContainer *').removeAttr('tabindex')
$('#newsContainer *').attr('tabindex', '-1') $('#newsContainer *').attr('tabindex', '-1')
} else { } else {
@ -846,9 +852,9 @@ document.getElementById('newsButton').onclick = () => {
ConfigManager.setNewsCacheDismissed(true) ConfigManager.setNewsCacheDismissed(true)
ConfigManager.save() ConfigManager.save()
} }
} }*/
slide_(!newsActive) //slide_(!newsActive)
newsActive = !newsActive //newsActive = !newsActive
} }
// Array to store article meta. // Array to store article meta.

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

@ -331,6 +331,14 @@ document.getElementById('settingsAddMojangAccount').onclick = (e) => {
}) })
} }
document.getElementById('settingsAddOfflineAccount').onclick = (e) => {
switchView(getCurrentView(), VIEWS.loginOffline, 500, 500, () => {
loginViewOnCancel = VIEWS.settings
loginViewOnSuccess = VIEWS.settings
loginCancelEnabled(true)
})
}
// Bind the add microsoft account button. // Bind the add microsoft account button.
document.getElementById('settingsAddMicrosoftAccount').onclick = (e) => { document.getElementById('settingsAddMicrosoftAccount').onclick = (e) => {
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => { switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
@ -501,6 +509,24 @@ function processLogOut(val, isLastAccount){
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => { switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
ipcRenderer.send(MSFT_OPCODE.OPEN_LOGOUT, uuid, isLastAccount) ipcRenderer.send(MSFT_OPCODE.OPEN_LOGOUT, uuid, isLastAccount)
}) })
} if(targetAcc.type === 'offline') {
AuthManager.removeOfflineAccount(uuid).then(() => {
if(!isLastAccount && uuid === prevSelAcc.uuid){
const selAcc = ConfigManager.getSelectedAccount()
refreshAuthAccountSelected(selAcc.uuid)
updateSelectedAccount(selAcc)
//validateSelectedAccount()
}
if(isLastAccount) {
loginOptionsCancelEnabled(false)
loginOptionsViewOnLoginSuccess = VIEWS.settings
loginOptionsViewOnLoginCancel = VIEWS.loginOptions
switchView(getCurrentView(), VIEWS.loginOptions)
}
})
$(parent).fadeOut(250, () => {
parent.remove()
})
} else { } else {
AuthManager.removeMojangAccount(uuid).then(() => { AuthManager.removeMojangAccount(uuid).then(() => {
if(!isLastAccount && uuid === prevSelAcc.uuid){ if(!isLastAccount && uuid === prevSelAcc.uuid){
@ -603,6 +629,7 @@ function refreshAuthAccountSelected(uuid){
const settingsCurrentMicrosoftAccounts = document.getElementById('settingsCurrentMicrosoftAccounts') const settingsCurrentMicrosoftAccounts = document.getElementById('settingsCurrentMicrosoftAccounts')
const settingsCurrentMojangAccounts = document.getElementById('settingsCurrentMojangAccounts') const settingsCurrentMojangAccounts = document.getElementById('settingsCurrentMojangAccounts')
const settingsCurrentOfflineAccounts = document.getElementById('settingsCurrentOfflineAccounts')
/** /**
* Add auth account elements for each one stored in the authentication database. * Add auth account elements for each one stored in the authentication database.
@ -617,6 +644,7 @@ function populateAuthAccounts(){
let microsoftAuthAccountStr = '' let microsoftAuthAccountStr = ''
let mojangAuthAccountStr = '' let mojangAuthAccountStr = ''
let offlineAuthAccountStr = ''
authKeys.forEach((val) => { authKeys.forEach((val) => {
const acc = authAccounts[val] const acc = authAccounts[val]
@ -647,6 +675,8 @@ function populateAuthAccounts(){
if(acc.type === 'microsoft') { if(acc.type === 'microsoft') {
microsoftAuthAccountStr += accHtml microsoftAuthAccountStr += accHtml
} if (acc.type === 'offline') {
offlineAuthAccountStr += accHtml
} else { } else {
mojangAuthAccountStr += accHtml mojangAuthAccountStr += accHtml
} }
@ -655,6 +685,7 @@ function populateAuthAccounts(){
settingsCurrentMicrosoftAccounts.innerHTML = microsoftAuthAccountStr settingsCurrentMicrosoftAccounts.innerHTML = microsoftAuthAccountStr
settingsCurrentMojangAccounts.innerHTML = mojangAuthAccountStr settingsCurrentMojangAccounts.innerHTML = mojangAuthAccountStr
settingsCurrentOfflineAccounts.innerHTML = offlineAuthAccountStr
} }
/** /**

View File

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

23
app/dynmap.ejs Normal file
View File

@ -0,0 +1,23 @@
<div id="dynmapContainer" style="display: none;">
<div id="center">
<div class="bot_wrapper">
<div id="content">
<button id="dynmapDoneButton"> <!-- Rename all elements to dynmapEtc -->
<div id="newsButtonAlert" style="display: none;"></div>
<svg id="newsButtonSVG" viewBox="0 0 24.87 13.97">
<defs>
<style>.arrowLine{fill:none;stroke:#FFF;stroke-width:2px;}</style>
</defs>
<polyline class="arrowLine" points="0.71 13.26 12.56 1.41 24.16 13.02"/>
</svg>
&#10;<span id="newsButtonText">MAP</span>
</button>
</div>
</div>
</div>
<div id="iframecontainer">
<iframe id="dynmapiframe" src="https://minemap.gregbrzezinski.com" frameborder="0"></iframe>
</div>
<script src="./assets/js/scripts/dynmap.js"></script>
</div>

View File

@ -26,15 +26,24 @@
<div id="settingsTooltip">Settings</div> <div id="settingsTooltip">Settings</div>
</button> </button>
</div> </div>
<!-- map integration button
<div class="mediaContainer" id="mapMediaContainer">
<button class="mediaButton" id="mapMediaButton">
<svg id="settingsSVG" class="mediaSVG" viewBox="0 0 141.36 137.43">
<path d="M70.70475616319865,83.36934004916053 a15.320781354859122,15.320781354859122 0 1 1 14.454501310561755,-15.296030496450625 A14.850515045097694,14.850515045097694 0 0 1 70.70475616319865,83.36934004916053 M123.25082856443602,55.425620905968366 h-12.375429204248078 A45.54157947163293,45.54157947163293 0 0 0 107.21227231573047,46.243052436416285 l8.613298726156664,-9.108315894326587 a9.727087354538993,9.727087354538993 0 0 0 0,-13.167456673319956 l-3.465120177189462,-3.6631270444574313 a8.489544434114185,8.489544434114185 0 0 0 -12.375429204248078,0 l-8.613298726156664,9.108315894326587 A40.442902639482725,40.442902639482725 0 0 0 81.99114759747292,25.427580514871032 V12.532383284044531 a9.108315894326587,9.108315894326587 0 0 0 -8.811305593424633,-9.306322761594556 h-4.950171681699231 a9.108315894326587,9.108315894326587 0 0 0 -8.811305593424633,9.306322761594556 v12.895197230826497 a40.17064319698927,40.17064319698927 0 0 0 -9.331073620003052,4.0591407789933704 l-8.613298726156664,-9.108315894326587 a8.489544434114185,8.489544434114185 0 0 0 -12.375429204248078,0 L25.58394128451018,23.967279868769744 a9.727087354538993,9.727087354538993 0 0 0 0,13.167456673319956 L34.19724001066683,46.243052436416285 a45.07131316187151,45.07131316187151 0 0 0 -3.6631270444574313,9.083565035918088 h-12.375429204248078 a9.083565035918088,9.083565035918088 0 0 0 -8.811305593424633,9.306322761594556 v5.197680265784193 a9.108315894326587,9.108315894326587 0 0 0 8.811305593424633,9.306322761594556 h11.979415469712139 a45.69008462208391,45.69008462208391 0 0 0 4.0591407789933704,10.642869115653347 l-8.613298726156664,9.108315894326587 a9.727087354538993,9.727087354538993 0 0 0 0,13.167456673319956 l3.465120177189462,3.6631270444574313 a8.489544434114185,8.489544434114185 0 0 0 12.375429204248078,0 l8.613298726156664,-9.108315894326587 a40.49240435629971,40.49240435629971 0 0 0 9.331073620003052,4.0591407789933704 v12.895197230826497 a9.083565035918088,9.083565035918088 0 0 0 8.811305593424633,9.306322761594556 h4.950171681699231 A9.083565035918088,9.083565035918088 0 0 0 81.99114759747292,123.68848839660077 V110.79329116577425 a40.78941465720167,40.78941465720167 0 0 0 9.331073620003052,-4.0591407789933704 l8.613298726156664,9.108315894326587 a8.489544434114185,8.489544434114185 0 0 0 12.375429204248078,0 l3.465120177189462,-3.6631270444574313 a9.727087354538993,9.727087354538993 0 0 0 0,-13.167456673319956 l-8.613298726156664,-9.108315894326587 a45.665333763675406,45.665333763675406 0 0 0 4.034389920584874,-10.642869115653347 h12.004166328120636 a9.108315894326587,9.108315894326587 0 0 0 8.811305593424633,-9.306322761594556 v-5.197680265784193 a9.083565035918088,9.083565035918088 0 0 0 -8.811305593424633,-9.306322761594556 " id="svg_3" class=""/>
</svg>
<div id="settingsTooltip">Map</div>
</button>
</div>
-->
</div> </div>
<div class="mediaDivider"></div> <div class="mediaDivider"></div>
<div id="externalMedia"> <div id="externalMedia">
<div class="mediaContainer"> <div class="mediaContainer">
<a href="https://github.com/dscalzi/HeliosLauncher" class="mediaURL" id="linkURL"> <a href="https://discord.gg/JwKaVJG" class="mediaURL" id="discordURL">
<svg id="linkSVG" class="mediaSVG" viewBox="35.34 34.3575 70.68 68.71500"> <svg id="discordSVG" class="mediaSVG" viewBox="35.34 34.3575 70.68 68.71500">
<g> <g>
<path d="M75.37,65.51a3.85,3.85,0,0,0-1.73.42,8.22,8.22,0,0,1,.94,3.76A8.36,8.36,0,0,1,66.23,78H46.37a8.35,8.35,0,1,1,0-16.7h9.18a21.51,21.51,0,0,1,6.65-8.72H46.37a17.07,17.07,0,1,0,0,34.15H66.23A17,17,0,0,0,82.77,65.51Z"/> <path d="M81.23,78.48a6.14,6.14,0,1,1,6.14-6.14,6.14,6.14,0,0,1-6.14,6.14M60,78.48a6.14,6.14,0,1,1,6.14-6.14A6.14,6.14,0,0,1,60,78.48M104.41,73c-.92-7.7-8.24-22.9-8.24-22.9A43,43,0,0,0,88,45.59a17.88,17.88,0,0,0-8.38-1.27l-.13,1.06a23.52,23.52,0,0,1,5.8,1.95,87.59,87.59,0,0,1,8.17,4.87s-10.32-5.63-22.27-5.63a51.32,51.32,0,0,0-23.2,5.63,87.84,87.84,0,0,1,8.17-4.87,23.57,23.57,0,0,1,5.8-1.95l-.13-1.06a17.88,17.88,0,0,0-8.38,1.27,42.84,42.84,0,0,0-8.21,4.56S37.87,65.35,37,73s-.37,11.54-.37,11.54,4.22,5.68,9.9,7.14,7.7,1.47,7.7,1.47l3.75-4.68a21.22,21.22,0,0,1-4.65-2A24.47,24.47,0,0,1,47.93,82S61.16,88.4,70.68,88.4c10,0,22.75-6.44,22.75-6.44a24.56,24.56,0,0,1-5.35,4.56,21.22,21.22,0,0,1-4.65,2l3.75,4.68s2,0,7.7-1.47,9.89-7.14,9.89-7.14.55-3.85-.37-11.54"/>
<path d="M66,73.88a3.85,3.85,0,0,0,1.73-.42,8.22,8.22,0,0,1-.94-3.76,8.36,8.36,0,0,1,8.35-8.35H95A8.35,8.35,0,1,1,95,78H85.8a21.51,21.51,0,0,1-6.65,8.72H95a17.07,17.07,0,0,0,0-34.15H75.13A17,17,0,0,0,58.59,73.88Z"/>
</g> </g>
</svg> </svg>
</a> </a>
@ -49,39 +58,11 @@
</a> </a>
</div> </div>
<div class="mediaContainer"> <div class="mediaContainer">
<a href="#" class="mediaURL" id="instagramURL" disabled> <a href="https://github.com/dscalzi/HeliosLauncher" class="mediaURL" id="linkURL">
<svg id="instagramSVG" class="mediaSVG" viewBox="0 0 5040 5040"> <svg id="linkSVG" class="mediaSVG" viewBox="35.34 34.3575 70.68 68.71500">
<defs>
<radialGradient id="instaFill" cx="30%" cy="107%" r="150%">
<stop offset="0%" stop-color="#fdf497"/>
<stop offset="5%" stop-color="#fdf497"/>
<stop offset="45%" stop-color="#fd5949"/>
<stop offset="60%" stop-color="#d6249f"/>
<stop offset="90%" stop-color="#285AEB"/>
</radialGradient>
</defs>
<g> <g>
<path d="M1390 5024 c-163 -9 -239 -19 -315 -38 -281 -70 -477 -177 -660 -361 -184 -184 -292 -380 -361 -660 -43 -171 -53 -456 -53 -1445 0 -989 10 -1274 53 -1445 69 -280 177 -476 361 -660 184 -184 380 -292 660 -361 171 -43 456 -53 1445 -53 989 0 1274 10 1445 53 280 69 476 177 660 361 184 184 292 380 361 660 43 171 53 456 53 1445 0 989 -10 1274 -53 1445 -69 280 -177 476 -361 660 -184 184 -380 292 -660 361 -174 44 -454 53 -1470 52 -599 0 -960 -5 -1105 -14z m2230 -473 c58 -6 141 -18 185 -27 397 -78 638 -318 719 -714 37 -183 41 -309 41 -1290 0 -981 -4 -1107 -41 -1290 -81 -395 -319 -633 -714 -714 -183 -37 -309 -41 -1290 -41 -981 0 -1107 4 -1290 41 -397 81 -636 322 -714 719 -33 166 -38 296 -43 1100 -5 796 3 1203 27 1380 67 489 338 758 830 825 47 7 162 15 255 20 250 12 1907 4 2035 -9z"/> <path d="M75.37,65.51a3.85,3.85,0,0,0-1.73.42,8.22,8.22,0,0,1,.94,3.76A8.36,8.36,0,0,1,66.23,78H46.37a8.35,8.35,0,1,1,0-16.7h9.18a21.51,21.51,0,0,1,6.65-8.72H46.37a17.07,17.07,0,1,0,0,34.15H66.23A17,17,0,0,0,82.77,65.51Z"/>
<path d="M2355 3819 c-307 -42 -561 -172 -780 -400 -244 -253 -359 -543 -359 -899 0 -361 116 -648 367 -907 262 -269 563 -397 937 -397 374 0 675 128 937 397 251 259 367 546 367 907 0 361 -116 648 -367 907 -197 203 -422 326 -690 378 -101 20 -317 27 -412 14z m400 -509 c275 -88 470 -284 557 -560 20 -65 23 -95 23 -230 0 -135 -3 -165 -23 -230 -88 -278 -284 -474 -562 -562 -65 -20 -95 -23 -230 -23 -135 0 -165 3 -230 23 -278 88 -474 284 -562 562 -20 65 -23 95 -23 230 0 135 3 165 23 230 73 230 219 403 427 507 134 67 212 83 390 79 111 -3 155 -8 210 -26z"/> <path d="M66,73.88a3.85,3.85,0,0,0,1.73-.42,8.22,8.22,0,0,1-.94-3.76,8.36,8.36,0,0,1,8.35-8.35H95A8.35,8.35,0,1,1,95,78H85.8a21.51,21.51,0,0,1-6.65,8.72H95a17.07,17.07,0,0,0,0-34.15H75.13A17,17,0,0,0,58.59,73.88Z"/>
<path d="M3750 1473 c-29 -11 -66 -38 -106 -77 -70 -71 -94 -126 -94 -221 0 -95 24 -150 94 -221 72 -71 126 -94 225 -94 168 0 311 143 311 311 0 99 -23 154 -94 225 -43 42 -76 66 -110 77 -61 21 -166 21 -226 0z"/>
</g>
</svg>
</a>
</div>
<div class="mediaContainer">
<a href="#" class="mediaURL" id="youtubeURL" disabled>
<svg id="youtubeSVG" class="mediaSVG" viewBox="35.34 34.3575 70.68 68.71500">
<g>
<path d="M84.8,69.52,65.88,79.76V59.27Zm23.65.59c0-5.14-.79-17.63-3.94-20.57S99,45.86,73.37,45.86s-28,.73-31.14,3.68S38.29,65,38.29,70.11s.79,17.63,3.94,20.57,5.52,3.68,31.14,3.68,28-.74,31.14-3.68,3.94-15.42,3.94-20.57"/>
</g>
</svg>
</a>
</div>
<div class="mediaContainer">
<a href="https://discord.gg/zNWUXdt" class="mediaURL" id="discordURL">
<svg id="discordSVG" class="mediaSVG" viewBox="35.34 34.3575 70.68 68.71500">
<g>
<path d="M81.23,78.48a6.14,6.14,0,1,1,6.14-6.14,6.14,6.14,0,0,1-6.14,6.14M60,78.48a6.14,6.14,0,1,1,6.14-6.14A6.14,6.14,0,0,1,60,78.48M104.41,73c-.92-7.7-8.24-22.9-8.24-22.9A43,43,0,0,0,88,45.59a17.88,17.88,0,0,0-8.38-1.27l-.13,1.06a23.52,23.52,0,0,1,5.8,1.95,87.59,87.59,0,0,1,8.17,4.87s-10.32-5.63-22.27-5.63a51.32,51.32,0,0,0-23.2,5.63,87.84,87.84,0,0,1,8.17-4.87,23.57,23.57,0,0,1,5.8-1.95l-.13-1.06a17.88,17.88,0,0,0-8.38,1.27,42.84,42.84,0,0,0-8.21,4.56S37.87,65.35,37,73s-.37,11.54-.37,11.54,4.22,5.68,9.9,7.14,7.7,1.47,7.7,1.47l3.75-4.68a21.22,21.22,0,0,1-4.65-2A24.47,24.47,0,0,1,47.93,82S61.16,88.4,70.68,88.4c10,0,22.75-6.44,22.75-6.44a24.56,24.56,0,0,1-5.35,4.56,21.22,21.22,0,0,1-4.65,2l3.75,4.68s2,0,7.7-1.47,9.89-7.14,9.89-7.14.55-3.85-.37-11.54"/>
</g> </g>
</svg> </svg>
</a> </a>
@ -125,7 +106,7 @@
<div class="bot_wrapper"> <div class="bot_wrapper">
<div id="content"> <div id="content">
<button id="newsButton"> <button id="newsButton">
<!--<img src="assets/images/icons/arrow.svg" id="newsButtonSVG"/>-->
<div id="newsButtonAlert" style="display: none;"></div> <div id="newsButtonAlert" style="display: none;"></div>
<svg id="newsButtonSVG" viewBox="0 0 24.87 13.97"> <svg id="newsButtonSVG" viewBox="0 0 24.87 13.97">
<defs> <defs>
@ -133,10 +114,10 @@
</defs> </defs>
<polyline class="arrowLine" points="0.71 13.26 12.56 1.41 24.16 13.02"/> <polyline class="arrowLine" points="0.71 13.26 12.56 1.41 24.16 13.02"/>
</svg> </svg>
&#10;<span id="newsButtonText">NEWS</span> &#10;<span id="newsButtonText">MAP</span>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<div id="right"> <div id="right">
<div class="bot_wrapper"> <div class="bot_wrapper">
@ -159,7 +140,10 @@
</div> </div>
</div> </div>
<div id="newsContainer"> <div id="newsContainer">
<div id="newsContent" article="-1" style="display: none;"> <div id="iframecontainer">
<iframe id="dynmapiframe" src="https://minemap.gregbrzezinski.com" frameborder="0"></iframe>
</div>
<!-- <div id="newsContent" article="-1" style="display: none;">
<div id="newsStatusContainer"> <div id="newsStatusContainer">
<div id="newsStatusContent"> <div id="newsStatusContent">
<div id="newsTitleContainer"> <div id="newsTitleContainer">
@ -198,7 +182,7 @@
<div id="newsArticleContainer"> <div id="newsArticleContainer">
<div id="newsArticleContent"> <div id="newsArticleContent">
<div id="newsArticleContentScrollable"> <div id="newsArticleContentScrollable">
<!-- Article Content -->
</div> </div>
</div> </div>
</div> </div>
@ -214,7 +198,7 @@
<div id="newsErrorNone" style="display: none;"> <div id="newsErrorNone" style="display: none;">
<span id="nENoneSpan" class="newsErrorContent">No News</span> <span id="nENoneSpan" class="newsErrorContent">No News</span>
</div> </div>
</div> </div>-->
</div> </div>
<script src="./assets/js/scripts/landing.js"></script> <script src="./assets/js/scripts/landing.js"></script>
</div> </div>

31
app/loginOffline.ejs Normal file
View File

@ -0,0 +1,31 @@
<div id="loginOfflineContainer" style="display: none;">
<div id="loginCancelContainer" style="display: none;">
<button id="loginCancelButton">
<div id="loginCancelIcon">X</div>
<span id="loginCancelText">Cancel</span>
</button>
</div>
<div id="loginContent">
<form id="loginForm" style="margin-top: 2.5em;">
<span id="loginSubheader">OFFLINE LOGIN</span>
<div class="loginFieldContainer">
<input id="loginOfflineUsername" class="loginField" type="text" placeholder="USERNAME"/>
</div>
<button id="loginOfflineButton" class="loginButton" 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>

View File

@ -68,6 +68,20 @@
<!-- Mojang auth accounts populated here. --> <!-- Mojang auth accounts populated here. -->
</div> </div>
</div> </div>
<div class="settingsAuthAccountTypeContainer">
<div class="settingsAuthAccountTypeHeader">
<div class="settingsAuthAccountTypeHeaderLeft">
<span>Offline</span>
</div>
<div class="settingsAuthAccountTypeHeaderRight">
<button class="settingsAddAuthAccount" id="settingsAddOfflineAccount">+ Add Offline Account</button>
</div>
</div>
<div class="settingsCurrentAccounts" id="settingsCurrentOfflineAccounts">
<!-- Offline auth accounts populated here. -->
</div>
</div>
</div> </div>
<div id="settingsTabMinecraft" class="settingsTab" style="display: none;"> <div id="settingsTabMinecraft" class="settingsTab" style="display: none;">
<div class="settingsTabHeader"> <div class="settingsTabHeader">

16678
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,9 @@
"request": "^2.88.2", "request": "^2.88.2",
"semver": "^7.3.7", "semver": "^7.3.7",
"tar-fs": "^2.1.1", "tar-fs": "^2.1.1",
"winreg": "^1.2.4" "winreg": "^1.2.4",
"7zip-bin": "^5.2.0"
}, },
"devDependencies": { "devDependencies": {
"electron": "^19.0.10", "electron": "^19.0.10",