A lot of changes

This commit is contained in:
JFronny 2021-10-29 22:50:42 +02:00
parent 5af42284ad
commit e2178809ab
No known key found for this signature in database
GPG Key ID: BEC5ACBBD4EE17E5
57 changed files with 1274 additions and 452 deletions

View File

@ -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

5
README.md Normal file
View File

@ -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

View File

@ -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

View File

@ -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();
}
}
}

View File

@ -1,2 +1,2 @@
rootProject.name = 'GLaunch2'
rootProject.name = 'Inceptum'

View File

@ -1,6 +0,0 @@
package io.gitlab.jfronny.glaunch;
public class Config {
public boolean snapshots = false; //TODO allow configuring
public boolean darkTheme = false;
}

View File

@ -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);
}
}

View File

@ -1,4 +0,0 @@
package io.gitlab.jfronny.glaunch.model;
public record Rules(boolean allow) {
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,7 @@
package io.gitlab.jfronny.inceptum;
public class Config {
public boolean snapshots = false;
public boolean darkTheme = false;
public String lastAccount;
}

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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"));
}

View File

@ -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);
}

View File

@ -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"));

View File

@ -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);
}
}

View File

@ -0,0 +1,5 @@
package io.gitlab.jfronny.inceptum.model;
public class InceptumVersion {
public String version;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.glaunch.model;
package io.gitlab.jfronny.inceptum.model.mojang;
import java.util.Map;

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.glaunch.model;
package io.gitlab.jfronny.inceptum.model.mojang;
import java.util.Set;

View File

@ -0,0 +1,4 @@
package io.gitlab.jfronny.inceptum.model.mojang;
public record Rules(boolean allow) {
}

View File

@ -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;

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.glaunch.model;
package io.gitlab.jfronny.inceptum.model.mojang;
import java.util.List;

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.glaunch.model;
package io.gitlab.jfronny.inceptum.model.mojang;
import java.util.Date;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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.");
}

View File

@ -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();
}
}
}
}

View File

@ -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;
}
}

View File

@ -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)
);
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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() {

View File

@ -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" />

View File

@ -0,0 +1,3 @@
{
"version": "${version}"
}