Fix auth, add modrinth export (untested)
This commit is contained in:
parent
f7a3d5be53
commit
d2c0979d16
|
@ -9,14 +9,12 @@ For documentation on how to use or install Inceptum, please head to the [wiki](h
|
|||
Inceptum utilizes code/libraries/assets from:
|
||||
|
||||
- [ATLauncher](https://github.com/ATLauncher/ATLauncher): Microsoft authentication
|
||||
- [JLHTTP](https://www.freeutils.net/source/jlhttp/): Used in MS Auth
|
||||
- [MultiMC](https://github.com/MultiMC/Launcher): Used as a reference for some implementations
|
||||
- [imgui-java](https://github.com/SpaiR/imgui-java): The library used for UI
|
||||
- [Dear ImGui](https://github.com/ocornut/imgui): Included and wrapped in imgui-java, UI library
|
||||
- [LWJGL](https://github.com/LWJGL/lwjgl3): Used as a backend for imgui-java
|
||||
- [gson](https://github.com/google/gson): Used for interacting with various APIs and configs
|
||||
- [slf4j](https://github.com/qos-ch/slf4j): Used for logging
|
||||
- [logback](https://github.com/qos-ch/logback): An implementation of slf4j
|
||||
- [JGit](https://www.eclipse.org/jgit/): Used to allow syncing repositories
|
||||
- [Ubuntu](https://design.ubuntu.com/font/): Used with nerd font symbols as the font
|
||||
- [meteor-client](https://github.com/MeteorDevelopment/meteor-client): A simple HTTP client
|
||||
- Several of [my other projects](https://gitlab.com/jfmods)
|
||||
|
|
|
@ -28,8 +28,8 @@ println("Using Inceptum Build Script $version")
|
|||
|
||||
val lwjglVersion by extra("3.3.1")
|
||||
val imguiVersion by extra("1.86.4")
|
||||
val jfCommonsVersion by extra("2022.9.18+12-38-21")
|
||||
val jgitVersion by extra("6.2.0.202206071550-r")
|
||||
val jfCommonsVersion by extra("2022.9.18+16-50-22")
|
||||
val jlhttpVersion by extra("2.6")
|
||||
val flavorProp: String by extra(if (project.hasProperty("flavor")) "${project.property("flavor")}" else "custom")
|
||||
if (flavorProp != "custom" && flavorProp != "maven" && flavorProp != "fat" && flavorProp != "windows" && flavorProp != "linux" && flavorProp != "macos")
|
||||
throw IllegalStateException("Unsupported flavor: $flavorProp")
|
||||
|
|
|
@ -11,11 +11,11 @@ import java.nio.file.Path;
|
|||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
//TODO modrinth
|
||||
public class ExportCommand extends BaseInstanceCommand {
|
||||
public ExportCommand() {
|
||||
this(List.of("export"), List.of(
|
||||
new ExportCommand(List.of("curseforge", "cf"), List.of()),
|
||||
new ModrinthExportCommand(List.of("modrinth", "mr"), List.of()),
|
||||
new MultiMCExportCommand(List.of("multimc", "mmc"), List.of())
|
||||
));
|
||||
}
|
||||
|
@ -50,4 +50,21 @@ public class ExportCommand extends BaseInstanceCommand {
|
|||
Exporters.MULTI_MC.generate(state, instancePath, meta, mds, Paths.get(args.get(0)), "1.0");
|
||||
}
|
||||
}
|
||||
|
||||
private static class ModrinthExportCommand extends BaseInstanceCommand {
|
||||
public ModrinthExportCommand(List<String> aliases, List<Command> subCommands) {
|
||||
super("Export a Modrinth instance", "<export path> <version>", aliases, subCommands);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws Exception {
|
||||
if (args.length == 0) throw new IllegalAccessException("You must specify a target path");
|
||||
if (args.length == 1) throw new IllegalAccessException("You must specify a version number");
|
||||
if (args.length != 2) throw new IllegalAccessException("Too many arguments");
|
||||
ProcessState state = new ProcessState();
|
||||
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
|
||||
mds.runOnce(R::nop);
|
||||
Exporters.MODRINTH.generate(state, instancePath, meta, mds, Paths.get(args.get(0)), args.get(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,14 @@
|
|||
package io.gitlab.jfronny.inceptum.imgui.window;
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import imgui.ImGui;
|
||||
import io.gitlab.jfronny.commons.serialize.Serializer;
|
||||
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.account.*;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.microsoft.*;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class MicrosoftLoginWindow extends Window {
|
||||
private static HttpServer server;
|
||||
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 final MicrosoftAccount account;
|
||||
private final MicrosoftAuthServer server;
|
||||
|
||||
public MicrosoftLoginWindow() {
|
||||
this(null);
|
||||
|
@ -27,9 +17,9 @@ public class MicrosoftLoginWindow extends Window {
|
|||
public MicrosoftLoginWindow(MicrosoftAccount account) {
|
||||
super("Microsoft Login");
|
||||
|
||||
this.account = account;
|
||||
this.server = new MicrosoftAuthServer(account);
|
||||
try {
|
||||
startServer();
|
||||
this.server.start();
|
||||
} catch (Exception e) {
|
||||
Utils.LOGGER.error("Could not start mc login server", e);
|
||||
}
|
||||
|
@ -41,141 +31,16 @@ public class MicrosoftLoginWindow extends Window {
|
|||
ImGui.text("Click the button below to begin");
|
||||
if (ImGui.button("Open in Browser")) {
|
||||
try {
|
||||
Utils.openWebBrowser(new URI(MICROSOFT_LOGIN_URL));
|
||||
Utils.openWebBrowser(new URI(MicrosoftAuthAPI.MICROSOFT_LOGIN_URL));
|
||||
} catch (URISyntaxException e) {
|
||||
Utils.LOGGER.error("Could not open browser", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startServer() throws Exception {
|
||||
if (server != null) return;
|
||||
server = HttpServer.create(new InetSocketAddress("127.0.0.1", MicrosoftAuthAPI.MICROSOFT_LOGIN_REDIRECT_PORT), 0);
|
||||
|
||||
server.createContext("/", req -> {
|
||||
if (req.getRequestMethod().equals("GET")) {
|
||||
Map<String, String> query = new LinkedHashMap<>();
|
||||
for (String pair : req.getRequestURI().getQuery().split("&")) {
|
||||
int index = pair.indexOf('=');
|
||||
query.put(URLDecoder.decode(pair.substring(0, index), StandardCharsets.UTF_8), URLDecoder.decode(pair.substring(index + 1), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
int respCode;
|
||||
String respStr;
|
||||
|
||||
if (query.containsKey("error")) {
|
||||
respCode = 500;
|
||||
respStr = "Error logging in. Check console for more information";
|
||||
Utils.LOGGER.error("Error logging into Microsoft account: " + URLDecoder
|
||||
.decode(query.get("error_description"), StandardCharsets.UTF_8.toString()));
|
||||
} else if (query.containsKey("code")) {
|
||||
try {
|
||||
acquireAccessToken(query.get("code"));
|
||||
respCode = 200;
|
||||
respStr = "Login complete. You can now close this window and go back to Inceptum";
|
||||
} catch (Exception e) {
|
||||
Utils.LOGGER.error("Error acquiring accessToken", e);
|
||||
respCode = 500;
|
||||
respStr = "Error logging in. Check console for more information";
|
||||
}
|
||||
} else {
|
||||
respCode = 400;
|
||||
respStr = "Code is missing";
|
||||
}
|
||||
|
||||
OutputStream os = req.getResponseBody();
|
||||
req.sendResponseHeaders(respCode, respStr.length());
|
||||
os.write(respStr.getBytes(StandardCharsets.UTF_8));
|
||||
os.flush();
|
||||
os.close();
|
||||
|
||||
stopServer();
|
||||
}
|
||||
});
|
||||
|
||||
server.start();
|
||||
}
|
||||
|
||||
private void stopServer() {
|
||||
if (server == null) return;
|
||||
server.stop(0);
|
||||
server = null;
|
||||
}
|
||||
|
||||
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 = MicrosoftAuthAPI.getXstsToken(xblToken);
|
||||
|
||||
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);
|
||||
|
||||
Utils.LOGGER.info(Serializer.getInstance().serialize(store));
|
||||
|
||||
if (!(store.items.stream().anyMatch(i -> i.name.equalsIgnoreCase("product_minecraft"))
|
||||
&& store.items.stream().anyMatch(i -> i.name.equalsIgnoreCase("game_minecraft")))) {
|
||||
LauncherEnv.showError("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.", "Doesn't own Minecraft");
|
||||
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);
|
||||
|
||||
close();
|
||||
LauncherEnv.showInfo("The account \"" + profile.name + "\" was added successfully", "Account added");
|
||||
}
|
||||
|
||||
private void addAccount(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse, LoginResponse loginResponse, Profile profile) {
|
||||
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 (this.account != null && !Objects.equals(account.accountId, this.account.accountId)) {
|
||||
LauncherEnv.showError("Logged into incorrect account. Please login again on the Accounts tab", "Incorrect account");
|
||||
return;
|
||||
}
|
||||
|
||||
account.update(oauthTokenResponse, xstsAuthResponse, loginResponse, profile);
|
||||
AccountManager.saveAccounts();
|
||||
} else {
|
||||
MicrosoftAccount account = new MicrosoftAccount(oauthTokenResponse, xstsAuthResponse, loginResponse, profile);
|
||||
AccountManager.addAccount(account);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
stopServer();
|
||||
server.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public class NewInstanceWindow extends Window {
|
|||
ImGui.text("Using git to manage it is recommended if you do so");
|
||||
ImGui.spacing();
|
||||
ImGui.text("Importing CurseForge or Modrinth packs is not yet implemented");
|
||||
//TODO generic importer based on zip contents and file name ("mrpack")
|
||||
//TODO generic importer based on zip contents and file name ("zip", "mrpack")
|
||||
ImGui.endTabItem();
|
||||
}
|
||||
ImGui.endTabBar();
|
||||
|
|
|
@ -5,6 +5,7 @@ import imgui.type.ImBoolean;
|
|||
import io.gitlab.jfronny.inceptum.imgui.GuiMain;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class Window implements Closeable {
|
||||
private final String name;
|
||||
|
|
|
@ -3,6 +3,9 @@ plugins {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
val jlhttpVersion: String by rootProject.extra
|
||||
|
||||
api(project(":common"))
|
||||
implementation("net.freeutils:jlhttp:$jlhttpVersion")
|
||||
compileOnly("org.jetbrains:annotations:23.0.0")
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ public class MicrosoftAuthAPI {
|
|||
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_LOGIN_URL = "https://api.minecraftservices.com/launcher/login";
|
||||
public static final String MICROSOFT_MINECRAFT_PROFILE_URL = "https://api.minecraftservices.com/minecraft/profile";
|
||||
public static final String MICROSOFT_MINECRAFT_ENTITLEMENTS_URL = "https://api.minecraftservices.com/entitlements/license?requestId=";
|
||||
|
||||
public static OauthTokenResponse tradeCodeForAccessToken(String code) throws IOException, URISyntaxException {
|
||||
return HttpUtils.post(MICROSOFT_AUTH_TOKEN_URL)
|
||||
|
@ -83,16 +83,17 @@ public class MicrosoftAuthAPI {
|
|||
}
|
||||
|
||||
public static LoginResponse loginToMinecraft(String xstsToken) throws IOException, URISyntaxException {
|
||||
Map<Object, Object> data = new HashMap<Object, Object>();
|
||||
data.put("identityToken", xstsToken);
|
||||
Map<Object, Object> data = new HashMap<>();
|
||||
data.put("xtoken", xstsToken);
|
||||
data.put("platform", "PC_LAUNCHER");
|
||||
|
||||
return HttpUtils.post(MICROSOFT_MINECRAFT_LOGIN_URL)
|
||||
.bodySerialized(data)
|
||||
.sendSerialized(LoginResponse.class);
|
||||
}
|
||||
|
||||
public static Store getMcEntitlements(String accessToken) throws IOException, URISyntaxException {
|
||||
return HttpUtils.get(MICROSOFT_MINECRAFT_STORE_URL).bearer(accessToken).sendSerialized(Store.class);
|
||||
public static Entitlements getEntitlements(String accessToken) throws IOException, URISyntaxException {
|
||||
return HttpUtils.get(MICROSOFT_MINECRAFT_ENTITLEMENTS_URL + UUID.randomUUID()).bearer(accessToken).sendSerialized(Entitlements.class);
|
||||
}
|
||||
|
||||
public static Profile getMcProfile(String accessToken) throws IOException, URISyntaxException {
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.api.account;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.microsoft.*;
|
||||
import net.freeutils.httpserver.HTTPServer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MicrosoftAuthServer implements Closeable {
|
||||
private static final HTTPServer server = new HTTPServer(MicrosoftAuthAPI.MICROSOFT_LOGIN_REDIRECT_PORT);
|
||||
private static final HTTPServer.VirtualHost host = server.getVirtualHost(null);
|
||||
|
||||
private final MicrosoftAccount previous;
|
||||
|
||||
public MicrosoftAuthServer(@Nullable MicrosoftAccount previous) {
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
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");
|
||||
Utils.LOGGER.error("Error logging into Microsoft account: " + URLDecoder
|
||||
.decode(req.getParams().get("error_description"), StandardCharsets.UTF_8));
|
||||
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) {
|
||||
Utils.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 the Launcher");
|
||||
close();
|
||||
return 0;
|
||||
}, "GET");
|
||||
|
||||
server.start();
|
||||
}
|
||||
|
||||
private void addAccount(OauthTokenResponse oauthTokenResponse, XboxLiveAuthResponse xstsAuthResponse, LoginResponse loginResponse, Profile profile) {
|
||||
if (this.previous != 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 (this.previous != null && !Objects.equals(account.accountId, this.previous.accountId)) {
|
||||
LauncherEnv.showError("Logged into incorrect account. Please login again on the Accounts tab", "Incorrect account");
|
||||
return;
|
||||
}
|
||||
|
||||
account.update(oauthTokenResponse, xstsAuthResponse, loginResponse, profile);
|
||||
AccountManager.saveAccounts();
|
||||
} else {
|
||||
MicrosoftAccount account = new MicrosoftAccount(oauthTokenResponse, xstsAuthResponse, loginResponse, profile);
|
||||
AccountManager.addAccount(account);
|
||||
}
|
||||
}
|
||||
|
||||
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 = MicrosoftAuthAPI.getXstsToken(xblToken);
|
||||
|
||||
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);
|
||||
|
||||
if (loginResponse == null) {
|
||||
throw new Exception("Failed to login to Minecraft");
|
||||
}
|
||||
|
||||
Entitlements entitlements = MicrosoftAuthAPI.getEntitlements(loginResponse.accessToken);
|
||||
|
||||
if (!(entitlements.items.stream().anyMatch(i -> i.name.equalsIgnoreCase("product_minecraft"))
|
||||
&& entitlements.items.stream().anyMatch(i -> i.name.equalsIgnoreCase("game_minecraft")))) {
|
||||
LauncherEnv.showError("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.", "Doesn't own Minecraft");
|
||||
throw new Exception("Account does not own Minecraft");
|
||||
}
|
||||
|
||||
Profile profile = null;
|
||||
|
||||
try {
|
||||
profile = MicrosoftAuthAPI.getMcProfile(loginResponse.accessToken);
|
||||
} catch (Exception e) {
|
||||
LauncherEnv.showError("""
|
||||
No Minecraft profiles were found for this account. Have you purchased Minecraft?
|
||||
Please make sure you've bought the Java edition of Minecraft and then try again.
|
||||
If you're an Xbox Game Pass subscriber, make sure to login and play through the Minecraft
|
||||
Launcher once in order to create your Minecraft profile, then try logging in again.""",
|
||||
"Minecraft Profile Not Found");
|
||||
throw new Exception("Minecraft Profile not found", e);
|
||||
}
|
||||
|
||||
if (profile == null) {
|
||||
throw new Exception("Failed to get Minecraft profile");
|
||||
}
|
||||
|
||||
// add the account
|
||||
addAccount(oauthTokenResponse, xstsAuthResponse, loginResponse, profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
server.stop();
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ package io.gitlab.jfronny.inceptum.launcher.model.microsoft;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
public class Store {
|
||||
public class Entitlements {
|
||||
public List<StoreItem> items;
|
||||
public String signature;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.model.modrinth;
|
||||
|
||||
public class ModrinthHashes { //TODO ensure this can parse with additional hashes
|
||||
public String sha1;
|
||||
public String sha512;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.model.modrinth;
|
||||
|
||||
import io.gitlab.jfronny.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ModrinthModpackManifest {
|
||||
public Integer formatVersion; // 1
|
||||
public String game; // "minecraft"
|
||||
public String versionId;
|
||||
public String name;
|
||||
public String summary; // optional
|
||||
public List<File> files;
|
||||
public Dependencies dependencies;
|
||||
|
||||
public static class File {
|
||||
public String path;
|
||||
public ModrinthHashes hashes;
|
||||
public Env env; // optional
|
||||
public List<String> downloads;
|
||||
public Long fileSize;
|
||||
|
||||
public static class Env {
|
||||
public ModrinthDependencyType client;
|
||||
public ModrinthDependencyType server;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Dependencies { // All are nullable
|
||||
public String minecraft;
|
||||
public String forge;
|
||||
@SerializedName("fabric-loader") public String fabricLoader;
|
||||
@SerializedName("quilt-loader") public String quiltLoader;
|
||||
}
|
||||
}
|
|
@ -25,15 +25,10 @@ public class ModrinthVersion {
|
|||
}
|
||||
|
||||
public static class File {
|
||||
public Hashes hashes;
|
||||
public ModrinthHashes hashes;
|
||||
public String url;
|
||||
public String filename;
|
||||
public Boolean primary;
|
||||
|
||||
public static class Hashes {
|
||||
public String sha1;
|
||||
public String sha512;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Dependency {
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.util.List;
|
|||
public class Exporters {
|
||||
public static final int STEP_COUNT = 4;
|
||||
public static final CurseForgeExporter CURSE_FORGE = new CurseForgeExporter();
|
||||
public static final ModrinthExporter MODRINTH = new ModrinthExporter();
|
||||
public static final MultiMCExporter MULTI_MC = new MultiMCExporter();
|
||||
public static final List<Exporter<?>> EXPORTERS = List.of(CURSE_FORGE, MULTI_MC);
|
||||
public static final List<Exporter<?>> EXPORTERS = List.of(CURSE_FORGE, MODRINTH, MULTI_MC);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.export;
|
||||
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.ModrinthModpackManifest;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.source.ModrinthModSource;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
public class ModrinthExporter extends Exporter<ModrinthModpackManifest> {
|
||||
public ModrinthExporter() {
|
||||
super("Modrinth", "mrpack", "overrides");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModrinthModpackManifest generateManifests(Path root, InstanceMeta instance, String name, String version) throws IOException {
|
||||
ModrinthModpackManifest manifest = new ModrinthModpackManifest();
|
||||
manifest.formatVersion = 1;
|
||||
manifest.game = "minecraft";
|
||||
manifest.versionId = version;
|
||||
manifest.name = name;
|
||||
manifest.files = new ArrayList<>();
|
||||
manifest.dependencies = new ModrinthModpackManifest.Dependencies();
|
||||
manifest.dependencies.minecraft = instance.getMinecraftVersion();
|
||||
if (instance.isFabric()) {
|
||||
manifest.dependencies.fabricLoader = instance.getLoaderVersion();
|
||||
}
|
||||
JFiles.writeObject(root.resolve("modrinth.index.json"), manifest);
|
||||
return manifest;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addMods(Path root, InstanceMeta instance, Iterable<IWModDescription> mods, ModrinthModpackManifest manifest, Path modsOverrides) throws IOException {
|
||||
modsLoop: for(IWModDescription mod : mods) {
|
||||
if (ModPath.isImod(mod.path())) {
|
||||
Set<ModSource> sources = mod.mod().orElseThrow().sources.keySet();
|
||||
for (ModSource source : sources) {
|
||||
if (source instanceof ModrinthModSource cms) {
|
||||
manifest.files.add(cms.toManifest());
|
||||
continue modsLoop;
|
||||
}
|
||||
}
|
||||
// Not available on modrinth
|
||||
for (ModSource source : sources) {
|
||||
Files.createDirectories(modsOverrides);
|
||||
Files.copy(source.getJarPath(), modsOverrides.resolve(mod.path().getFileName().toString()));
|
||||
continue modsLoop;
|
||||
}
|
||||
} else {
|
||||
Files.createDirectories(modsOverrides);
|
||||
Files.copy(mod.path(), modsOverrides.resolve(mod.path().getFileName().toString()));
|
||||
continue modsLoop;
|
||||
}
|
||||
throw new FileNotFoundException("Could not find mod file for " + mod.path());
|
||||
}
|
||||
JFiles.writeObject(root.resolve("modrinth.index.json"), manifest);
|
||||
}
|
||||
}
|
|
@ -5,8 +5,7 @@ import io.gitlab.jfronny.commons.cache.MemoryOperationResultCache;
|
|||
import io.gitlab.jfronny.commons.tuple.Tuple;
|
||||
import io.gitlab.jfronny.inceptum.common.Net;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.ModrinthProject;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.ModrinthVersion;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.*;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.ModrinthApi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -112,4 +111,16 @@ public final class ModrinthModSource implements ModSource {
|
|||
public String getModId() {
|
||||
return current.project_id;
|
||||
}
|
||||
|
||||
public ModrinthModpackManifest.File toManifest() throws IOException {
|
||||
ModrinthVersion.File orig = current.files.get(0);
|
||||
ModrinthModpackManifest.File f = new ModrinthModpackManifest.File();
|
||||
f.path = "mods/" + orig.filename;
|
||||
f.hashes = new ModrinthHashes();
|
||||
f.hashes.sha1 = orig.hashes.sha1;
|
||||
f.hashes.sha512 = orig.hashes.sha512;
|
||||
f.downloads = List.of(orig.url);
|
||||
f.fileSize = Files.size(getJarPath());
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue