From e2178809ab034275076650bb61c9bfa9ded682cf Mon Sep 17 00:00:00 2001 From: JFronny Date: Fri, 29 Oct 2021 22:50:42 +0200 Subject: [PATCH] A lot of changes --- LICENSE | 2 +- README.md | 5 + build.gradle | 11 +- reference/mcman.cs | 205 ---------------- settings.gradle | 2 +- .../io/gitlab/jfronny/glaunch/Config.java | 6 - .../install/steps/WriteMetadataStep.java | 22 -- .../gitlab/jfronny/glaunch/model/Rules.java | 4 - .../jfronny/glaunch/windows/AlertWindow.java | 18 -- .../jfronny/glaunch/windows/LogWindow.java | 21 -- .../io/gitlab/jfronny/inceptum/Config.java | 7 + .../GLaunch.java => inceptum/Inceptum.java} | 45 ++-- .../gson/GsonIgnore.java | 2 +- .../gson/GsonIgnoreExclusionStrategy.java | 2 +- .../gson/MinecraftArgumentDeserializer.java | 6 +- .../gson/OauthTokenResponseDeserializer.java | 36 +++ .../gson/RulesDeserializer.java | 6 +- .../install/SetupStepInfo.java | 6 +- .../{glaunch => inceptum}/install/Step.java | 4 +- .../{glaunch => inceptum}/install/Steps.java | 4 +- .../install/steps/DownloadAssetsStep.java | 16 +- .../install/steps/DownloadClientStep.java | 16 +- .../install/steps/DownloadLibrariesStep.java | 18 +- .../install/steps/SetupDirsStep.java | 16 +- .../install/steps/WriteMetadataStep.java | 22 ++ .../inceptum/model/InceptumVersion.java | 5 + .../model/InstanceMeta.java | 6 +- .../model/microsoft/LoginResponse.java | 16 ++ .../model/microsoft/OauthTokenResponse.java | 29 +++ .../inceptum/model/microsoft/Profile.java | 22 ++ .../inceptum/model/microsoft/Store.java | 13 ++ .../model/microsoft/XboxLiveAuthResponse.java | 28 +++ .../model/mojang}/AssetIndex.java | 2 +- .../model/mojang}/MinecraftArgument.java | 2 +- .../jfronny/inceptum/model/mojang/Rules.java | 4 + .../model/mojang}/VersionInfo.java | 2 +- .../model/mojang}/VersionsList.java | 2 +- .../model/mojang}/VersionsListInfo.java | 2 +- .../jfronny/inceptum/util/HttpUtils.java | 151 ++++++++++++ .../util/MapAppender.java | 2 +- .../util/mojang => inceptum/util}/McApi.java | 17 +- .../util/ThrowingSupplier.java | 2 +- .../{glaunch => inceptum}/util/Utils.java | 66 ++++-- .../util/VersionInfoLibraryResolver.java | 8 +- .../inceptum/util/account/AccountManager.java | 106 +++++++++ .../inceptum/util/account/AuthInfo.java | 15 ++ .../util/account/MicrosoftAccount.java | 220 ++++++++++++++++++ .../util/account/MicrosoftAuthAPI.java | 102 ++++++++ .../windows/AboutWindow.java | 4 +- .../jfronny/inceptum/windows/AlertWindow.java | 36 +++ .../jfronny/inceptum/windows/LogWindow.java | 27 +++ .../windows/MainWindow.java | 89 ++++--- .../windows/MicrosoftLoginWindow.java | 189 +++++++++++++++ .../windows/NewInstanceWindow.java | 46 ++-- .../{glaunch => inceptum}/windows/Window.java | 6 +- src/main/resources/log4j2.xml | 2 +- src/main/resources/version.json | 3 + 57 files changed, 1274 insertions(+), 452 deletions(-) create mode 100644 README.md delete mode 100644 reference/mcman.cs delete mode 100644 src/main/java/io/gitlab/jfronny/glaunch/Config.java delete mode 100644 src/main/java/io/gitlab/jfronny/glaunch/install/steps/WriteMetadataStep.java delete mode 100644 src/main/java/io/gitlab/jfronny/glaunch/model/Rules.java delete mode 100644 src/main/java/io/gitlab/jfronny/glaunch/windows/AlertWindow.java delete mode 100644 src/main/java/io/gitlab/jfronny/glaunch/windows/LogWindow.java create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/Config.java rename src/main/java/io/gitlab/jfronny/{glaunch/GLaunch.java => inceptum/Inceptum.java} (82%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/gson/GsonIgnore.java (86%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/gson/GsonIgnoreExclusionStrategy.java (90%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/gson/MinecraftArgumentDeserializer.java (90%) create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/gson/OauthTokenResponseDeserializer.java rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/gson/RulesDeserializer.java (91%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/install/SetupStepInfo.java (50%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/install/Step.java (54%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/install/Steps.java (79%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/install/steps/DownloadAssetsStep.java (60%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/install/steps/DownloadClientStep.java (52%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/install/steps/DownloadLibrariesStep.java (54%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/install/steps/SetupDirsStep.java (52%) create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/install/steps/WriteMetadataStep.java create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/model/InceptumVersion.java rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/model/InstanceMeta.java (54%) create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/LoginResponse.java create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/OauthTokenResponse.java create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/Profile.java create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/Store.java create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/XboxLiveAuthResponse.java rename src/main/java/io/gitlab/jfronny/{glaunch/model => inceptum/model/mojang}/AssetIndex.java (78%) rename src/main/java/io/gitlab/jfronny/{glaunch/model => inceptum/model/mojang}/MinecraftArgument.java (61%) create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/model/mojang/Rules.java rename src/main/java/io/gitlab/jfronny/{glaunch/model => inceptum/model/mojang}/VersionInfo.java (97%) rename src/main/java/io/gitlab/jfronny/{glaunch/model => inceptum/model/mojang}/VersionsList.java (82%) rename src/main/java/io/gitlab/jfronny/{glaunch/model => inceptum/model/mojang}/VersionsListInfo.java (78%) create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/util/HttpUtils.java rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/util/MapAppender.java (98%) rename src/main/java/io/gitlab/jfronny/{glaunch/util/mojang => inceptum/util}/McApi.java (73%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/util/ThrowingSupplier.java (68%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/util/Utils.java (68%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/util/VersionInfoLibraryResolver.java (79%) create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/util/account/AccountManager.java create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/util/account/AuthInfo.java create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/util/account/MicrosoftAccount.java create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/util/account/MicrosoftAuthAPI.java rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/windows/AboutWindow.java (76%) create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/windows/AlertWindow.java create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/windows/LogWindow.java rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/windows/MainWindow.java (58%) create mode 100644 src/main/java/io/gitlab/jfronny/inceptum/windows/MicrosoftLoginWindow.java rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/windows/NewInstanceWindow.java (68%) rename src/main/java/io/gitlab/jfronny/{glaunch => inceptum}/windows/Window.java (85%) create mode 100644 src/main/resources/version.json diff --git a/LICENSE b/LICENSE index fec68fe..01122f6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -GLaunch - A FOSS Launcher for Minecraft written in Java +Inceptum - A FOSS Launcher for Minecraft written in Java Copyright (C) 2021 JFronny This program is free software: you can redistribute it and/or modify diff --git a/README.md b/README.md new file mode 100644 index 0000000..9abd568 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Inceptum Launcher +A FOSS Launcher for Minecraft written in Java + +Inceptum is a WIP minecraft launcher\ +Since it is very bare-bones currently, I would not recommend using it \ No newline at end of file diff --git a/build.gradle b/build.gradle index e338ab4..e0c117c 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ group 'io.gitlab.jfronny' version '1.0' application { - mainClass = 'io.gitlab.jfronny.glaunch.GLaunch' + mainClass = 'io.gitlab.jfronny.inceptum.Inceptum' } repositories { @@ -26,6 +26,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.8' implementation "org.apache.logging.log4j:log4j-api:$log4jVersion" implementation "org.apache.logging.log4j:log4j-core:$log4jVersion" + implementation 'net.freeutils:jlhttp:2.6' implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion") @@ -45,6 +46,14 @@ dependencies { implementation "io.github.spair:imgui-java-natives-macos:$imguiVersion" } +processResources { + inputs.property "version", project.version + + filesMatching("version.json") { + expand "version": project.version + } +} + class FileOutput extends DefaultTask { @OutputFile File output diff --git a/reference/mcman.cs b/reference/mcman.cs deleted file mode 100644 index 790e9d2..0000000 --- a/reference/mcman.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Text; -using Newtonsoft.Json.Linq; - -namespace McMan -{ - internal class Program - { - public static void Main(string[] args) - { - if (args.Length == 0) - { - args = new string[1]; - Console.WriteLine("Username"); - Console.Write("> "); - args[0] = Console.ReadLine(); - } - if (args.Length == 1) - { - args = new [] {args[0], "choose"}; - } - if (args.Length == 2) - { - Console.WriteLine("Max memory (empty for 2G)"); - Console.Write("> "); - args = new [] {args[0], args[1], Console.ReadLine()}; - } - if (Directory.Exists("assets")) - Directory.Delete("assets", true); - if (Directory.Exists("Game")) - Directory.Delete("Game", true); - Directory.CreateDirectory("Game"); - using WebClient client = new WebClient(); - string maxMem = args[2]; - maxMem = string.IsNullOrWhiteSpace(maxMem) ? "2G" : maxMem; - Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - JObject gameJson = GetMinecraftJson(client, args[1], out string ver); - DownloadClient(client, gameJson["downloads"]["client"]); - DownloadAssets(client, gameJson["assetIndex"], ver); - string classpath = DownloadLibs(client, (JArray) gameJson["libraries"]); - Console.WriteLine("Creating launch script"); - classpath += "client.jar"; - const string mainClass = "net.minecraft.client.main.Main"; - const string java = "javaw"; - const string lowMem = "768M"; - string javaOptions = - $"-server -splash:splash.png -d64 -da -dsa -Xrs -Xms{lowMem} -Xmx{maxMem} -XX:NewSize={lowMem} -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:-UseAdaptiveSizePolicy -XX:+DisableExplicitGC -Djava.library.path=libraries -cp {classpath} {mainClass}"; - string jvmBat = - $"start /D %cd% /I /HIGH {java} {javaOptions} --username {args[0]} --version {ver} --gameDir %cd% --assetsDir assets --assetIndex {gameJson.Value("assets")} --uuid 2536abce90e8476a871679918164abc5 --accessToken 99abe417230342cb8e9e2168ab46297a --userType legacy --versionType release --nativeLauncherVersion 307"; - File.WriteAllText(Path.Combine("Game", "start.bat"), jvmBat); - Console.WriteLine("Done!"); - } - - //Downloads a JRE, doesn't start - /*private static void GetJRE(WebClient client, bool x64) - { - Console.WriteLine("Fetching JRE Versions..."); - IEnumerable json = JArray.Parse(client.DownloadString("https://api.adoptopenjdk.net/v2/info/releases/openjdk11")) - .SelectMany(s => (JArray)s["binaries"]); - Console.WriteLine("Selecting version..."); - string download = json.Last(s => - s.Value("os") == "windows" && s.Value("architecture") == (x64 ? "x64" : "x32") && - s.Value("binary_type") == "jre" && s.Value("heap_size") == "normal") - .Value("binary_link"); - Console.WriteLine($"Downloading from {download}..."); - if (Directory.Exists("jdk")) - Directory.Delete("jdk", true); - Directory.CreateDirectory("jdk"); - using MemoryStream ms = new MemoryStream(client.DownloadData(download)); - Console.WriteLine("Extracting..."); - using ZipArchive archive = new ZipArchive(ms); - archive.ExtractToDirectory("jdk"); - Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(Directory.GetDirectories("jdk")[0], "Game"); - Directory.Delete("jdk", true); - }*/ - - private static string DownloadLibs(WebClient client, JArray libraries) - { - string output = ""; - string libdir = Path.Combine("Game", "libraries"); - Console.WriteLine("Downloading libs..."); - Directory.CreateDirectory(libdir); - Console.CursorTop++; - JToken[] tmp = libraries.ToArray(); - for (int i = 0; i < tmp.Length; i++) - { - JToken lib = tmp[i]; - if (lib["downloads"].Any(s => ((JProperty) s).Name == "classifiers")) - output = lib["downloads"]["classifiers"].Where(lib2 => ((JProperty) lib2).Name == "natives-windows") - .Aggregate( - output, - (current, lib2) => current + DownloadLib(client, lib2.First.Value("sha1"), - lib2.First.Value("url"), lib2.First.Value("path"), - libdir)); - Console.CursorLeft = 0; - Console.CursorTop--; - Console.WriteLine($"[{i + 1}/{tmp.Length}] Getting {lib["downloads"]["artifact"].Value("path")}{new string(' ', 10)}"); - output += DownloadLib(client, - lib["downloads"]["artifact"].Value("sha1"), - lib["downloads"]["artifact"].Value("url"), - lib["downloads"]["artifact"].Value("path"), - libdir); - } - return output; - } - - private static string DownloadLib(WebClient client, string hash, string url, string path, string libdir) - { - string tmp = path.Split('/').Aggregate(libdir, Path.Combine); - byte[] libB = client.DownloadData(url); - if (Tools.Hash(libB) != hash) - { - Console.WriteLine("ERROR: HASH MISMATCH IN LIBRARY"); - throw new Exception(); - } - Directory.CreateDirectory(Path.GetDirectoryName(tmp)); - File.WriteAllBytes(tmp, libB); - return $"{path.Split('/').Aggregate("libraries", Path.Combine)};"; - } - - private static void DownloadAssets(WebClient client, JToken assetIndex, string version) - { - bool success = false; - byte[] indexB = new byte[0]; - while (!success) - { - Console.WriteLine("Downloading asset index..."); - indexB = client.DownloadData(assetIndex.Value("url")); - success = Tools.Hash(indexB) == assetIndex.Value("sha1"); - } - JObject index = JObject.Parse(Encoding.Default.GetString(indexB)); - string assetPath = Path.Combine("Game", "assets"); - Directory.CreateDirectory(Path.Combine(assetPath, "indexes")); - File.WriteAllBytes(Path.Combine(assetPath, "indexes", Path.GetFileName(assetIndex.Value("url"))), indexB); - Console.WriteLine("Processing..."); - Console.CursorTop++; - JToken[] tmp = Enumerable.ToArray(index["objects"]); - for (int i = 0; i < tmp.Length; i++) - { - JProperty asset = (JProperty)tmp[i]; - string name = asset.Name; - Console.CursorLeft = 0; - Console.CursorTop--; - Console.WriteLine($"[{i + 1}/{tmp.Length}] Getting {name}{new string(' ', 10)}"); - string hash = asset.First.Value("hash"); - string url = $"http://resources.download.minecraft.net/{string.Join("", hash.Substring(0, 2))}/{hash}"; - byte[] file = client.DownloadData(url); - if (Tools.Hash(file) != hash) - { - Console.WriteLine("ERROR: HASH MISMATCH IN ASSET"); - throw new Exception(); - } - string path = Path.Combine(assetPath, "objects", hash.Substring(0, 2)); - if (!Directory.Exists(path)) - Directory.CreateDirectory(path); - File.WriteAllBytes(Path.Combine(path, hash), file); - } - } - - private static void DownloadClient(WebClient client, JToken clientJson) - { - bool success = false; - byte[] mcClient = new byte[0]; - while (!success) - { - Console.WriteLine("Downloading client..."); - mcClient = client.DownloadData(clientJson.Value("url")); - success = Tools.Hash(mcClient) == clientJson.Value("sha1"); - } - File.WriteAllBytes(Path.Combine("Game", "client.jar"), mcClient); - } - - private static JObject GetMinecraftJson(WebClient client, string verdat, out string version) - { - Tuple tmp; - Tuple[] versions = GetVersions(client); - if (verdat == "choose") - { - Console.WriteLine("Select a Version (empty for latest)"); - for (int i = 0; i < versions.Length; i++) Console.WriteLine($"{i + 1}\t- {versions[i].Item1}"); - Console.Write("> "); - string inp = Console.ReadLine(); - tmp = string.IsNullOrWhiteSpace(inp) ? versions.Last() : versions[int.Parse(inp) - 1]; - } - else - tmp = versions.First(s => s.Item1.ToLower().Equals(verdat.ToLower(), StringComparison.InvariantCultureIgnoreCase)); - version = tmp.Item1; - Console.WriteLine($"Using minecraft {version}"); - return JObject.Parse(client.DownloadString(tmp.Item2)); - } - - private static Tuple[] GetVersions(WebClient client) - { - JObject tmp = - JObject.Parse(client.DownloadString("https://launchermeta.mojang.com/mc/game/version_manifest.json")); - return tmp["versions"].Where(token => token.Value("type") == "release").Select(token => new Tuple(token.Value("id"), token.Value("url"))).OrderBy(s => Version.Parse(s.Item1)).ToArray(); - } - } -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 391ea9e..7804cb3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -rootProject.name = 'GLaunch2' +rootProject.name = 'Inceptum' diff --git a/src/main/java/io/gitlab/jfronny/glaunch/Config.java b/src/main/java/io/gitlab/jfronny/glaunch/Config.java deleted file mode 100644 index 7719e7d..0000000 --- a/src/main/java/io/gitlab/jfronny/glaunch/Config.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.gitlab.jfronny.glaunch; - -public class Config { - public boolean snapshots = false; //TODO allow configuring - public boolean darkTheme = false; -} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/install/steps/WriteMetadataStep.java b/src/main/java/io/gitlab/jfronny/glaunch/install/steps/WriteMetadataStep.java deleted file mode 100644 index 8b4cbf7..0000000 --- a/src/main/java/io/gitlab/jfronny/glaunch/install/steps/WriteMetadataStep.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.gitlab.jfronny.glaunch.install.steps; - -import io.gitlab.jfronny.glaunch.GLaunch; -import io.gitlab.jfronny.glaunch.install.SetupStepInfo; -import io.gitlab.jfronny.glaunch.install.Step; -import io.gitlab.jfronny.glaunch.model.InstanceMeta; -import io.gitlab.jfronny.glaunch.util.Utils; - -import java.io.IOException; -import java.nio.file.Path; - -public class WriteMetadataStep implements Step { - @Override - public void execute(SetupStepInfo info) throws IOException { - GLaunch.LOGGER.info("Writing metadata"); - Path metaDir = GLaunch.INSTANCE_DIR.resolve(info.name()).resolve("instance.json"); - InstanceMeta meta = new InstanceMeta(); - meta.loaderType = info.loaderType(); - meta.version = info.version().id; - Utils.writeObject(metaDir, meta); - } -} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/model/Rules.java b/src/main/java/io/gitlab/jfronny/glaunch/model/Rules.java deleted file mode 100644 index de40951..0000000 --- a/src/main/java/io/gitlab/jfronny/glaunch/model/Rules.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.gitlab.jfronny.glaunch.model; - -public record Rules(boolean allow) { -} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/windows/AlertWindow.java b/src/main/java/io/gitlab/jfronny/glaunch/windows/AlertWindow.java deleted file mode 100644 index 8345618..0000000 --- a/src/main/java/io/gitlab/jfronny/glaunch/windows/AlertWindow.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.gitlab.jfronny.glaunch.windows; - -import imgui.ImGui; - -public class AlertWindow extends Window { - private final String message; - - public AlertWindow(String message) { - super("Warning"); - this.message = message; - } - - @Override - public void draw() { - ImGui.text(message); - if (ImGui.button("OK")) close(); - } -} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/windows/LogWindow.java b/src/main/java/io/gitlab/jfronny/glaunch/windows/LogWindow.java deleted file mode 100644 index 3e3bb67..0000000 --- a/src/main/java/io/gitlab/jfronny/glaunch/windows/LogWindow.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.gitlab.jfronny.glaunch.windows; - -import imgui.ImGui; -import io.gitlab.jfronny.glaunch.util.MapAppender; - -public class LogWindow extends Window { - public LogWindow() { - super("Log"); - } - - @Override - public void draw() { - //TODO autoscroll - MapAppender.LOG.forEach(ImGui::textUnformatted); - } - - @Override - public int getFlags() { - return 0; - } -} diff --git a/src/main/java/io/gitlab/jfronny/inceptum/Config.java b/src/main/java/io/gitlab/jfronny/inceptum/Config.java new file mode 100644 index 0000000..ac861ee --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/Config.java @@ -0,0 +1,7 @@ +package io.gitlab.jfronny.inceptum; + +public class Config { + public boolean snapshots = false; + public boolean darkTheme = false; + public String lastAccount; +} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/GLaunch.java b/src/main/java/io/gitlab/jfronny/inceptum/Inceptum.java similarity index 82% rename from src/main/java/io/gitlab/jfronny/glaunch/GLaunch.java rename to src/main/java/io/gitlab/jfronny/inceptum/Inceptum.java index 71b873e..a783b17 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/GLaunch.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/Inceptum.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch; +package io.gitlab.jfronny.inceptum; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -7,14 +7,18 @@ import imgui.ImGuiIO; import imgui.flag.ImGuiConfigFlags; import imgui.gl3.ImGuiImplGl3; import imgui.glfw.ImGuiImplGlfw; -import io.gitlab.jfronny.glaunch.gson.GsonIgnoreExclusionStrategy; -import io.gitlab.jfronny.glaunch.gson.MinecraftArgumentDeserializer; -import io.gitlab.jfronny.glaunch.gson.RulesDeserializer; -import io.gitlab.jfronny.glaunch.model.MinecraftArgument; -import io.gitlab.jfronny.glaunch.model.Rules; -import io.gitlab.jfronny.glaunch.util.Utils; -import io.gitlab.jfronny.glaunch.windows.MainWindow; -import io.gitlab.jfronny.glaunch.windows.Window; +import io.gitlab.jfronny.inceptum.gson.GsonIgnoreExclusionStrategy; +import io.gitlab.jfronny.inceptum.gson.MinecraftArgumentDeserializer; +import io.gitlab.jfronny.inceptum.gson.OauthTokenResponseDeserializer; +import io.gitlab.jfronny.inceptum.gson.RulesDeserializer; +import io.gitlab.jfronny.inceptum.model.InceptumVersion; +import io.gitlab.jfronny.inceptum.model.microsoft.OauthTokenResponse; +import io.gitlab.jfronny.inceptum.model.mojang.MinecraftArgument; +import io.gitlab.jfronny.inceptum.model.mojang.Rules; +import io.gitlab.jfronny.inceptum.util.Utils; +import io.gitlab.jfronny.inceptum.util.account.AccountManager; +import io.gitlab.jfronny.inceptum.windows.MainWindow; +import io.gitlab.jfronny.inceptum.windows.Window; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.lwjgl.glfw.*; @@ -25,6 +29,7 @@ import org.lwjgl.system.MemoryUtil; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.reflect.Modifier; import java.nio.IntBuffer; import java.nio.file.Files; @@ -35,15 +40,15 @@ import java.util.Set; //TODO generate gitignore //TODO mods browser -//TODO load and launch instance from metadata //TODO allow instance sync through metadata //TODO update checker -public class GLaunch { +public class Inceptum { public static final Set WINDOWS = new LinkedHashSet<>(); - public static final Logger LOGGER = LogManager.getFormatterLogger("GLaunch"); + public static final Logger LOGGER = LogManager.getFormatterLogger("Inceptum"); public static final Gson GSON = new GsonBuilder() .registerTypeAdapter(MinecraftArgument.class, new MinecraftArgumentDeserializer()) .registerTypeAdapter(Rules.class, new RulesDeserializer()) + .registerTypeAdapter(OauthTokenResponse.class, new OauthTokenResponseDeserializer()) .excludeFieldsWithModifiers(Modifier.TRANSIENT) .excludeFieldsWithModifiers(Modifier.PRIVATE) .addSerializationExclusionStrategy(new GsonIgnoreExclusionStrategy()) @@ -55,9 +60,16 @@ public class GLaunch { public static final Path ASSETS_DIR = Path.of("run/assets"); public static final Path LIBRARIES_DIR = Path.of("run/libraries"); private static final Path CONFIG_PATH = Path.of("run/glaunch2.json"); + public static final Path ACCOUNTS_PATH = Path.of("run/accounts.json"); + public static InceptumVersion VERSION; public static Config CONFIG; public static void main(String[] args) throws IOException { + try (InputStream is = Inceptum.class.getClassLoader().getResourceAsStream("version.json"); + InputStreamReader isr = new InputStreamReader(is)) { + VERSION = GSON.fromJson(isr, InceptumVersion.class); + } + LOGGER.info("Launching Inceptum v" + VERSION.version); LOGGER.info("Setting up cache dir"); if (!Files.exists(CONFIG_PATH)) Utils.writeObject(CONFIG_PATH, new Config()); CONFIG = Utils.loadObject(CONFIG_PATH, Config.class); @@ -65,6 +77,7 @@ public class GLaunch { if (!Files.exists(INSTANCE_DIR)) Files.createDirectories(INSTANCE_DIR); if (!Files.exists(ASSETS_DIR)) Files.createDirectories(ASSETS_DIR); if (!Files.exists(LIBRARIES_DIR)) Files.createDirectories(LIBRARIES_DIR); + AccountManager.loadAccounts(); LOGGER.info("Initializing UI"); WINDOWS.add(new MainWindow()); init(); @@ -131,7 +144,7 @@ public class GLaunch { GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_FALSE); - handle = GLFW.glfwCreateWindow(10, 10, "GLaunch", MemoryUtil.NULL, MemoryUtil.NULL); + handle = GLFW.glfwCreateWindow(10, 10, "Inceptum", MemoryUtil.NULL, MemoryUtil.NULL); if (handle == MemoryUtil.NULL) { throw new RuntimeException("Failed to create the GLFW window"); @@ -182,7 +195,7 @@ public class GLaunch { io.setConfigViewportsNoAutoMerge(true); //TODO use included icons (https://www.nerdfonts.com/cheat-sheet) //Nerd Fonts-patched ubuntu font - try (InputStream is = GLaunch.class.getClassLoader().getResourceAsStream("font.ttf")) { + try (InputStream is = Inceptum.class.getClassLoader().getResourceAsStream("font.ttf")) { assert is != null; io.setFontDefault(io.getFonts().addFontFromMemoryTTF(is.readAllBytes(), 16f)); } catch (IOException e) { @@ -201,9 +214,9 @@ public class GLaunch { imGuiGlfw.newFrame(); ImGui.newFrame(); //render - if (GLaunch.WINDOWS.isEmpty()) exit(); + if (Inceptum.WINDOWS.isEmpty()) exit(); else { - for (Window window : GLaunch.WINDOWS.toArray(new Window[0])) { + for (Window window : Inceptum.WINDOWS.toArray(new Window[0])) { if (window.isNew()) window.preFirstDraw(); if (ImGui.begin(window.getName(), window.getOpenState(), window.getFlags())) window.draw(); ImGui.end(); diff --git a/src/main/java/io/gitlab/jfronny/glaunch/gson/GsonIgnore.java b/src/main/java/io/gitlab/jfronny/inceptum/gson/GsonIgnore.java similarity index 86% rename from src/main/java/io/gitlab/jfronny/glaunch/gson/GsonIgnore.java rename to src/main/java/io/gitlab/jfronny/inceptum/gson/GsonIgnore.java index 2dee3f7..ac98a91 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/gson/GsonIgnore.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/gson/GsonIgnore.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch.gson; +package io.gitlab.jfronny.inceptum.gson; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/main/java/io/gitlab/jfronny/glaunch/gson/GsonIgnoreExclusionStrategy.java b/src/main/java/io/gitlab/jfronny/inceptum/gson/GsonIgnoreExclusionStrategy.java similarity index 90% rename from src/main/java/io/gitlab/jfronny/glaunch/gson/GsonIgnoreExclusionStrategy.java rename to src/main/java/io/gitlab/jfronny/inceptum/gson/GsonIgnoreExclusionStrategy.java index b136110..5e98bfb 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/gson/GsonIgnoreExclusionStrategy.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/gson/GsonIgnoreExclusionStrategy.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch.gson; +package io.gitlab.jfronny.inceptum.gson; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; diff --git a/src/main/java/io/gitlab/jfronny/glaunch/gson/MinecraftArgumentDeserializer.java b/src/main/java/io/gitlab/jfronny/inceptum/gson/MinecraftArgumentDeserializer.java similarity index 90% rename from src/main/java/io/gitlab/jfronny/glaunch/gson/MinecraftArgumentDeserializer.java rename to src/main/java/io/gitlab/jfronny/inceptum/gson/MinecraftArgumentDeserializer.java index 65df978..704d2c2 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/gson/MinecraftArgumentDeserializer.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/gson/MinecraftArgumentDeserializer.java @@ -1,8 +1,8 @@ -package io.gitlab.jfronny.glaunch.gson; +package io.gitlab.jfronny.inceptum.gson; import com.google.gson.*; -import io.gitlab.jfronny.glaunch.model.MinecraftArgument; -import io.gitlab.jfronny.glaunch.model.Rules; +import io.gitlab.jfronny.inceptum.model.mojang.MinecraftArgument; +import io.gitlab.jfronny.inceptum.model.mojang.Rules; import java.lang.reflect.Type; import java.util.LinkedHashSet; diff --git a/src/main/java/io/gitlab/jfronny/inceptum/gson/OauthTokenResponseDeserializer.java b/src/main/java/io/gitlab/jfronny/inceptum/gson/OauthTokenResponseDeserializer.java new file mode 100644 index 0000000..9b5144d --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/gson/OauthTokenResponseDeserializer.java @@ -0,0 +1,36 @@ +package io.gitlab.jfronny.inceptum.gson; + +import com.google.gson.*; +import io.gitlab.jfronny.inceptum.model.microsoft.OauthTokenResponse; + +import java.lang.reflect.Type; +import java.util.Date; + +public class OauthTokenResponseDeserializer implements JsonDeserializer { + @Override + public OauthTokenResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + OauthTokenResponse oauthTokenResponse = new OauthTokenResponse(); + JsonObject rootObject = json.getAsJsonObject(); + + oauthTokenResponse.tokenType = rootObject.get("token_type").getAsString(); + oauthTokenResponse.expiresIn = rootObject.get("expires_in").getAsInt(); + oauthTokenResponse.scope = rootObject.get("scope").getAsString(); + oauthTokenResponse.accessToken = rootObject.get("access_token").getAsString(); + oauthTokenResponse.refreshToken = rootObject.get("refresh_token").getAsString(); + oauthTokenResponse.userId = rootObject.get("user_id").getAsString(); + + if (rootObject.has("foci")) { + oauthTokenResponse.foci = rootObject.get("foci").getAsString(); + } + + if (rootObject.has("expires_at")) { + oauthTokenResponse.expiresAt = context.deserialize(rootObject.get("expires_at"), Date.class); + } else { + oauthTokenResponse.expiresAt = new Date(); + oauthTokenResponse.expiresAt + .setTime(oauthTokenResponse.expiresAt.getTime() + (oauthTokenResponse.expiresIn * 1000)); + } + + return oauthTokenResponse; + } +} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/gson/RulesDeserializer.java b/src/main/java/io/gitlab/jfronny/inceptum/gson/RulesDeserializer.java similarity index 91% rename from src/main/java/io/gitlab/jfronny/glaunch/gson/RulesDeserializer.java rename to src/main/java/io/gitlab/jfronny/inceptum/gson/RulesDeserializer.java index 90f1f61..899b15f 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/gson/RulesDeserializer.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/gson/RulesDeserializer.java @@ -1,8 +1,8 @@ -package io.gitlab.jfronny.glaunch.gson; +package io.gitlab.jfronny.inceptum.gson; import com.google.gson.*; -import io.gitlab.jfronny.glaunch.model.Rules; -import io.gitlab.jfronny.glaunch.util.Utils; +import io.gitlab.jfronny.inceptum.model.mojang.Rules; +import io.gitlab.jfronny.inceptum.util.Utils; import java.lang.reflect.Type; diff --git a/src/main/java/io/gitlab/jfronny/glaunch/install/SetupStepInfo.java b/src/main/java/io/gitlab/jfronny/inceptum/install/SetupStepInfo.java similarity index 50% rename from src/main/java/io/gitlab/jfronny/glaunch/install/SetupStepInfo.java rename to src/main/java/io/gitlab/jfronny/inceptum/install/SetupStepInfo.java index c86ae77..92d4378 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/install/SetupStepInfo.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/install/SetupStepInfo.java @@ -1,7 +1,7 @@ -package io.gitlab.jfronny.glaunch.install; +package io.gitlab.jfronny.inceptum.install; -import io.gitlab.jfronny.glaunch.model.VersionInfo; -import io.gitlab.jfronny.glaunch.windows.NewInstanceWindow; +import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo; +import io.gitlab.jfronny.inceptum.windows.NewInstanceWindow; public record SetupStepInfo(VersionInfo version, NewInstanceWindow.LoaderType loaderType, diff --git a/src/main/java/io/gitlab/jfronny/glaunch/install/Step.java b/src/main/java/io/gitlab/jfronny/inceptum/install/Step.java similarity index 54% rename from src/main/java/io/gitlab/jfronny/glaunch/install/Step.java rename to src/main/java/io/gitlab/jfronny/inceptum/install/Step.java index cfabb8f..295c24c 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/install/Step.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/install/Step.java @@ -1,6 +1,4 @@ -package io.gitlab.jfronny.glaunch.install; - -import io.gitlab.jfronny.glaunch.model.VersionInfo; +package io.gitlab.jfronny.inceptum.install; import java.io.IOException; diff --git a/src/main/java/io/gitlab/jfronny/glaunch/install/Steps.java b/src/main/java/io/gitlab/jfronny/inceptum/install/Steps.java similarity index 79% rename from src/main/java/io/gitlab/jfronny/glaunch/install/Steps.java rename to src/main/java/io/gitlab/jfronny/inceptum/install/Steps.java index c08dab4..acee1a1 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/install/Steps.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/install/Steps.java @@ -1,6 +1,6 @@ -package io.gitlab.jfronny.glaunch.install; +package io.gitlab.jfronny.inceptum.install; -import io.gitlab.jfronny.glaunch.install.steps.*; +import io.gitlab.jfronny.inceptum.install.steps.*; import java.util.LinkedHashSet; import java.util.List; diff --git a/src/main/java/io/gitlab/jfronny/glaunch/install/steps/DownloadAssetsStep.java b/src/main/java/io/gitlab/jfronny/inceptum/install/steps/DownloadAssetsStep.java similarity index 60% rename from src/main/java/io/gitlab/jfronny/glaunch/install/steps/DownloadAssetsStep.java rename to src/main/java/io/gitlab/jfronny/inceptum/install/steps/DownloadAssetsStep.java index 3303f68..c046c32 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/install/steps/DownloadAssetsStep.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/install/steps/DownloadAssetsStep.java @@ -1,10 +1,10 @@ -package io.gitlab.jfronny.glaunch.install.steps; +package io.gitlab.jfronny.inceptum.install.steps; -import io.gitlab.jfronny.glaunch.GLaunch; -import io.gitlab.jfronny.glaunch.install.SetupStepInfo; -import io.gitlab.jfronny.glaunch.install.Step; -import io.gitlab.jfronny.glaunch.model.AssetIndex; -import io.gitlab.jfronny.glaunch.util.mojang.McApi; +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.install.SetupStepInfo; +import io.gitlab.jfronny.inceptum.install.Step; +import io.gitlab.jfronny.inceptum.model.mojang.AssetIndex; +import io.gitlab.jfronny.inceptum.util.McApi; import java.io.IOException; import java.nio.file.Files; @@ -14,13 +14,13 @@ import java.util.Map; public class DownloadAssetsStep implements Step { @Override public void execute(SetupStepInfo info) throws IOException { - Path o = GLaunch.ASSETS_DIR.resolve("objects"); + Path o = Inceptum.ASSETS_DIR.resolve("objects"); for (Map.Entry entry : McApi.getAssetIndex(info.version()).objects.entrySet()) { Path fPath = o.resolve(entry.getValue().hash.substring(0, 2)); if (!Files.exists(fPath)) Files.createDirectories(fPath); fPath = fPath.resolve(entry.getValue().hash); if (Files.exists(fPath)) return; - GLaunch.LOGGER.info("Downloading asset: " + entry.getKey()); + Inceptum.LOGGER.info("Downloading asset: " + entry.getKey()); McApi.downloadAsset(entry.getValue(), fPath); } } diff --git a/src/main/java/io/gitlab/jfronny/glaunch/install/steps/DownloadClientStep.java b/src/main/java/io/gitlab/jfronny/inceptum/install/steps/DownloadClientStep.java similarity index 52% rename from src/main/java/io/gitlab/jfronny/glaunch/install/steps/DownloadClientStep.java rename to src/main/java/io/gitlab/jfronny/inceptum/install/steps/DownloadClientStep.java index 983978c..53d1461 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/install/steps/DownloadClientStep.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/install/steps/DownloadClientStep.java @@ -1,10 +1,10 @@ -package io.gitlab.jfronny.glaunch.install.steps; +package io.gitlab.jfronny.inceptum.install.steps; -import io.gitlab.jfronny.glaunch.GLaunch; -import io.gitlab.jfronny.glaunch.install.SetupStepInfo; -import io.gitlab.jfronny.glaunch.install.Step; -import io.gitlab.jfronny.glaunch.model.VersionInfo; -import io.gitlab.jfronny.glaunch.util.Utils; +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.install.SetupStepInfo; +import io.gitlab.jfronny.inceptum.install.Step; +import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo; +import io.gitlab.jfronny.inceptum.util.Utils; import java.io.IOException; import java.nio.file.Files; @@ -14,8 +14,8 @@ public class DownloadClientStep implements Step { @Override public void execute(SetupStepInfo info) throws IOException { VersionInfo.Downloads.Download client = info.version().downloads.client; - GLaunch.LOGGER.info("Downloading client"); - Path parentPath = GLaunch.LIBRARIES_DIR.resolve("net/minecraft/minecraft"); + Inceptum.LOGGER.info("Downloading client"); + Path parentPath = Inceptum.LIBRARIES_DIR.resolve("net/minecraft/minecraft"); if (!Files.exists(parentPath)) Files.createDirectories(parentPath); Utils.downloadFile(client.url, client.sha1, parentPath.resolve(info.version().id + ".jar")); } diff --git a/src/main/java/io/gitlab/jfronny/glaunch/install/steps/DownloadLibrariesStep.java b/src/main/java/io/gitlab/jfronny/inceptum/install/steps/DownloadLibrariesStep.java similarity index 54% rename from src/main/java/io/gitlab/jfronny/glaunch/install/steps/DownloadLibrariesStep.java rename to src/main/java/io/gitlab/jfronny/inceptum/install/steps/DownloadLibrariesStep.java index 19cbf39..dcc59ce 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/install/steps/DownloadLibrariesStep.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/install/steps/DownloadLibrariesStep.java @@ -1,11 +1,11 @@ -package io.gitlab.jfronny.glaunch.install.steps; +package io.gitlab.jfronny.inceptum.install.steps; -import io.gitlab.jfronny.glaunch.GLaunch; -import io.gitlab.jfronny.glaunch.install.SetupStepInfo; -import io.gitlab.jfronny.glaunch.install.Step; -import io.gitlab.jfronny.glaunch.model.VersionInfo; -import io.gitlab.jfronny.glaunch.util.Utils; -import io.gitlab.jfronny.glaunch.util.VersionInfoLibraryResolver; +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.install.SetupStepInfo; +import io.gitlab.jfronny.inceptum.install.Step; +import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo; +import io.gitlab.jfronny.inceptum.util.Utils; +import io.gitlab.jfronny.inceptum.util.VersionInfoLibraryResolver; import java.io.IOException; import java.nio.file.Files; @@ -15,10 +15,10 @@ public class DownloadLibrariesStep implements Step { @Override public void execute(SetupStepInfo info) throws IOException { for (VersionInfo.Library.Downloads.Artifact artifact : VersionInfoLibraryResolver.getRelevant(info.version())) { - Path path = GLaunch.LIBRARIES_DIR.resolve(artifact.path); + Path path = Inceptum.LIBRARIES_DIR.resolve(artifact.path); if (Files.exists(path)) return; //TODO allow maven-like download for fabric - GLaunch.LOGGER.info("Downloading library: " + artifact.path); + Inceptum.LOGGER.info("Downloading library: " + artifact.path); if (!Files.exists(path.getParent())) Files.createDirectories(path.getParent()); Utils.downloadFile(artifact.url, artifact.sha1, path); } diff --git a/src/main/java/io/gitlab/jfronny/glaunch/install/steps/SetupDirsStep.java b/src/main/java/io/gitlab/jfronny/inceptum/install/steps/SetupDirsStep.java similarity index 52% rename from src/main/java/io/gitlab/jfronny/glaunch/install/steps/SetupDirsStep.java rename to src/main/java/io/gitlab/jfronny/inceptum/install/steps/SetupDirsStep.java index ca57f19..95e5efa 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/install/steps/SetupDirsStep.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/install/steps/SetupDirsStep.java @@ -1,8 +1,9 @@ -package io.gitlab.jfronny.glaunch.install.steps; +package io.gitlab.jfronny.inceptum.install.steps; -import io.gitlab.jfronny.glaunch.GLaunch; -import io.gitlab.jfronny.glaunch.install.SetupStepInfo; -import io.gitlab.jfronny.glaunch.install.Step; +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.install.SetupStepInfo; +import io.gitlab.jfronny.inceptum.install.Step; +import io.gitlab.jfronny.inceptum.windows.NewInstanceWindow; import java.io.IOException; import java.nio.file.Files; @@ -11,12 +12,13 @@ import java.nio.file.Path; public class SetupDirsStep implements Step { @Override public void execute(SetupStepInfo info) throws IOException { - GLaunch.LOGGER.info("Setting up instance dirs"); - Path iDir = GLaunch.INSTANCE_DIR.resolve(info.name()); + Inceptum.LOGGER.info("Setting up instance dirs"); + Path iDir = Inceptum.INSTANCE_DIR.resolve(info.name()); if (!Files.exists(iDir)) { Files.createDirectories(iDir); Files.createDirectories(iDir.resolve("config")); - Files.createDirectories(iDir.resolve("mods")); + if (info.loaderType() != NewInstanceWindow.LoaderType.None) + Files.createDirectories(iDir.resolve("mods")); Files.createDirectories(iDir.resolve("resourcepacks")); Files.createDirectories(iDir.resolve("saves")); Files.createDirectories(iDir.resolve("screenshots")); diff --git a/src/main/java/io/gitlab/jfronny/inceptum/install/steps/WriteMetadataStep.java b/src/main/java/io/gitlab/jfronny/inceptum/install/steps/WriteMetadataStep.java new file mode 100644 index 0000000..43f57cb --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/install/steps/WriteMetadataStep.java @@ -0,0 +1,22 @@ +package io.gitlab.jfronny.inceptum.install.steps; + +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.install.SetupStepInfo; +import io.gitlab.jfronny.inceptum.install.Step; +import io.gitlab.jfronny.inceptum.model.InstanceMeta; +import io.gitlab.jfronny.inceptum.util.Utils; + +import java.io.IOException; +import java.nio.file.Path; + +public class WriteMetadataStep implements Step { + @Override + public void execute(SetupStepInfo info) throws IOException { + Inceptum.LOGGER.info("Writing metadata"); + Path metaDir = Inceptum.INSTANCE_DIR.resolve(info.name()).resolve("instance.json"); + InstanceMeta meta = new InstanceMeta(); + meta.loaderType = info.loaderType(); + meta.version = info.version().id; + Utils.writeObject(metaDir, meta); + } +} diff --git a/src/main/java/io/gitlab/jfronny/inceptum/model/InceptumVersion.java b/src/main/java/io/gitlab/jfronny/inceptum/model/InceptumVersion.java new file mode 100644 index 0000000..ede818f --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/InceptumVersion.java @@ -0,0 +1,5 @@ +package io.gitlab.jfronny.inceptum.model; + +public class InceptumVersion { + public String version; +} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/model/InstanceMeta.java b/src/main/java/io/gitlab/jfronny/inceptum/model/InstanceMeta.java similarity index 54% rename from src/main/java/io/gitlab/jfronny/glaunch/model/InstanceMeta.java rename to src/main/java/io/gitlab/jfronny/inceptum/model/InstanceMeta.java index 5db1cf4..5ffa956 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/model/InstanceMeta.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/InstanceMeta.java @@ -1,7 +1,7 @@ -package io.gitlab.jfronny.glaunch.model; +package io.gitlab.jfronny.inceptum.model; -import io.gitlab.jfronny.glaunch.gson.GsonIgnore; -import io.gitlab.jfronny.glaunch.windows.NewInstanceWindow; +import io.gitlab.jfronny.inceptum.gson.GsonIgnore; +import io.gitlab.jfronny.inceptum.windows.NewInstanceWindow; import java.nio.file.Path; diff --git a/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/LoginResponse.java b/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/LoginResponse.java new file mode 100644 index 0000000..14259c4 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/LoginResponse.java @@ -0,0 +1,16 @@ +package io.gitlab.jfronny.inceptum.model.microsoft; + +import com.google.gson.annotations.SerializedName; + +public class LoginResponse { + public String username; + + @SerializedName("access_token") + public String accessToken; + + @SerializedName("token_type") + public String tokenType; + + @SerializedName("expires_in") + public Integer expiresIn; +} diff --git a/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/OauthTokenResponse.java b/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/OauthTokenResponse.java new file mode 100644 index 0000000..f3acde8 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/OauthTokenResponse.java @@ -0,0 +1,29 @@ +package io.gitlab.jfronny.inceptum.model.microsoft; + +import com.google.gson.annotations.SerializedName; + +import java.util.Date; + +public class OauthTokenResponse { + @SerializedName("token_type") + public String tokenType; + + @SerializedName("expires_in") + public Integer expiresIn; + + @SerializedName("expires_at") + public Date expiresAt; + + public String scope; + + @SerializedName("access_token") + public String accessToken; + + @SerializedName("refresh_token") + public String refreshToken; + + @SerializedName("user_id") + public String userId; + + public String foci; +} diff --git a/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/Profile.java b/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/Profile.java new file mode 100644 index 0000000..8e7178d --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/Profile.java @@ -0,0 +1,22 @@ +package io.gitlab.jfronny.inceptum.model.microsoft; + +import java.util.List; + +public class Profile { + public String id; + public String name; + public List skins; + public List capes; + + public static class Skin { + public String id; + public String state; + public String url; + public String variant; + public String alias; + } + + public static class Cape { + public String id; + } +} diff --git a/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/Store.java b/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/Store.java new file mode 100644 index 0000000..9e0e779 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/Store.java @@ -0,0 +1,13 @@ +package io.gitlab.jfronny.inceptum.model.microsoft; + +import java.util.List; + +public class Store { + public List items; + public String signature; + + public static class StoreItem { + public String name; + public String signature; + } +} diff --git a/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/XboxLiveAuthResponse.java b/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/XboxLiveAuthResponse.java new file mode 100644 index 0000000..2f15bf3 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/microsoft/XboxLiveAuthResponse.java @@ -0,0 +1,28 @@ +package io.gitlab.jfronny.inceptum.model.microsoft; + +import com.google.gson.annotations.SerializedName; + +import java.util.Date; +import java.util.List; + +public class XboxLiveAuthResponse { + @SerializedName("IssueInstant") + public Date issueInstant; + + @SerializedName("NotAfter") + public Date notAfter; + + @SerializedName("Token") + public String token; + + @SerializedName("DisplayClaims") + public DisplayClaims displayClaims; + + public static class DisplayClaims { + public List xui; + + public static class XUIClaim { + public String uhs; + } + } +} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/model/AssetIndex.java b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/AssetIndex.java similarity index 78% rename from src/main/java/io/gitlab/jfronny/glaunch/model/AssetIndex.java rename to src/main/java/io/gitlab/jfronny/inceptum/model/mojang/AssetIndex.java index 6a895b4..ddbbdd7 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/model/AssetIndex.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/AssetIndex.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch.model; +package io.gitlab.jfronny.inceptum.model.mojang; import java.util.Map; diff --git a/src/main/java/io/gitlab/jfronny/glaunch/model/MinecraftArgument.java b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/MinecraftArgument.java similarity index 61% rename from src/main/java/io/gitlab/jfronny/glaunch/model/MinecraftArgument.java rename to src/main/java/io/gitlab/jfronny/inceptum/model/mojang/MinecraftArgument.java index de7faa6..c54e495 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/model/MinecraftArgument.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/MinecraftArgument.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch.model; +package io.gitlab.jfronny.inceptum.model.mojang; import java.util.Set; diff --git a/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/Rules.java b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/Rules.java new file mode 100644 index 0000000..5e21588 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/Rules.java @@ -0,0 +1,4 @@ +package io.gitlab.jfronny.inceptum.model.mojang; + +public record Rules(boolean allow) { +} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/model/VersionInfo.java b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/VersionInfo.java similarity index 97% rename from src/main/java/io/gitlab/jfronny/glaunch/model/VersionInfo.java rename to src/main/java/io/gitlab/jfronny/inceptum/model/mojang/VersionInfo.java index cf72cac..909797b 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/model/VersionInfo.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/VersionInfo.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch.model; +package io.gitlab.jfronny.inceptum.model.mojang; import java.util.List; import java.util.Map; diff --git a/src/main/java/io/gitlab/jfronny/glaunch/model/VersionsList.java b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/VersionsList.java similarity index 82% rename from src/main/java/io/gitlab/jfronny/glaunch/model/VersionsList.java rename to src/main/java/io/gitlab/jfronny/inceptum/model/mojang/VersionsList.java index e2b1a1e..373d921 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/model/VersionsList.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/VersionsList.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch.model; +package io.gitlab.jfronny.inceptum.model.mojang; import java.util.List; diff --git a/src/main/java/io/gitlab/jfronny/glaunch/model/VersionsListInfo.java b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/VersionsListInfo.java similarity index 78% rename from src/main/java/io/gitlab/jfronny/glaunch/model/VersionsListInfo.java rename to src/main/java/io/gitlab/jfronny/inceptum/model/mojang/VersionsListInfo.java index 4f13ab6..e7b101b 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/model/VersionsListInfo.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/model/mojang/VersionsListInfo.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch.model; +package io.gitlab.jfronny.inceptum.model.mojang; import java.util.Date; diff --git a/src/main/java/io/gitlab/jfronny/inceptum/util/HttpUtils.java b/src/main/java/io/gitlab/jfronny/inceptum/util/HttpUtils.java new file mode 100644 index 0000000..3062de9 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/util/HttpUtils.java @@ -0,0 +1,151 @@ +package io.gitlab.jfronny.inceptum.util; + +import io.gitlab.jfronny.inceptum.Inceptum; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class HttpUtils { + private static final HttpClient CLIENT = HttpClient.newHttpClient(); + + private enum Method { + GET, + POST + } + + public static class Request { + private HttpRequest.Builder builder; + private Method method; + + public Request(Method method, String url) { + try { + this.builder = HttpRequest.newBuilder().uri(new URI(url)).header("User-Agent", "Meteor Client"); + this.method = method; + } catch (URISyntaxException e) { + Inceptum.LOGGER.error("Could not create request", e); + } + } + + public Request bearer(String token) { + builder.header("Authorization", "Bearer " + token); + + return this; + } + + public Request header(String name, String value) { + builder.header(name, value); + return this; + } + + public Request bodyString(String string) { + builder.header("Content-Type", "text/plain"); + builder.method(method.name(), HttpRequest.BodyPublishers.ofString(string)); + method = null; + + return this; + } + + public Request bodyForm(String string) { + builder.header("Content-Type", "application/x-www-form-urlencoded"); + builder.method(method.name(), HttpRequest.BodyPublishers.ofString(string)); + method = null; + + return this; + } + + public Request bodyForm(Map entries) { + /*StringBuilder content = new StringBuilder(); + for (Map.Entry entry : entries.entrySet()) { + if (content.length() > 0) content.append('&'); + content.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8)) + .append('=') + .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)); + } + return bodyForm(content.toString());*/ + return bodyForm(entries.entrySet() + .stream() + .map(entry -> URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8) + '=' + URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)) + .collect(Collectors.joining("&"))); + } + + public Request bodyJson(String string) { + builder.header("Content-Type", "application/json"); + builder.method(method.name(), HttpRequest.BodyPublishers.ofString(string)); + method = null; + + return this; + } + + public Request bodyJson(Object object) { + builder.header("Content-Type", "application/json"); + builder.method(method.name(), HttpRequest.BodyPublishers.ofString(Inceptum.GSON.toJson(object))); + method = null; + + return this; + } + + private T _send(String accept, HttpResponse.BodyHandler responseBodyHandler) { + builder.header("Accept", accept); + if (method != null) builder.method(method.name(), HttpRequest.BodyPublishers.noBody()); + + try { + var res = CLIENT.send(builder.build(), responseBodyHandler); + if (res.statusCode() == 200) return res.body(); + Inceptum.LOGGER.error("Unexpected return method: " + res.statusCode()); + Inceptum.LOGGER.error(getString(res.body())); + return null; + } catch (IOException | InterruptedException e) { + Inceptum.LOGGER.error("Could not send request", e); + return null; + } + } + + public void send() { + _send("*/*", HttpResponse.BodyHandlers.discarding()); + } + + public InputStream sendInputStream() { + return _send("*/*", HttpResponse.BodyHandlers.ofInputStream()); + } + + public String sendString() { + return _send("*/*", HttpResponse.BodyHandlers.ofString()); + } + + public Stream sendLines() { + return _send("*/*", HttpResponse.BodyHandlers.ofLines()); + } + + public T sendJson(Type type) { + InputStream in = _send("application/json", HttpResponse.BodyHandlers.ofInputStream()); + return in == null ? null : Inceptum.GSON.fromJson(new InputStreamReader(in), type); + } + + private String getString(Object a) throws IOException { + if (a instanceof InputStream s) return new String(s.readAllBytes()); + if (a instanceof String s) return s; + if (a instanceof Stream s) return ((Stream)s).collect(Collectors.joining()); + return ""; + } + } + + public static Request get(String url) { + return new Request(Method.GET, url); + } + + public static Request post(String url) { + return new Request(Method.POST, url); + } +} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/util/MapAppender.java b/src/main/java/io/gitlab/jfronny/inceptum/util/MapAppender.java similarity index 98% rename from src/main/java/io/gitlab/jfronny/glaunch/util/MapAppender.java rename to src/main/java/io/gitlab/jfronny/inceptum/util/MapAppender.java index 9c258d9..a596701 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/util/MapAppender.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/util/MapAppender.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch.util; +package io.gitlab.jfronny.inceptum.util; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Core; diff --git a/src/main/java/io/gitlab/jfronny/glaunch/util/mojang/McApi.java b/src/main/java/io/gitlab/jfronny/inceptum/util/McApi.java similarity index 73% rename from src/main/java/io/gitlab/jfronny/glaunch/util/mojang/McApi.java rename to src/main/java/io/gitlab/jfronny/inceptum/util/McApi.java index eb60184..9a6b4db 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/util/mojang/McApi.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/util/McApi.java @@ -1,17 +1,16 @@ -package io.gitlab.jfronny.glaunch.util.mojang; +package io.gitlab.jfronny.inceptum.util; -import io.gitlab.jfronny.glaunch.GLaunch; -import io.gitlab.jfronny.glaunch.model.AssetIndex; -import io.gitlab.jfronny.glaunch.model.VersionInfo; -import io.gitlab.jfronny.glaunch.model.VersionsList; -import io.gitlab.jfronny.glaunch.model.VersionsListInfo; -import io.gitlab.jfronny.glaunch.util.Utils; +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.model.mojang.AssetIndex; +import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo; +import io.gitlab.jfronny.inceptum.model.mojang.VersionsList; +import io.gitlab.jfronny.inceptum.model.mojang.VersionsListInfo; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import static io.gitlab.jfronny.glaunch.util.Utils.downloadObject; +import static io.gitlab.jfronny.inceptum.util.Utils.downloadObject; public class McApi { public static VersionsList getVersions() { @@ -27,7 +26,7 @@ public class McApi { } public static AssetIndex getAssetIndex(VersionInfo info) throws IOException { - Path file = GLaunch.ASSETS_DIR.resolve("indexes"); + Path file = Inceptum.ASSETS_DIR.resolve("indexes"); if (!Files.exists(file)) Files.createDirectories(file); file = file.resolve(Path.of(info.assetIndex.url).getFileName()); try { diff --git a/src/main/java/io/gitlab/jfronny/glaunch/util/ThrowingSupplier.java b/src/main/java/io/gitlab/jfronny/inceptum/util/ThrowingSupplier.java similarity index 68% rename from src/main/java/io/gitlab/jfronny/glaunch/util/ThrowingSupplier.java rename to src/main/java/io/gitlab/jfronny/inceptum/util/ThrowingSupplier.java index 90e4aa6..66a5aa3 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/util/ThrowingSupplier.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/util/ThrowingSupplier.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch.util; +package io.gitlab.jfronny.inceptum.util; public interface ThrowingSupplier { T get() throws TEx; diff --git a/src/main/java/io/gitlab/jfronny/glaunch/util/Utils.java b/src/main/java/io/gitlab/jfronny/inceptum/util/Utils.java similarity index 68% rename from src/main/java/io/gitlab/jfronny/glaunch/util/Utils.java rename to src/main/java/io/gitlab/jfronny/inceptum/util/Utils.java index 887ec72..456a118 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/util/Utils.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/util/Utils.java @@ -1,9 +1,11 @@ -package io.gitlab.jfronny.glaunch.util; +package io.gitlab.jfronny.inceptum.util; -import io.gitlab.jfronny.glaunch.GLaunch; +import io.gitlab.jfronny.inceptum.Inceptum; +import java.awt.*; import java.io.*; -import java.net.URL; +import java.lang.reflect.Type; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; @@ -41,7 +43,7 @@ public class Utils { } public static byte[] downloadData(String url) throws IOException { - try (InputStream is = new URL(url).openStream()) { + try (InputStream is = HttpUtils.get(url).sendInputStream()) { return is.readAllBytes(); } } @@ -53,7 +55,7 @@ public class Utils { } public static T downloadObject(String url, Class type) throws IOException { - return downloadObject(url, () -> downloadString(url), type); + return downloadObject(url, () -> HttpUtils.get(url).sendString(), type); } public static T downloadObject(String url, String sha1, Class type) throws IOException { @@ -61,16 +63,16 @@ public class Utils { } private static T downloadObject(String url, ThrowingSupplier sourceString, Class type) throws IOException { - Path cache = GLaunch.CACHE_DIR.resolve(Integer.toString(url.hashCode())); + Path cache = Inceptum.CACHE_DIR.resolve(Integer.toString(url.hashCode())); try { String download = sourceString.get(); Files.writeString(cache, download); - return GLaunch.GSON.fromJson(download, type); + return Inceptum.GSON.fromJson(download, type); } catch (IOException e) { if (Files.exists(cache)) { - GLaunch.LOGGER.info("Using cache for " + url, e); + Inceptum.LOGGER.info("Using cache for " + url, e); try (BufferedReader br = Files.newBufferedReader(cache)) { - return GLaunch.GSON.fromJson(br, type); + return Inceptum.GSON.fromJson(br, type); } catch (IOException ioE) { throw new IOException("Could not download object and failed loading cache", ioE); } @@ -81,25 +83,19 @@ public class Utils { public static T loadObject(Path file, Class type) throws IOException { try (BufferedReader br = Files.newBufferedReader(file)) { - return GLaunch.GSON.fromJson(br, type); + return Inceptum.GSON.fromJson(br, type); + } + } + + public static T loadObject(Path file, Type type) throws IOException { + try (BufferedReader br = Files.newBufferedReader(file)) { + return Inceptum.GSON.fromJson(br, type); } } public static void writeObject(Path file, T object) throws IOException { try (BufferedWriter bw = Files.newBufferedWriter(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { - GLaunch.GSON.toJson(object, bw); - } - } - - private static String downloadString(String url) throws IOException { - try (InputStream is = new URL(url).openStream(); - Reader isr = new InputStreamReader(is); - BufferedReader br = new BufferedReader(isr)) { - StringBuilder text = new StringBuilder(); - String line; - while ((line = br.readLine()) != null) - text.append('\n').append(line); - return text.substring(1); + Inceptum.GSON.toJson(object, bw); } } @@ -130,4 +126,28 @@ public class Utils { } }); } + + public static void openWebBrowser(URI uri) { + try { + if (OS.equals("linux") && Utils.executableInPath("xdg-open")) { + Runtime.getRuntime().exec("xdg-open " + uri); + } else if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + Desktop.getDesktop().browse(uri); + } + } catch (Exception e) { + Inceptum.LOGGER.error("Error opening web browser!", e); + } + } + + public static boolean executableInPath(String executableName) { + try { + return java.util.stream.Stream + .of(System.getenv("PATH").split(java.util.regex.Pattern.quote(File.pathSeparator))) + .map(path -> path.replace("\"", "")).map(Paths::get) + .anyMatch(path -> Files.exists(path.resolve(executableName)) + && Files.isExecutable(path.resolve(executableName))); + } catch (Exception e) { + return false; + } + } } diff --git a/src/main/java/io/gitlab/jfronny/glaunch/util/VersionInfoLibraryResolver.java b/src/main/java/io/gitlab/jfronny/inceptum/util/VersionInfoLibraryResolver.java similarity index 79% rename from src/main/java/io/gitlab/jfronny/glaunch/util/VersionInfoLibraryResolver.java rename to src/main/java/io/gitlab/jfronny/inceptum/util/VersionInfoLibraryResolver.java index 9d87033..845944f 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/util/VersionInfoLibraryResolver.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/util/VersionInfoLibraryResolver.java @@ -1,7 +1,7 @@ -package io.gitlab.jfronny.glaunch.util; +package io.gitlab.jfronny.inceptum.util; -import io.gitlab.jfronny.glaunch.GLaunch; -import io.gitlab.jfronny.glaunch.model.VersionInfo; +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo; import java.util.LinkedHashSet; import java.util.Set; @@ -15,7 +15,7 @@ public class VersionInfoLibraryResolver { artifacts.add(library.downloads.classifiers.get(library.natives.get(Utils.OS))); } if (library.downloads.artifact == null) { - GLaunch.LOGGER.info("Null library artifact @ " + library.name); + Inceptum.LOGGER.info("Null library artifact @ " + library.name); continue; } artifacts.add(library.downloads.artifact); diff --git a/src/main/java/io/gitlab/jfronny/inceptum/util/account/AccountManager.java b/src/main/java/io/gitlab/jfronny/inceptum/util/account/AccountManager.java new file mode 100644 index 0000000..3b605a7 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/util/account/AccountManager.java @@ -0,0 +1,106 @@ +package io.gitlab.jfronny.inceptum.util.account; + +import com.google.gson.reflect.TypeToken; +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.util.Utils; +import io.gitlab.jfronny.inceptum.windows.AlertWindow; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +public class AccountManager { + private static final Type abstractAccountListType = new TypeToken>() {}.getType(); + private static MicrosoftAccount SELECTED_ACCOUNT; + private static final List ACCOUNTS = new ArrayList<>(); + + public static AuthInfo getSelectedAccount() { + if (SELECTED_ACCOUNT == null) return new AuthInfo("Joe", "2536abce90e8476a871679918164abc5", "99abe417230342cb8e9e2168ab46297a", "legacy"); + return new AuthInfo(SELECTED_ACCOUNT); + } + + public static List getAccounts() { + return List.copyOf(ACCOUNTS); + } + + public static void saveAccounts() { + try { + Utils.writeObject(Inceptum.ACCOUNTS_PATH, ACCOUNTS); + } catch (IOException e) { + Inceptum.LOGGER.error("Could not save accounts", e); + } + } + + public static void loadAccounts() { + Inceptum.LOGGER.info("Loading accounts"); + if (Files.exists(Inceptum.ACCOUNTS_PATH)) { + try { + ACCOUNTS.addAll(Utils.loadObject(Inceptum.ACCOUNTS_PATH, abstractAccountListType)); + } catch (IOException e) { + Inceptum.LOGGER.error("Could not load accounts", e); + } + } + for (MicrosoftAccount account : ACCOUNTS) { + if (account.accountId.equalsIgnoreCase(Inceptum.CONFIG.lastAccount)) { + SELECTED_ACCOUNT = account; + } + } + if (SELECTED_ACCOUNT == null && ACCOUNTS.size() >= 1) { + SELECTED_ACCOUNT = ACCOUNTS.get(0); + } + Inceptum.LOGGER.info("Finished loading accounts"); + } + + public static void addAccount(MicrosoftAccount account) { + ACCOUNTS.add(account); + if (ACCOUNTS.size() > 1) { + Inceptum.open(new AlertWindow("Account added successfully. Switch to it now?", + () -> switchAccount(account), + () -> {})); + } + else switchAccount(account); + saveAccounts(); + } + + public static void removeAccount(MicrosoftAccount account) { + if (SELECTED_ACCOUNT == account) { + if (ACCOUNTS.size() == 1) + switchAccount(null); + else + switchAccount(ACCOUNTS.get(0)); + } + ACCOUNTS.remove(account); + saveAccounts(); + } + + public static void switchAccount(MicrosoftAccount account) { + if (account == null) { + Inceptum.LOGGER.info("Logging out"); + SELECTED_ACCOUNT = null; + Inceptum.CONFIG.lastAccount = null; + } else { + Inceptum.LOGGER.info("Changed account to " + account); + SELECTED_ACCOUNT = account; + Inceptum.CONFIG.lastAccount = account.accountId; + } + Inceptum.saveConfig(); + } + + public static Object getAccountByName(String username) { + for (MicrosoftAccount account : ACCOUNTS) { + if (account.accountId.equalsIgnoreCase(username)) + return account; + } + return null; + } + + public static boolean isAccountByName(String username) { + for (MicrosoftAccount account : ACCOUNTS) { + if (account.accountId.equalsIgnoreCase(username)) + return true; + } + return false; + } +} diff --git a/src/main/java/io/gitlab/jfronny/inceptum/util/account/AuthInfo.java b/src/main/java/io/gitlab/jfronny/inceptum/util/account/AuthInfo.java new file mode 100644 index 0000000..88b3699 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/util/account/AuthInfo.java @@ -0,0 +1,15 @@ +package io.gitlab.jfronny.inceptum.util.account; + +public record AuthInfo(String name, String uuid, String accessToken, String userType) { + public AuthInfo(MicrosoftAccount account) { + this(account.minecraftUsername, account.uuid, account.accessToken, "msc"); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return userType.equals("legacy"); + if (obj instanceof AuthInfo info) return info.name.equalsIgnoreCase(name); + if (obj instanceof MicrosoftAccount info) return info.minecraftUsername.equalsIgnoreCase(name); + return false; + } +} diff --git a/src/main/java/io/gitlab/jfronny/inceptum/util/account/MicrosoftAccount.java b/src/main/java/io/gitlab/jfronny/inceptum/util/account/MicrosoftAccount.java new file mode 100644 index 0000000..ccc9a2a --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/util/account/MicrosoftAccount.java @@ -0,0 +1,220 @@ +package io.gitlab.jfronny.inceptum.util.account; + +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.model.microsoft.LoginResponse; +import io.gitlab.jfronny.inceptum.model.microsoft.OauthTokenResponse; +import io.gitlab.jfronny.inceptum.model.microsoft.Profile; +import io.gitlab.jfronny.inceptum.model.microsoft.XboxLiveAuthResponse; +import io.gitlab.jfronny.inceptum.windows.AlertWindow; +import io.gitlab.jfronny.inceptum.windows.MicrosoftLoginWindow; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Optional; +import java.util.function.Consumer; + +public class MicrosoftAccount { + /** + * Auto generated serial. + */ + private static final long serialVersionUID = 5483749902584257559L; + + /** + * The username/email/id of the account. + */ + public String accountId; + + /** + * The account's Minecraft username. + */ + public String minecraftUsername; + + /** + * The UUID of the account. + */ + public String uuid; + + /** + * The access token. + */ + public String accessToken; + + /** + * The Microsoft oauth token. + */ + public OauthTokenResponse oauthToken; + + /** + * The xsts auth response. + */ + public XboxLiveAuthResponse xstsAuth; + + /** + * The date that the accessToken expires at. + */ + public Date accessTokenExpiresAt; + + /** + * If the user must login again. This is usually the result of a failed + * accessToken refresh. + */ + public boolean mustLogin; + + public MicrosoftAccount(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse, + LoginResponse loginResponse, Profile profile) { + update(oauthTokenResponse, xstsAuthResponse, loginResponse, profile); + } + + public void update(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse, + LoginResponse loginResponse, Profile profile) { + this.oauthToken = oauthTokenResponse; + this.xstsAuth = xstsAuthResponse; + this.accessToken = loginResponse.accessToken; + this.minecraftUsername = profile.name; + this.uuid = profile.id; + this.accountId = loginResponse.username; + this.mustLogin = false; + + this.accessTokenExpiresAt = new Date(); + this.accessTokenExpiresAt.setTime(this.accessTokenExpiresAt.getTime() + (loginResponse.expiresIn * 1000)); + } + + public String getAccessToken() { + return accessToken; + } + + public String getSessionToken() { + return accessToken; + } + + public String getCurrentUsername() { + Profile profile = MicrosoftAuthAPI.getMcProfile(accessToken); + + if (profile == null) { + Inceptum.LOGGER.error("Error getting Minecraft profile"); + return null; + } + + return Optional.of(profile.name).orElse(null); + } + + public void updateSkinPreCheck() { + this.refreshAccessToken(); + } + + public String getSkinUrl() { + Profile profile = MicrosoftAuthAPI.getMcProfile(accessToken); + + if (profile == null) { + Inceptum.LOGGER.error("Error getting Minecraft profile"); + return null; + } + + return Optional.of(profile.skins).orElse(new ArrayList<>()).stream() + .filter(s -> s.state.equalsIgnoreCase("ACTIVE")).findFirst().map(s -> s.url).orElse(null); + } + + public boolean refreshAccessToken() { + return refreshAccessToken(false); + } + + public boolean refreshAccessToken(boolean force) { + try { + if (force || new Date().after(this.oauthToken.expiresAt)) { + Inceptum.LOGGER.info("Oauth token expired. Attempting to refresh"); + OauthTokenResponse oauthTokenResponse = MicrosoftAuthAPI.refreshAccessToken(oauthToken.refreshToken); + + if (oauthTokenResponse == null) { + mustLogin = true; + AccountManager.saveAccounts(); + Inceptum.LOGGER.error("Failed to refresh accessToken"); + return false; + } + + this.oauthToken = oauthTokenResponse; + + AccountManager.saveAccounts(); + } + + if (force || new Date().after(this.xstsAuth.notAfter)) { + Inceptum.LOGGER.info("xsts auth expired. Attempting to get new auth"); + XboxLiveAuthResponse xboxLiveAuthResponse = MicrosoftAuthAPI.getXBLToken(this.oauthToken.accessToken); + this.xstsAuth = MicrosoftAuthAPI.getXstsToken(xboxLiveAuthResponse.token); + + if (xstsAuth == null) { + mustLogin = true; + AccountManager.saveAccounts(); + Inceptum.LOGGER.error("Failed to get XBLToken"); + return false; + } + + AccountManager.saveAccounts(); + } + + if (force || new Date().after(this.accessTokenExpiresAt)) { + LoginResponse loginResponse = MicrosoftAuthAPI.loginToMinecraft(this.getIdentityToken()); + + if (loginResponse == null) { + mustLogin = true; + AccountManager.saveAccounts(); + Inceptum.LOGGER.error("Failed to login to Minecraft"); + return false; + } + + this.accessToken = loginResponse.accessToken; + this.accountId = loginResponse.username; + + this.accessTokenExpiresAt = new Date(); + this.accessTokenExpiresAt + .setTime(this.accessTokenExpiresAt.getTime() + (loginResponse.expiresIn * 1000)); + + AccountManager.saveAccounts(); + } + } catch (Exception e) { + mustLogin = true; + AccountManager.saveAccounts(); + + Inceptum.LOGGER.error("Exception refreshing accessToken", e); + return false; + } + + return true; + } + + private String getIdentityToken() { + return "XBL3.0 x=" + xstsAuth.displayClaims.xui.get(0).uhs + ";" + xstsAuth.token; + } + + public void ensureAccessTokenValid(Consumer isValid) { + boolean hasCancelled = false; + if (mustLogin) { + Inceptum.open(new AlertWindow("You must login again in order to continue", () -> { + Inceptum.open(new MicrosoftLoginWindow(this)); + + + if (!new Date().after(accessTokenExpiresAt)) { + isValid.accept(true); + return; + } + + Inceptum.LOGGER.info("Access Token has expired. Attempting to refresh it."); + + try { + isValid.accept(refreshAccessToken()); + return; + } catch (Exception e) { + Inceptum.LOGGER.error("Exception while attempting to refresh access token", e); + } + + isValid.accept(false); + }, () -> { + isValid.accept(false); + })); + } + } + + @Override + public String toString() { + return minecraftUsername; + } +} diff --git a/src/main/java/io/gitlab/jfronny/inceptum/util/account/MicrosoftAuthAPI.java b/src/main/java/io/gitlab/jfronny/inceptum/util/account/MicrosoftAuthAPI.java new file mode 100644 index 0000000..1f48bd8 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/util/account/MicrosoftAuthAPI.java @@ -0,0 +1,102 @@ +package io.gitlab.jfronny.inceptum.util.account; + +import io.gitlab.jfronny.inceptum.model.microsoft.*; +import io.gitlab.jfronny.inceptum.util.HttpUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MicrosoftAuthAPI { + public static final String MICROSOFT_LOGIN_CLIENT_ID = "90890812-00d1-48a8-8d3f-38465ef43b58"; + public static final int MICROSOFT_LOGIN_REDIRECT_PORT = 28562; + public static final String MICROSOFT_LOGIN_REDIRECT_URL = "http://127.0.0.1:" + MICROSOFT_LOGIN_REDIRECT_PORT; + public static final String MICROSOFT_LOGIN_REDIRECT_URL_ENCODED = "http%3A%2F%2F127.0.0.1%3A" + + MICROSOFT_LOGIN_REDIRECT_PORT; + public static final String[] MICROSOFT_LOGIN_SCOPES = { "XboxLive.signin", "XboxLive.offline_access" }; + + // General Microsoft login constants + public static final String MICROSOFT_LOGIN_URL = "https://login.live.com/oauth20_authorize.srf" + "?client_id=" + + MICROSOFT_LOGIN_CLIENT_ID + + "&prompt=select_account&cobrandid=8058f65d-ce06-4c30-9559-473c9275a65d&response_type=code" + "&scope=" + + String.join("%20", MICROSOFT_LOGIN_SCOPES) + "&redirect_uri=" + MICROSOFT_LOGIN_REDIRECT_URL_ENCODED; + public static final String MICROSOFT_AUTH_TOKEN_URL = "https://login.live.com/oauth20_token.srf"; + public static final String MICROSOFT_XBL_AUTH_TOKEN_URL = "https://user.auth.xboxlive.com/user/authenticate"; + public static final String MICROSOFT_XSTS_AUTH_TOKEN_URL = "https://xsts.auth.xboxlive.com/xsts/authorize"; + public static final String MICROSOFT_MINECRAFT_LOGIN_URL = "https://api.minecraftservices.com/authentication/login_with_xbox"; + public static final String MICROSOFT_MINECRAFT_STORE_URL = "https://api.minecraftservices.com/entitlements/mcstore"; + public static final String MICROSOFT_MINECRAFT_PROFILE_URL = "https://api.minecraftservices.com/minecraft/profile"; + + public static OauthTokenResponse tradeCodeForAccessToken(String code) { + return HttpUtils.post(MICROSOFT_AUTH_TOKEN_URL) + .bodyForm(Map.of("client_id", MICROSOFT_LOGIN_CLIENT_ID, + "code", code, + "grant_type", "authorization_code", + "redirect_uri", MICROSOFT_LOGIN_REDIRECT_URL, + "scope", String.join(" ", MICROSOFT_LOGIN_SCOPES))) + .sendJson(OauthTokenResponse.class); + } + + public static OauthTokenResponse refreshAccessToken(String refreshToken) { + return HttpUtils.post(MICROSOFT_AUTH_TOKEN_URL) + .bodyForm(Map.of("client_id", MICROSOFT_LOGIN_CLIENT_ID, + "refresh_token", refreshToken, + "grant_type", "refresh_token", + "redirect_uri", MICROSOFT_LOGIN_REDIRECT_URL)) + .sendJson(OauthTokenResponse.class); + } + + public static XboxLiveAuthResponse getXBLToken(String accessToken) { + Map properties = new HashMap<>(); + properties.put("AuthMethod", "RPS"); + properties.put("SiteName", "user.auth.xboxlive.com"); + properties.put("RpsTicket", "d=" + accessToken); + + Map data = new HashMap<>(); + data.put("Properties", properties); + data.put("RelyingParty", "http://auth.xboxlive.com"); + data.put("TokenType", "JWT"); + + return HttpUtils.post(MICROSOFT_XBL_AUTH_TOKEN_URL) + .header("x-xbl-contract-version", "1") + .bodyJson(data) + .sendJson(XboxLiveAuthResponse.class); + } + + public static XboxLiveAuthResponse getXstsToken(String xblToken) { + Map properties = new HashMap<>(); + properties.put("SandboxId", "RETAIL"); + + List userTokens = new ArrayList<>(); + userTokens.add(xblToken); + properties.put("UserTokens", userTokens); + + Map data = new HashMap<>(); + data.put("Properties", properties); + data.put("RelyingParty", "rp://api.minecraftservices.com/"); + data.put("TokenType", "JWT"); + + return HttpUtils.post(MICROSOFT_XSTS_AUTH_TOKEN_URL) + .header("x-xbl-contract-version", "1") + .bodyJson(data) + .sendJson(XboxLiveAuthResponse.class); + } + + public static LoginResponse loginToMinecraft(String xstsToken) { + Map data = new HashMap(); + data.put("identityToken", xstsToken); + + return HttpUtils.post(MICROSOFT_MINECRAFT_LOGIN_URL) + .bodyJson(data) + .sendJson(LoginResponse.class); + } + + public static Store getMcEntitlements(String accessToken) { + return HttpUtils.get(MICROSOFT_MINECRAFT_STORE_URL).bearer(accessToken).sendJson(Store.class); + } + + public static Profile getMcProfile(String accessToken) { + return HttpUtils.get(MICROSOFT_MINECRAFT_PROFILE_URL).bearer(accessToken).sendJson(Profile.class); + } +} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/windows/AboutWindow.java b/src/main/java/io/gitlab/jfronny/inceptum/windows/AboutWindow.java similarity index 76% rename from src/main/java/io/gitlab/jfronny/glaunch/windows/AboutWindow.java rename to src/main/java/io/gitlab/jfronny/inceptum/windows/AboutWindow.java index 8c6cefa..a9f9060 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/windows/AboutWindow.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/windows/AboutWindow.java @@ -1,4 +1,4 @@ -package io.gitlab.jfronny.glaunch.windows; +package io.gitlab.jfronny.inceptum.windows; import imgui.ImGui; @@ -9,7 +9,7 @@ public class AboutWindow extends Window { @Override public void draw() { - ImGui.text("GLaunch Copyright (C) 2021 JFronny"); + ImGui.text("Inceptum Copyright (C) 2021 JFronny"); ImGui.text("This program comes with ABSOLUTELY NO WARRANTY."); ImGui.text("This is free software, and you are welcome to redistribute it under certain conditions."); } diff --git a/src/main/java/io/gitlab/jfronny/inceptum/windows/AlertWindow.java b/src/main/java/io/gitlab/jfronny/inceptum/windows/AlertWindow.java new file mode 100644 index 0000000..a5a2b7f --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/windows/AlertWindow.java @@ -0,0 +1,36 @@ +package io.gitlab.jfronny.inceptum.windows; + +import imgui.ImGui; + +public class AlertWindow extends Window { + private final String message; + private final Runnable onOk; + private final Runnable onCancel; + + public AlertWindow(String message) { + this(message, null, null); + } + + public AlertWindow(String message, Runnable onOk, Runnable onCancel) { + super("Warning"); + this.message = message; + this.onOk = onOk; + this.onCancel = onCancel; + } + + @Override + public void draw() { + ImGui.text(message); + if (ImGui.button("OK")) { + close(); + if (onOk != null) onOk.run(); + } + if (onOk != null || onCancel != null) { + ImGui.sameLine(); + if (ImGui.button("Cancel")) { + close(); + if (onCancel != null) onCancel.run(); + } + } + } +} diff --git a/src/main/java/io/gitlab/jfronny/inceptum/windows/LogWindow.java b/src/main/java/io/gitlab/jfronny/inceptum/windows/LogWindow.java new file mode 100644 index 0000000..c293ed0 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/windows/LogWindow.java @@ -0,0 +1,27 @@ +package io.gitlab.jfronny.inceptum.windows; + +import imgui.ImGui; +import io.gitlab.jfronny.inceptum.util.MapAppender; + +import java.util.Set; + +public class LogWindow extends Window { + private final Set source; + + public LogWindow(Set source) { + super("Log"); + this.source = source; + } + + @Override + public void draw() { + source.forEach(ImGui::textUnformatted); + if (ImGui.getScrollY() >= ImGui.getScrollMaxY()) + ImGui.setScrollHereY(1.0f); + } + + @Override + public int getFlags() { + return 0; + } +} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/windows/MainWindow.java b/src/main/java/io/gitlab/jfronny/inceptum/windows/MainWindow.java similarity index 58% rename from src/main/java/io/gitlab/jfronny/glaunch/windows/MainWindow.java rename to src/main/java/io/gitlab/jfronny/inceptum/windows/MainWindow.java index ab0449c..7325bd9 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/windows/MainWindow.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/windows/MainWindow.java @@ -1,17 +1,22 @@ -package io.gitlab.jfronny.glaunch.windows; +package io.gitlab.jfronny.inceptum.windows; import imgui.ImGui; import imgui.flag.ImGuiTableFlags; import imgui.flag.ImGuiWindowFlags; import imgui.type.ImBoolean; -import io.gitlab.jfronny.glaunch.GLaunch; -import io.gitlab.jfronny.glaunch.model.InstanceMeta; -import io.gitlab.jfronny.glaunch.model.MinecraftArgument; -import io.gitlab.jfronny.glaunch.model.VersionInfo; -import io.gitlab.jfronny.glaunch.model.VersionsListInfo; -import io.gitlab.jfronny.glaunch.util.Utils; -import io.gitlab.jfronny.glaunch.util.VersionInfoLibraryResolver; -import io.gitlab.jfronny.glaunch.util.mojang.McApi; +import imgui.type.ImInt; +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.model.InstanceMeta; +import io.gitlab.jfronny.inceptum.model.mojang.MinecraftArgument; +import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo; +import io.gitlab.jfronny.inceptum.model.mojang.VersionsListInfo; +import io.gitlab.jfronny.inceptum.util.MapAppender; +import io.gitlab.jfronny.inceptum.util.Utils; +import io.gitlab.jfronny.inceptum.util.VersionInfoLibraryResolver; +import io.gitlab.jfronny.inceptum.util.McApi; +import io.gitlab.jfronny.inceptum.util.account.AccountManager; +import io.gitlab.jfronny.inceptum.util.account.AuthInfo; +import io.gitlab.jfronny.inceptum.util.account.MicrosoftAccount; import java.io.IOException; import java.nio.file.Files; @@ -20,24 +25,25 @@ import java.util.ArrayList; import java.util.List; public class MainWindow extends Window { - private final ImBoolean darkTheme = new ImBoolean(GLaunch.CONFIG.darkTheme); + private final ImBoolean darkTheme = new ImBoolean(Inceptum.CONFIG.darkTheme); private final ImBoolean debugTools = new ImBoolean(false); private final List instances; //TODO custom ordering + private final ImInt accountIndex = new ImInt(0); public MainWindow() { - super("GLaunch"); + super("Inceptum"); List instances = new ArrayList<>(); try { - for (Path path : Files.list(GLaunch.INSTANCE_DIR).filter(Files::isDirectory).toList()) { + for (Path path : Files.list(Inceptum.INSTANCE_DIR).filter(Files::isDirectory).toList()) { if (Files.exists(path.resolve("instance.json"))) { InstanceMeta im = Utils.loadObject(path.resolve("instance.json"), InstanceMeta.class); im.instancePath = path; instances.add(im); } else { - GLaunch.LOGGER.error("Invalid instance (doesn't contain instance.json): " + path); + Inceptum.LOGGER.error("Invalid instance (doesn't contain instance.json): " + path); } } } catch (IOException e) { - GLaunch.LOGGER.error("Could not list present instances", e); + Inceptum.LOGGER.error("Could not list present instances", e); } this.instances = List.copyOf(instances); } @@ -51,21 +57,36 @@ public class MainWindow extends Window { public void draw() { ImGui.beginMenuBar(); if (ImGui.beginMenu("File")) { - if (ImGui.menuItem("New Instance")) GLaunch.open(new NewInstanceWindow()); - if (ImGui.menuItem("Exit GLaunch2")) GLaunch.exit(); + if (ImGui.menuItem("New Instance")) Inceptum.open(new NewInstanceWindow()); + if (ImGui.menuItem("Exit Inceptum")) Inceptum.exit(); + ImGui.endMenu(); + } + if (ImGui.beginMenu("Account")) { + if (ImGui.menuItem("New")) Inceptum.open(new MicrosoftLoginWindow()); + AuthInfo selected = AccountManager.getSelectedAccount(); + List accounts = AccountManager.getAccounts(); + for (int i = 0, accountsSize = accounts.size(); i < accountsSize; i++) { + MicrosoftAccount account = accounts.get(i); + if (selected.equals(account)) accountIndex.set(i); + if (ImGui.radioButton(account.minecraftUsername, accountIndex, i)) { + AccountManager.switchAccount(account); + } + ImGui.sameLine(); + if (ImGui.button("X")) AccountManager.removeAccount(account); + } ImGui.endMenu(); } if (ImGui.beginMenu("Settings")) { if (ImGui.checkbox("Dark Theme", darkTheme)) { - GLaunch.CONFIG.darkTheme = darkTheme.get(); - GLaunch.saveConfig(); - GLaunch.applyTheme(); + Inceptum.CONFIG.darkTheme = darkTheme.get(); + Inceptum.saveConfig(); + Inceptum.applyTheme(); } ImGui.endMenu(); } if (ImGui.beginMenu("Help")) { - if (ImGui.menuItem("About")) GLaunch.open(new AboutWindow()); - if (ImGui.menuItem("Log")) GLaunch.open(new LogWindow()); + if (ImGui.menuItem("About")) Inceptum.open(new AboutWindow()); + if (ImGui.menuItem("Log")) Inceptum.open(new LogWindow(MapAppender.LOG)); ImGui.checkbox("Debug Tools", debugTools); ImGui.endMenu(); } @@ -94,16 +115,15 @@ public class MainWindow extends Window { VersionInfo info = McApi.getVersionInfo(version); StringBuilder classPath = new StringBuilder(); for (VersionInfo.Library.Downloads.Artifact artifact : VersionInfoLibraryResolver.getRelevant(info)) { - classPath.append(GLaunch.LIBRARIES_DIR.resolve(artifact.path).toAbsolutePath()); + classPath.append(Inceptum.LIBRARIES_DIR.resolve(artifact.path).toAbsolutePath()); classPath.append(':'); } - classPath.append(GLaunch.LIBRARIES_DIR.resolve("net/minecraft/minecraft").resolve(version.id + ".jar").toAbsolutePath()); - GLaunch.LOGGER.info(classPath.toString()); + classPath.append(Inceptum.LIBRARIES_DIR.resolve("net/minecraft/minecraft").resolve(version.id + ".jar").toAbsolutePath()); + Inceptum.LOGGER.info(classPath.toString()); //TODO -Xms{lowMem} -Xmx{maxMem} args.addAll(parse(info.arguments.jvm, info, instance, classPath.toString())); args.add(info.mainClass); args.addAll(parse(info.arguments.game, info, instance, classPath.toString())); - //Process p = Runtime.getRuntime().exec(args.toArray(new String[0]), new String[0], instance.instancePath.toFile()); ProcessBuilder pb = new ProcessBuilder(args.toArray(new String[0])); pb.directory(instance.instancePath.toFile()); pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); @@ -117,7 +137,7 @@ public class MainWindow extends Window { } ImGui.tableNextColumn(); if (ImGui.button("Edit")) { - GLaunch.LOGGER.error("Editing not yet implemented"); //TODO allow changing name (moving), changing version, managing mods etc + Inceptum.LOGGER.error("Editing not yet implemented"); //TODO allow changing name (moving), changing version, managing mods etc } } ImGui.endTable(); @@ -130,23 +150,24 @@ public class MainWindow extends Window { for (MinecraftArgument argument : arguments) { for (String s : argument.arg()) { //TODO support old versions + AuthInfo authInfo = AccountManager.getSelectedAccount(); res.add(s // game args - .replace("${auth_player_name}", "Joe") //TODO auth support + .replace("${auth_player_name}", authInfo.name()) .replace("${version_name}", info.id) //TODO fabric support .replace("${game_directory}", instance.instancePath.toAbsolutePath().toString()) - .replace("${assets_root}", GLaunch.ASSETS_DIR.toAbsolutePath().toString()) + .replace("${assets_root}", Inceptum.ASSETS_DIR.toAbsolutePath().toString()) .replace("${assets_index_name}", info.assets) - .replace("${auth_uuid}", "2536abce90e8476a871679918164abc5") //TODO auth support - .replace("${auth_access_token}", "99abe417230342cb8e9e2168ab46297a") //TODO auth support - .replace("${user_type}", "legacy") //TODO auth support + .replace("${auth_uuid}", authInfo.uuid()) + .replace("${auth_access_token}", authInfo.accessToken()) + .replace("${user_type}", authInfo.userType()) .replace("${version_type}", info.type) .replace("${resolution_width}", "1920") //TODO has_custom_resolution .replace("${resolution_height}", "1080") //TODO has_custom_resolution // jvm args - .replace("${natives_directory}", GLaunch.INSTANCE_DIR.toAbsolutePath().toString()) - .replace("${launcher_name}", "GLaunch") - .replace("${launcher_version}", "1.0") //TODO automatically fill in + .replace("${natives_directory}", Inceptum.INSTANCE_DIR.toAbsolutePath().toString()) + .replace("${launcher_name}", "Inceptum") + .replace("${launcher_version}", Inceptum.VERSION.version) //TODO automatically fill in .replace("${classpath}", classPath) ); } diff --git a/src/main/java/io/gitlab/jfronny/inceptum/windows/MicrosoftLoginWindow.java b/src/main/java/io/gitlab/jfronny/inceptum/windows/MicrosoftLoginWindow.java new file mode 100644 index 0000000..60b086e --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/inceptum/windows/MicrosoftLoginWindow.java @@ -0,0 +1,189 @@ +package io.gitlab.jfronny.inceptum.windows; + +import imgui.ImGui; +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.model.microsoft.*; +import io.gitlab.jfronny.inceptum.util.account.AccountManager; +import io.gitlab.jfronny.inceptum.util.account.MicrosoftAccount; +import io.gitlab.jfronny.inceptum.util.account.MicrosoftAuthAPI; +import io.gitlab.jfronny.inceptum.util.Utils; +import net.freeutils.httpserver.HTTPServer; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; + +public class MicrosoftLoginWindow extends Window { + private static HTTPServer server = new HTTPServer(28562); + private static HTTPServer.VirtualHost host = server.getVirtualHost(null); + private static final String MICROSOFT_LOGIN_URL = "https://login.live.com/oauth20_authorize.srf?client_id=90890812-00d1-48a8-8d3f-38465ef43b58&prompt=select_account&cobrandid=8058f65d-ce06-4c30-9559-473c9275a65d&response_type=code&scope=XboxLive.signin%20XboxLive.offline_access&redirect_uri=http%3A%2F%2F127.0.0.1%3A28562"; + + private MicrosoftAccount account; + + public MicrosoftLoginWindow() { + this(null); + } + + public MicrosoftLoginWindow(MicrosoftAccount account) { + super("Microsoft Login"); + + this.account = account; + try { + startServer(); + } catch (Exception e) { + Inceptum.LOGGER.error("Could not start mc login server", e); + } + } + + @Override + public void draw() { + ImGui.text("This feature uses modified ATLauncher code"); + ImGui.text("Click the button below to begin"); + if (ImGui.button("Open in Browser")) { + try { + Utils.openWebBrowser(new URI(MICROSOFT_LOGIN_URL)); + } catch (URISyntaxException e) { + Inceptum.LOGGER.error("Could not open browser", e); + } + } + } + + private void startServer() throws Exception { + host.addContext("/", (req, res) -> { + if (req.getParams().containsKey("error")) { + res.getHeaders().add("Content-Type", "text/plain"); + res.send(500, "Error logging in. Check console for more information"); + Inceptum.LOGGER.error("Error logging into Microsoft account: " + URLDecoder + .decode(req.getParams().get("error_description"), StandardCharsets.UTF_8.toString())); + close(); + return 0; + } + + if (!req.getParams().containsKey("code")) { + res.getHeaders().add("Content-Type", "text/plain"); + res.send(400, "Code is missing"); + close(); + return 0; + } + + try { + acquireAccessToken(req.getParams().get("code")); + } catch (Exception e) { + Inceptum.LOGGER.error("Error acquiring accessToken", e); + res.getHeaders().add("Content-Type", "text/html"); + res.send(500, "Error logging in. Check console for more information"); + close(); + return 0; + } + + res.getHeaders().add("Content-Type", "text/plain"); + // #. {0} is the name of the launcher + res.send(200, "Login complete. You can now close this window and go back to GLaunch"); + close(); + return 0; + }, "GET"); + + server.start(); + } + + private void acquireAccessToken(String authcode) throws Exception { + OauthTokenResponse oauthTokenResponse = MicrosoftAuthAPI.tradeCodeForAccessToken(authcode); + + acquireXBLToken(oauthTokenResponse); + } + + private void acquireXBLToken(OauthTokenResponse oauthTokenResponse) throws Exception { + XboxLiveAuthResponse xblAuthResponse = MicrosoftAuthAPI.getXBLToken(oauthTokenResponse.accessToken); + + acquireXsts(oauthTokenResponse, xblAuthResponse.token); + } + + private void acquireXsts(OauthTokenResponse oauthTokenResponse, String xblToken) throws Exception { + XboxLiveAuthResponse xstsAuthResponse = null; + + xstsAuthResponse = MicrosoftAuthAPI.getXstsToken(xblToken); + /*try { + xstsAuthResponse = MicrosoftAuthAPI.getXstsToken(xblToken); + } catch (DownloadException e) { + if (e.response != null) { + GLaunch.LOGGER.debug(Gsons.DEFAULT.toJson(e.response)); + XboxLiveAuthErrorResponse xboxLiveAuthErrorResponse = Gsons.DEFAULT.fromJson(e.response, + XboxLiveAuthErrorResponse.class); + + String error = xboxLiveAuthErrorResponse.getErrorMessageForCode(); + + if (error != null) { + GLaunch.LOGGER.warn(error); + DialogManager.okDialog().setTitle("Error logging into Xbox Live") + .setContent(new HTMLBuilder().center().text(error).build()).setType(DialogManager.ERROR) + .show(); + + String link = xboxLiveAuthErrorResponse.getBrowserLinkForCode(); + + if (link != null) { + Utils.openWebBrowser(new URI(link)); + } + } + + throw e; + } + }*/ + + if (xstsAuthResponse != null) { + acquireMinecraftToken(oauthTokenResponse, xstsAuthResponse); + } + } + + private void acquireMinecraftToken(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse) throws Exception { + String xblUhs = xstsAuthResponse.displayClaims.xui.get(0).uhs; + String xblXsts = xstsAuthResponse.token; + + LoginResponse loginResponse = MicrosoftAuthAPI.loginToMinecraft("XBL3.0 x=" + xblUhs + ";" + xblXsts); + + Store store = MicrosoftAuthAPI.getMcEntitlements(loginResponse.accessToken); + + Inceptum.LOGGER.info(Inceptum.GSON.toJson(store)); + + if (!(store.items.stream().anyMatch(i -> i.name.equalsIgnoreCase("product_minecraft")) + && store.items.stream().anyMatch(i -> i.name.equalsIgnoreCase("game_minecraft")))) { + Inceptum.open(new AlertWindow("This account doesn't have a valid purchase of Minecraft.\nPlease make sure you've bought the Java edition of Minecraft and then try again.")); + throw new Exception("Account does not own Minecraft"); + } + + Profile profile = MicrosoftAuthAPI.getMcProfile(loginResponse.accessToken); + + if (profile == null) { + throw new Exception("Failed to get Minecraft profile"); + } + + // add the account + + addAccount(oauthTokenResponse, xstsAuthResponse, loginResponse, profile); + } + + private void addAccount(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse, + LoginResponse loginResponse, Profile profile) throws Exception { + if (account != null || AccountManager.isAccountByName(loginResponse.username)) { + MicrosoftAccount account = (MicrosoftAccount) AccountManager.getAccountByName(loginResponse.username); + + if (account == null) { + return; + } + + // if forced to relogin, then make sure they logged into correct account + if (account != null && this.account != null && account.accountId != this.account.accountId) { + Inceptum.open(new AlertWindow("Logged into incorrect account. Please login again on the Accounts tab")); + return; + } + + account.update(oauthTokenResponse, xstsAuthResponse, loginResponse, profile); + AccountManager.saveAccounts(); + } else { + MicrosoftAccount account = new MicrosoftAccount(oauthTokenResponse, xstsAuthResponse, loginResponse, + profile); + + AccountManager.addAccount(account); + } + } +} diff --git a/src/main/java/io/gitlab/jfronny/glaunch/windows/NewInstanceWindow.java b/src/main/java/io/gitlab/jfronny/inceptum/windows/NewInstanceWindow.java similarity index 68% rename from src/main/java/io/gitlab/jfronny/glaunch/windows/NewInstanceWindow.java rename to src/main/java/io/gitlab/jfronny/inceptum/windows/NewInstanceWindow.java index 5fda0a4..611f67e 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/windows/NewInstanceWindow.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/windows/NewInstanceWindow.java @@ -1,17 +1,17 @@ -package io.gitlab.jfronny.glaunch.windows; +package io.gitlab.jfronny.inceptum.windows; import imgui.ImGui; import imgui.type.ImBoolean; import imgui.type.ImInt; import imgui.type.ImString; -import io.gitlab.jfronny.glaunch.GLaunch; -import io.gitlab.jfronny.glaunch.install.SetupStepInfo; -import io.gitlab.jfronny.glaunch.install.Step; -import io.gitlab.jfronny.glaunch.install.Steps; -import io.gitlab.jfronny.glaunch.model.VersionsList; -import io.gitlab.jfronny.glaunch.model.VersionsListInfo; -import io.gitlab.jfronny.glaunch.util.Utils; -import io.gitlab.jfronny.glaunch.util.mojang.McApi; +import io.gitlab.jfronny.inceptum.Inceptum; +import io.gitlab.jfronny.inceptum.install.SetupStepInfo; +import io.gitlab.jfronny.inceptum.install.Step; +import io.gitlab.jfronny.inceptum.install.Steps; +import io.gitlab.jfronny.inceptum.model.mojang.VersionsList; +import io.gitlab.jfronny.inceptum.model.mojang.VersionsListInfo; +import io.gitlab.jfronny.inceptum.util.Utils; +import io.gitlab.jfronny.inceptum.util.McApi; import java.io.IOException; import java.nio.file.Files; @@ -24,12 +24,12 @@ public class NewInstanceWindow extends Window { State state = State.Configure; ImInt version = new ImInt(0); //TODO select latest stable ImString name = new ImString("", 128); - ImBoolean snapshots = new ImBoolean(GLaunch.CONFIG.snapshots); + ImBoolean snapshots = new ImBoolean(Inceptum.CONFIG.snapshots); ImBoolean fabric = new ImBoolean(true); public NewInstanceWindow() { super("New Instance"); selected = getVersions(false).get(0); - if (GLaunch.CONFIG.snapshots) + if (Inceptum.CONFIG.snapshots) version.set(manifest.versions.indexOf(selected)); name.set(getDefaultName(selected, fabric.get())); } @@ -39,15 +39,15 @@ public class NewInstanceWindow extends Window { switch (state) { case Configure -> { if (ImGui.checkbox("Show snapshots", snapshots)) { - boolean prev = GLaunch.CONFIG.snapshots; - GLaunch.CONFIG.snapshots = snapshots.get(); - GLaunch.saveConfig(); + boolean prev = Inceptum.CONFIG.snapshots; + Inceptum.CONFIG.snapshots = snapshots.get(); + Inceptum.saveConfig(); //fix version index - int i = getVersions(GLaunch.CONFIG.snapshots).indexOf(getVersions(prev).get(version.get())); + int i = getVersions(Inceptum.CONFIG.snapshots).indexOf(getVersions(prev).get(version.get())); if (i == -1) version.set(0); else version.set(i); } - List vil = getVersions(GLaunch.CONFIG.snapshots); + List vil = getVersions(Inceptum.CONFIG.snapshots); if (ImGui.combo("Version", version, vil.stream().map(info -> info.id).toArray(String[]::new))) { VersionsListInfo prev = selected; selected = vil.get(version.get()); @@ -63,13 +63,13 @@ public class NewInstanceWindow extends Window { ImGui.inputTextWithHint("Name", "Select a name", name); if (!Utils.VALID_FILENAME.matcher(name.get()).matches()) ImGui.text("Invalid name"); - else if (Files.exists(GLaunch.INSTANCE_DIR.resolve(name.get()))) + else if (Files.exists(Inceptum.INSTANCE_DIR.resolve(name.get()))) ImGui.text("Already exists"); else if (ImGui.button("OK")) state = State.Installing; } case Installing -> { - if (Files.exists(GLaunch.INSTANCE_DIR.resolve(name.get()))) { + if (Files.exists(Inceptum.INSTANCE_DIR.resolve(name.get()))) { ImGui.text("Already exists"); } ImGui.text("Installing..."); //TODO update UI during process @@ -78,16 +78,16 @@ public class NewInstanceWindow extends Window { for (Step step : Steps.STEPS) { step.execute(stepInfo); } - GLaunch.open(new AlertWindow("Successfully installed!")); + Inceptum.open(new AlertWindow("Successfully installed!")); close(); } catch (Throwable e) { - GLaunch.LOGGER.error("Could not initialize instance", e); - GLaunch.open(new AlertWindow("Could not initialize instance, look at the log for details")); + Inceptum.LOGGER.error("Could not initialize instance", e); + Inceptum.open(new AlertWindow("Could not initialize instance, look at the log for details")); close(); try { - Utils.deleteRecursive(GLaunch.INSTANCE_DIR.resolve(name.get())); + Utils.deleteRecursive(Inceptum.INSTANCE_DIR.resolve(name.get())); } catch (IOException ex) { - GLaunch.LOGGER.error("Could not delete instance dir", e); + Inceptum.LOGGER.error("Could not delete instance dir", e); } } } diff --git a/src/main/java/io/gitlab/jfronny/glaunch/windows/Window.java b/src/main/java/io/gitlab/jfronny/inceptum/windows/Window.java similarity index 85% rename from src/main/java/io/gitlab/jfronny/glaunch/windows/Window.java rename to src/main/java/io/gitlab/jfronny/inceptum/windows/Window.java index 0cdbd1d..b3a1ce2 100644 --- a/src/main/java/io/gitlab/jfronny/glaunch/windows/Window.java +++ b/src/main/java/io/gitlab/jfronny/inceptum/windows/Window.java @@ -1,8 +1,8 @@ -package io.gitlab.jfronny.glaunch.windows; +package io.gitlab.jfronny.inceptum.windows; import imgui.flag.ImGuiWindowFlags; import imgui.type.ImBoolean; -import io.gitlab.jfronny.glaunch.GLaunch; +import io.gitlab.jfronny.inceptum.Inceptum; public abstract class Window { private final String name; @@ -23,7 +23,7 @@ public abstract class Window { public void close() { openState.set(false); - GLaunch.WINDOWS.remove(this); + Inceptum.WINDOWS.remove(this); } public boolean isNew() { diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 4a843d8..2354f8f 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -1,5 +1,5 @@ - + diff --git a/src/main/resources/version.json b/src/main/resources/version.json new file mode 100644 index 0000000..3e640ab --- /dev/null +++ b/src/main/resources/version.json @@ -0,0 +1,3 @@ +{ + "version": "${version}" +} \ No newline at end of file