A lot of changes
This commit is contained in:
parent
5af42284ad
commit
e2178809ab
2
LICENSE
2
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
|
||||
|
|
|
@ -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
|
11
build.gradle
11
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
|
||||
|
|
|
@ -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<string>("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<JToken> 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<string>("os") == "windows" && s.Value<string>("architecture") == (x64 ? "x64" : "x32") &&
|
||||
s.Value<string>("binary_type") == "jre" && s.Value<string>("heap_size") == "normal")
|
||||
.Value<string>("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<string>("sha1"),
|
||||
lib2.First.Value<string>("url"), lib2.First.Value<string>("path"),
|
||||
libdir));
|
||||
Console.CursorLeft = 0;
|
||||
Console.CursorTop--;
|
||||
Console.WriteLine($"[{i + 1}/{tmp.Length}] Getting {lib["downloads"]["artifact"].Value<string>("path")}{new string(' ', 10)}");
|
||||
output += DownloadLib(client,
|
||||
lib["downloads"]["artifact"].Value<string>("sha1"),
|
||||
lib["downloads"]["artifact"].Value<string>("url"),
|
||||
lib["downloads"]["artifact"].Value<string>("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<string>("url"));
|
||||
success = Tools.Hash(indexB) == assetIndex.Value<string>("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<string>("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<string>("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<string>("url"));
|
||||
success = Tools.Hash(mcClient) == clientJson.Value<string>("sha1");
|
||||
}
|
||||
File.WriteAllBytes(Path.Combine("Game", "client.jar"), mcClient);
|
||||
}
|
||||
|
||||
private static JObject GetMinecraftJson(WebClient client, string verdat, out string version)
|
||||
{
|
||||
Tuple<string, string> tmp;
|
||||
Tuple<string, string>[] 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<string, string>[] 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<string>("type") == "release").Select(token => new Tuple<string, string>(token.Value<string>("id"), token.Value<string>("url"))).OrderBy(s => Version.Parse(s.Item1)).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,2 +1,2 @@
|
|||
rootProject.name = 'GLaunch2'
|
||||
rootProject.name = 'Inceptum'
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
package io.gitlab.jfronny.glaunch;
|
||||
|
||||
public class Config {
|
||||
public boolean snapshots = false; //TODO allow configuring
|
||||
public boolean darkTheme = false;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package io.gitlab.jfronny.glaunch.model;
|
||||
|
||||
public record Rules(boolean allow) {
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package io.gitlab.jfronny.inceptum;
|
||||
|
||||
public class Config {
|
||||
public boolean snapshots = false;
|
||||
public boolean darkTheme = false;
|
||||
public String lastAccount;
|
||||
}
|
|
@ -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<Window> 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();
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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<OauthTokenResponse> {
|
||||
@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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -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,
|
|
@ -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;
|
||||
|
|
@ -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;
|
|
@ -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<String, AssetIndex.Asset> 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);
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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"));
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package io.gitlab.jfronny.inceptum.model;
|
||||
|
||||
public class InceptumVersion {
|
||||
public String version;
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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<Skin> skins;
|
||||
public List<Cape> 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.gitlab.jfronny.inceptum.model.microsoft;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Store {
|
||||
public List<StoreItem> items;
|
||||
public String signature;
|
||||
|
||||
public static class StoreItem {
|
||||
public String name;
|
||||
public String signature;
|
||||
}
|
||||
}
|
|
@ -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<XUIClaim> xui;
|
||||
|
||||
public static class XUIClaim {
|
||||
public String uhs;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package io.gitlab.jfronny.glaunch.model;
|
||||
package io.gitlab.jfronny.inceptum.model.mojang;
|
||||
|
||||
import java.util.Map;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.gitlab.jfronny.glaunch.model;
|
||||
package io.gitlab.jfronny.inceptum.model.mojang;
|
||||
|
||||
import java.util.Set;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package io.gitlab.jfronny.inceptum.model.mojang;
|
||||
|
||||
public record Rules(boolean allow) {
|
||||
}
|
|
@ -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;
|
|
@ -1,4 +1,4 @@
|
|||
package io.gitlab.jfronny.glaunch.model;
|
||||
package io.gitlab.jfronny.inceptum.model.mojang;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.gitlab.jfronny.glaunch.model;
|
||||
package io.gitlab.jfronny.inceptum.model.mojang;
|
||||
|
||||
import java.util.Date;
|
||||
|
|
@ -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<String, String> entries) {
|
||||
/*StringBuilder content = new StringBuilder();
|
||||
for (Map.Entry<String, String> 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> T _send(String accept, HttpResponse.BodyHandler<T> 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<String> sendLines() {
|
||||
return _send("*/*", HttpResponse.BodyHandlers.ofLines());
|
||||
}
|
||||
|
||||
public <T> 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<String>)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);
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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 {
|
|
@ -1,4 +1,4 @@
|
|||
package io.gitlab.jfronny.glaunch.util;
|
||||
package io.gitlab.jfronny.inceptum.util;
|
||||
|
||||
public interface ThrowingSupplier<T, TEx extends Throwable> {
|
||||
T get() throws TEx;
|
|
@ -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> T downloadObject(String url, Class<T> type) throws IOException {
|
||||
return downloadObject(url, () -> downloadString(url), type);
|
||||
return downloadObject(url, () -> HttpUtils.get(url).sendString(), type);
|
||||
}
|
||||
|
||||
public static <T> T downloadObject(String url, String sha1, Class<T> type) throws IOException {
|
||||
|
@ -61,16 +63,16 @@ public class Utils {
|
|||
}
|
||||
|
||||
private static <T> T downloadObject(String url, ThrowingSupplier<String, IOException> sourceString, Class<T> 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> T loadObject(Path file, Class<T> type) throws IOException {
|
||||
try (BufferedReader br = Files.newBufferedReader(file)) {
|
||||
return GLaunch.GSON.fromJson(br, type);
|
||||
return Inceptum.GSON.fromJson(br, type);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T loadObject(Path file, Type type) throws IOException {
|
||||
try (BufferedReader br = Files.newBufferedReader(file)) {
|
||||
return Inceptum.GSON.fromJson(br, type);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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<List<MicrosoftAccount>>() {}.getType();
|
||||
private static MicrosoftAccount SELECTED_ACCOUNT;
|
||||
private static final List<MicrosoftAccount> 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<MicrosoftAccount> 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<Boolean> 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;
|
||||
}
|
||||
}
|
|
@ -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<Object, Object> properties = new HashMap<>();
|
||||
properties.put("AuthMethod", "RPS");
|
||||
properties.put("SiteName", "user.auth.xboxlive.com");
|
||||
properties.put("RpsTicket", "d=" + accessToken);
|
||||
|
||||
Map<Object, Object> 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<Object, Object> properties = new HashMap<>();
|
||||
properties.put("SandboxId", "RETAIL");
|
||||
|
||||
List<String> userTokens = new ArrayList<>();
|
||||
userTokens.add(xblToken);
|
||||
properties.put("UserTokens", userTokens);
|
||||
|
||||
Map<Object, Object> 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<Object, Object> data = new HashMap<Object, Object>();
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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.");
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<String> source;
|
||||
|
||||
public LogWindow(Set<String> 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;
|
||||
}
|
||||
}
|
|
@ -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<InstanceMeta> instances; //TODO custom ordering
|
||||
private final ImInt accountIndex = new ImInt(0);
|
||||
public MainWindow() {
|
||||
super("GLaunch");
|
||||
super("Inceptum");
|
||||
List<InstanceMeta> 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<MicrosoftAccount> 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)
|
||||
);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<VersionsListInfo> vil = getVersions(GLaunch.CONFIG.snapshots);
|
||||
List<VersionsListInfo> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() {
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration xmlns="http://logging.apache.org/log4j/2.0/config" packages="io.gitlab.jfronny.glaunch.util">
|
||||
<Configuration xmlns="http://logging.apache.org/log4j/2.0/config" packages="io.gitlab.jfronny.inceptum.util">
|
||||
<Appenders>
|
||||
<Console name="STDOUT" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%-5p | %d{yyyy-MM-dd HH:mm:ss} | [%t] %C{2} (%F:%L) - %m%n" />
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"version": "${version}"
|
||||
}
|
Loading…
Reference in New Issue