aboutsummaryrefslogtreecommitdiff
path: root/loader/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'loader/src/main')
-rwxr-xr-xloader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DGInterface.java31
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java24
-rwxr-xr-xloader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java261
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java150
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java70
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/InvalidDungeonsGuideCredentialsException.java8
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/ResourceManager.java164
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/AuthProvider.java16
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/DgAuth/DgAuth.java46
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/DgAuth/DgAuthUtil.java88
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/NullAuth.java62
-rwxr-xr-xloader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/Authenticator.java257
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/TokenStatus.java27
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/Update.java46
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateBranch.java29
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/AuthServerException.java34
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/NoSuitableLoaderFoundException.java32
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/PrivacyPolicyRequiredException.java22
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/ReferenceLeakedException.java22
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/TokenExpiredException.java26
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/GuiLoadingError.java90
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/GuiPrivacyPolicy.java53
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java39
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java134
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java64
-rwxr-xr-xloader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGConnection.java49
-rwxr-xr-xloader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGStreamHandler.java34
-rwxr-xr-xloader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGStreamHandlerFactory.java35
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/QRCodeGenerator.java43
-rwxr-xr-xloader/src/main/resources/mcmod.info16
30 files changed, 1972 insertions, 0 deletions
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DGInterface.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DGInterface.java
new file mode 100755
index 00000000..c88a3cbf
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DGInterface.java
@@ -0,0 +1,31 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher;
+
+import net.minecraft.client.resources.IResourceManager;
+import net.minecraftforge.fml.common.event.FMLInitializationEvent;
+import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
+
+import java.io.File;
+
+public interface DGInterface {
+ void init(File resourceDir);
+ void unload();
+ void onResourceReload(IResourceManager a);
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java
new file mode 100644
index 00000000..7252a9db
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java
@@ -0,0 +1,24 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher;
+
+public interface DungeonsGuideReloadListener {
+ public void unloadReference();
+ public void onLoad(DGInterface dgInterface);
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java
new file mode 100755
index 00000000..7cc0f806
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java
@@ -0,0 +1,261 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher;
+
+import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
+import com.mojang.authlib.exceptions.InvalidCredentialsException;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.AuthServerException;
+import kr.syeyoung.dungeonsguide.launcher.authentication.Authenticator;
+import kr.syeyoung.dungeonsguide.launcher.branch.ModDownloader;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.NoSuitableLoaderFoundException;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.PrivacyPolicyRequiredException;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.TokenExpiredException;
+import kr.syeyoung.dungeonsguide.launcher.gui.GuiLoadingError;
+import kr.syeyoung.dungeonsguide.launcher.gui.GuiPrivacyPolicy;
+import kr.syeyoung.dungeonsguide.launcher.loader.IDGLoader;
+import kr.syeyoung.dungeonsguide.launcher.loader.JarLoader;
+import kr.syeyoung.dungeonsguide.launcher.loader.LocalLoader;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiMainMenu;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.resources.IReloadableResourceManager;
+import net.minecraftforge.client.event.GuiOpenEvent;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.config.Configuration;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.Mod.EventHandler;
+import net.minecraftforge.fml.common.ProgressManager;
+import net.minecraftforge.fml.common.event.FMLInitializationEvent;
+import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@Mod(modid = Main.MOD_ID, version = Main.VERSION)
+public class Main
+{
+ public static final String MOD_ID = "dungeons_guide_wrapper";
+ public static final String VERSION = "1.0";
+ public static final String DOMAIN = "http://testmachine:8080/api";
+
+ private static Main main;
+
+ private File configDir;
+
+ private DGInterface dgInterface;
+ private Authenticator authenticator = new Authenticator();
+ private ModDownloader modDownloader = new ModDownloader(authenticator);
+
+ private List<DungeonsGuideReloadListener> listeners = new ArrayList<>();
+
+ public void addDGReloadListener(DungeonsGuideReloadListener dungeonsGuideReloadListener) {
+ listeners.add(Objects.requireNonNull(dungeonsGuideReloadListener));
+ }
+ public void removeDGReloadListener(DungeonsGuideReloadListener dungeonsGuideReloadListener) {
+ listeners.remove(dungeonsGuideReloadListener);
+ }
+
+ private IDGLoader currentLoader;
+
+ private Throwable lastError;
+ private boolean isMcLoaded;
+
+
+
+
+ @EventHandler
+ public void initEvent(FMLInitializationEvent initializationEvent)
+ {
+ MinecraftForge.EVENT_BUS.register(this);
+ if (dgInterface != null) {
+ try {
+ dgInterface.init(configDir);
+
+ for (DungeonsGuideReloadListener listener : listeners) {
+ listener.onLoad(dgInterface);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ setLastFatalError(e);
+ }
+ }
+ }
+
+ public void unload() throws ReferenceLeakedException {
+ if (currentLoader != null && !currentLoader.isUnloadable()) {
+ throw new UnsupportedOperationException("Current version is not unloadable");
+ }
+ dgInterface = null;
+ for (DungeonsGuideReloadListener listener : listeners) {
+ listener.unloadReference();
+ }
+ if (currentLoader != null) {
+ currentLoader.unloadJar();
+ }
+ currentLoader = null;
+ }
+ private void load(IDGLoader newLoader) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+ partialLoad(newLoader);
+
+ dgInterface.init(configDir);
+
+ for (DungeonsGuideReloadListener listener : listeners) {
+ listener.onLoad(dgInterface);
+ }
+ }
+ private void partialLoad(IDGLoader newLoader) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+ if (dgInterface != null) throw new IllegalStateException("DG is loaded");
+ newLoader.loadJar(authenticator);
+ dgInterface = newLoader.getInstance();
+ currentLoader = newLoader;
+ }
+
+ public void reload(IDGLoader newLoader) {
+ try {
+ unload();
+ load(newLoader);
+ } catch (Exception e) {
+ dgInterface = null;
+ currentLoader = null;
+
+ e.printStackTrace();
+ setLastFatalError(e);
+ }
+ }
+
+ public void tryOpenError() {
+ if (isMcLoaded) Minecraft.getMinecraft().displayGuiScreen(obtainErrorGUI());
+ }
+
+ public GuiScreen obtainErrorGUI() {
+ if (lastError instanceof PrivacyPolicyRequiredException) {
+ return new GuiPrivacyPolicy();
+ } else if (lastError instanceof TokenExpiredException) {
+
+ } else if (lastError instanceof NoSuitableLoaderFoundException) {
+
+ } else if (lastError instanceof ReferenceLeakedException) {
+
+ } else if (lastError instanceof AuthServerException) {
+
+ } else if (lastError instanceof InvalidCredentialsException) {
+
+ } else if (lastError instanceof AuthenticationUnavailableException) {
+
+ } else if (lastError != null){
+ return new GuiLoadingError(lastError, () -> {lastError = null;});
+ }
+ if (lastError != null)
+ lastError.printStackTrace();
+ // when gets called init and stuff remove thing
+ return null;
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOWEST)
+ public void onGuiOpen(GuiOpenEvent guiOpenEvent) {
+ if (guiOpenEvent.gui instanceof GuiMainMenu) {
+ isMcLoaded = true;
+ }
+ if (lastError != null && guiOpenEvent.gui instanceof GuiMainMenu) {
+ GuiScreen gui = obtainErrorGUI();
+ if (gui != null)
+ guiOpenEvent.gui = gui;
+ }
+ }
+
+ public String getLoaderName(Configuration configuration) {
+ String loader = System.getProperty("dg.loader");
+ if (loader == null) {
+ loader = configuration.get("loader", "modsource", "auto").getString();
+ }
+ if (loader == null) loader = "auto";
+ return loader;
+ }
+
+
+ public IDGLoader obtainLoader(Configuration configuration) {
+ String loader = getLoaderName(configuration);
+
+ if ("local".equals(loader) ||
+ (loader.equals("auto") && this.getClass().getResourceAsStream("/kr/syeyoung/dungeonsguide/DungeonsGuide.class") == null)) {
+ return new LocalLoader();
+ } else if ("jar".equals(loader) ||
+ (loader.equals("auto") && this.getClass().getResourceAsStream("/mod.jar") == null)) {
+ return new JarLoader();
+ } else if (loader.equals("auto") ){
+ // remote load
+ throw new UnsupportedOperationException(""); // yet
+ } else {
+ throw new NoSuitableLoaderFoundException(System.getProperty("dg.loader"), configuration.get("loader", "modsource", "auto").getString());
+ }
+ }
+
+ @EventHandler
+ public void preInit(FMLPreInitializationEvent preInitializationEvent) {
+ // setup static variables
+ main = this;
+ configDir = preInitializationEvent.getModConfigurationDirectory();
+
+ // setup preinit progress bar for well, progress bar!
+ ProgressManager.ProgressBar bar = ProgressManager.push("DungeonsGuide", 2);
+ try {
+ // Try authenticate
+ bar.step("Authenticating...");
+ authenticator.repeatAuthenticate(5);
+
+
+ // If authentication succeeds, obtain loader and partially load dungeons guide
+ File f = new File(preInitializationEvent.getModConfigurationDirectory(), "loader.cfg");
+ Configuration configuration = new Configuration(f);
+
+ bar.step("Instantiating...");
+ partialLoad(obtainLoader(configuration));
+
+ // Save config because... well to generate it
+ configuration.save();
+ } catch (Throwable t) {
+ dgInterface = null;
+ currentLoader = null;
+
+ t.printStackTrace();
+ setLastFatalError(t);
+ } finally {
+ while(bar.getStep() < bar.getSteps()) bar.step("");
+ ProgressManager.pop(bar);
+ }
+
+ ((IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()).registerReloadListener(a -> {
+ if (dgInterface != null) dgInterface.onResourceReload(a);
+ });
+ }
+
+ public void setLastFatalError(Throwable t) {
+ lastError = t;
+ tryOpenError();
+ }
+
+ public static Main getMain() {
+ return main;
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java
new file mode 100644
index 00000000..f0c346c6
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java
@@ -0,0 +1,150 @@
+package kr.syeyoung.dungeonsguide.launcher.auth;
+
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.google.gson.JsonObject;
+import com.mojang.authlib.exceptions.AuthenticationException;
+import kr.syeyoung.dungeonsguide.launcher.auth.authprovider.AuthProvider;
+import kr.syeyoung.dungeonsguide.launcher.auth.authprovider.DgAuth.DgAuth;
+import kr.syeyoung.dungeonsguide.launcher.auth.authprovider.DgAuth.DgAuthUtil;
+import kr.syeyoung.dungeonsguide.mod.chat.ChatTransmitter;
+import kr.syeyoung.dungeonsguide.mod.events.impl.AuthChangedEvent;
+import kr.syeyoung.dungeonsguide.mod.stomp.StompManager;
+import lombok.Setter;
+import net.minecraft.client.Minecraft;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+
+public class AuthManager {
+ Logger logger = LogManager.getLogger("AuthManger");
+
+ private static AuthManager instance;
+
+ public static AuthManager getInstance() {
+ if(instance == null) instance = new AuthManager();
+ return instance;
+ }
+
+
+ @Setter
+ private String baseserverurl = "https://dungeons.guide";
+
+ private AuthProvider currentProvider;
+
+ public String getToken() {
+ if (currentProvider != null && currentProvider.getToken() != null) {
+ return currentProvider.getToken();
+ }
+ return null;
+ }
+
+ public KeyPair getKeyPair(){
+ if (currentProvider != null && currentProvider.getToken() != null) {
+ return currentProvider.getRsaKey();
+ }
+ return null;
+ }
+
+
+ boolean initlock = false;
+
+ public void init() {
+ if (initlock) {
+ logger.info("Cannot init AuthManger twice");
+ return;
+ }
+
+ reauth();
+
+ initlock = true;
+
+
+ MinecraftForge.EVENT_BUS.register(this);
+
+ ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("DgAuth Pool").build();
+ final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, namedThreadFactory);
+ scheduler.scheduleAtFixedRate(() -> {
+ if (getToken() != null) {
+ JsonObject obj = DgAuthUtil.getJwtPayload(getToken());
+ if (!obj.get("uuid").getAsString().replace("-", "").equals(Minecraft.getMinecraft().getSession().getPlayerID())) {
+ shouldReAuth = true;
+ }
+ }
+ }, 10,2000, TimeUnit.MILLISECONDS);
+ }
+
+ boolean shouldReAuth = true;
+ int tickCounter;
+
+ @SubscribeEvent
+ public void onTickClientTick(TickEvent.ClientTickEvent event) {
+ if (event.phase != TickEvent.Phase.START) return;
+
+ if (tickCounter % 200 == 0) {
+ tickCounter = 0;
+ reauth();
+ }
+ tickCounter++;
+
+ }
+
+ public boolean isPlebUser(){
+ return Objects.equals(getInstance().getPlanType(), "OPENSOURCE");
+ }
+
+ public String getPlanType(){
+ if(getToken() == null) return null;
+
+
+ JsonObject jwt = DgAuthUtil.getJwtPayload(getToken());
+
+ if(!jwt.has("plan")) return null;
+
+ return jwt.get("plan").getAsString();
+
+ }
+
+ void reauth() {
+ if (!shouldReAuth) return;
+
+ shouldReAuth = false;
+
+ currentProvider = null;
+ try {
+ currentProvider = new DgAuth(baseserverurl).createAuthProvider();
+ if (currentProvider.getToken() == null) {
+ shouldReAuth = true;
+ currentProvider = null;
+ ChatTransmitter.addToQueue("§eDungeons Guide §7:: §r§cDG auth failed, trying again in ten seconds", true);
+ logger.info("DG auth failed, trying again in a second");
+ } else {
+ // RE-AUTHed SUCCESSFULLY HOORAY
+ // for some reason the forge events don't work in pre init, so I call the callback directly
+ StompManager.getInstance().init();
+ MinecraftForge.EVENT_BUS.post(new AuthChangedEvent());
+ }
+ } catch (NoSuchAlgorithmException | AuthenticationException | IOException e) {
+
+ shouldReAuth = true;
+ currentProvider = null;
+ ChatTransmitter.addToQueue("§eDungeons Guide §7:: §r§cDG auth failed, trying again in ten seconds", true);
+ logger.error("Re-auth failed with message {}, trying again in a ten seconds", String.valueOf(Throwables.getRootCause(e)));
+ }
+
+ }
+
+
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java
new file mode 100644
index 00000000..9ce02643
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthUtil.java
@@ -0,0 +1,70 @@
+package kr.syeyoung.dungeonsguide.launcher.auth;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+import javax.crypto.*;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.HttpsURLConnection;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.security.*;
+
+public class AuthUtil {
+ private AuthUtil() {}
+
+ public static KeyPair getKeyPair() throws NoSuchAlgorithmException {
+ KeyPairGenerator a = null;
+ a = KeyPairGenerator.getInstance("RSA");
+ a.initialize(1024);
+ return a.generateKeyPair();
+ }
+
+
+ public static JsonElement getJsonSecured(String u) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException{
+
+ int length = 0;
+ CipherInputStream cipherInputStream = null;
+
+ HttpsURLConnection httpsURLConnection = (HttpsURLConnection) new URL(u).openConnection();
+ httpsURLConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
+ httpsURLConnection.setRequestProperty("Content-Type", "application/json");
+ httpsURLConnection.setRequestMethod("GET");
+ httpsURLConnection.setRequestProperty("Authorization", AuthManager.getInstance().getToken());
+ httpsURLConnection.setDoInput(true);
+ httpsURLConnection.setDoOutput(true);
+
+ InputStream inputStream = httpsURLConnection.getInputStream();
+ byte[] lengthPayload = new byte[4];
+ inputStream.read(lengthPayload);
+ length = ((lengthPayload[0] & 0xFF) << 24) |
+ ((lengthPayload[1] & 0xFF) << 16) |
+ ((lengthPayload[2] & 0xFF) << 8) |
+ ((lengthPayload[3] & 0xFF));
+ while (inputStream.available() < length) ;
+ byte[] keyPayload = new byte[length];
+ inputStream.read(keyPayload);
+
+ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ cipher.init(Cipher.DECRYPT_MODE, AuthManager.getInstance().getKeyPair().getPrivate());
+ byte[] AESKey = cipher.doFinal(keyPayload);
+
+ cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ SecretKeySpec secretKeySpec = new SecretKeySpec(AESKey, "AES");
+ IvParameterSpec ivParameterSpec = new IvParameterSpec(AESKey);
+ cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
+ cipherInputStream = new CipherInputStream(inputStream, cipher);
+ cipherInputStream.read(lengthPayload);
+ length = ((lengthPayload[0] & 0xFF) << 24) |
+ ((lengthPayload[1] & 0xFF) << 16) |
+ ((lengthPayload[2] & 0xFF) << 8) |
+ ((lengthPayload[3] & 0xFF));
+
+ httpsURLConnection.disconnect();
+
+ return new JsonParser().parse(new InputStreamReader(cipherInputStream));
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/InvalidDungeonsGuideCredentialsException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/InvalidDungeonsGuideCredentialsException.java
new file mode 100644
index 00000000..ebe78196
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/InvalidDungeonsGuideCredentialsException.java
@@ -0,0 +1,8 @@
+package kr.syeyoung.dungeonsguide.launcher.auth;
+
+public class InvalidDungeonsGuideCredentialsException extends Throwable {
+
+ public InvalidDungeonsGuideCredentialsException(String message) {
+ super(message);
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/ResourceManager.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/ResourceManager.java
new file mode 100644
index 00000000..721b629f
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/ResourceManager.java
@@ -0,0 +1,164 @@
+package kr.syeyoung.dungeonsguide.launcher.auth;
+
+import lombok.Setter;
+import net.minecraftforge.common.MinecraftForge;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import javax.crypto.*;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.HttpsURLConnection;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.*;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class ResourceManager {
+
+ Logger logger = LogManager.getLogger("ResourceManager");
+
+ @Setter
+ private String baseUrl;
+ @Setter
+ private String BASE64_X509ENCODEDKEYSPEC;
+ private final HashMap<String, byte[]> loadedResources = new HashMap<>();
+
+
+ private static ResourceManager instance;
+ public static ResourceManager getInstance() {
+ if(instance == null) {
+ instance = new ResourceManager();
+ MinecraftForge.EVENT_BUS.register(instance);
+ }
+ return instance;
+ }
+
+ private ResourceManager() {
+ }
+
+ public Map<String, byte[]> getResources() {
+ return loadedResources;
+ }
+
+
+ public void downloadAssets(String version) throws InvalidDungeonsGuideCredentialsException {
+ if(AuthManager.getInstance().getToken() == null) throw new InvalidDungeonsGuideCredentialsException("Not Authenticated while downloading assets");
+ try {
+ // version not being null indicates that the user is "premium"
+ // so we download the special version
+ if (version != null)
+ downloadSafe( baseUrl + "/resource/version?v=" + version, true);
+
+ if(!AuthManager.getInstance().isPlebUser()){
+ downloadSafe(baseUrl + "/resource/roomdata", false);
+ } else {
+ logger.error("The current User is a pleb not downloading user data");
+ }
+
+ } catch (Exception t) {
+ t.printStackTrace();
+ }
+
+ }
+
+ private void downloadSafe(String url, boolean isValidateSignature) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, SignatureException, InvalidKeySpecException {
+ HttpsURLConnection dgConnection = (HttpsURLConnection) new URL(url).openConnection();
+ dgConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
+ dgConnection.setRequestProperty("Content-Type", "application/json");
+ dgConnection.setRequestMethod("GET");
+ dgConnection.setRequestProperty("Authorization", AuthManager.getInstance().getToken());
+ dgConnection.setDoInput(true);
+ dgConnection.setDoOutput(true);
+
+ InputStream inputStream = dgConnection.getInputStream();
+ byte[] lengthBytes = new byte[4];
+ inputStream.read(lengthBytes);
+ int length = ((lengthBytes[0] & 0xFF) << 24) |
+ ((lengthBytes[1] & 0xFF) << 16) |
+ ((lengthBytes[2] & 0xFF) << 8) |
+ ((lengthBytes[3] & 0xFF));
+ while (inputStream.available() < length) ;
+ byte[] keyPayload = new byte[length];
+ inputStream.read(keyPayload);
+
+ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ cipher.init(Cipher.DECRYPT_MODE, AuthManager.getInstance().getKeyPair().getPrivate());
+ byte[] h = cipher.doFinal(keyPayload);
+
+ cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ SecretKeySpec keySpec = new SecretKeySpec(h, "AES");
+ IvParameterSpec ivSpec = new IvParameterSpec(h);
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+ CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
+
+ cipherInputStream.read(lengthBytes);
+ length = ((lengthBytes[0] & 0xFF) << 24) |
+ ((lengthBytes[1] & 0xFF) << 16) |
+ ((lengthBytes[2] & 0xFF) << 8) |
+ ((lengthBytes[3] & 0xFF));
+
+ int totalLen = length;
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ byte[] buff = new byte[256];
+ while (totalLen > 0) {
+ int len = cipherInputStream.read(buff, 0, Math.min(buff.length, totalLen));
+ totalLen -= len;
+ bos.write(buff, 0, len);
+ }
+ byte[] body = bos.toByteArray();
+
+ byte[] signed;
+ if (isValidateSignature) {
+ cipherInputStream.read(lengthBytes,0 , 4);
+ length = ((lengthBytes[0] & 0xFF) << 24) |
+ ((lengthBytes[1] & 0xFF) << 16) |
+ ((lengthBytes[2] & 0xFF) << 8) |
+ ((lengthBytes[3] & 0xFF));
+
+ totalLen = length;
+ bos = new ByteArrayOutputStream();
+ while (totalLen > 0) {
+ int len = cipherInputStream.read(buff, 0, Math.min(buff.length, totalLen));
+ totalLen -= len;
+ bos.write(buff, 0, len);
+ }
+ signed = bos.toByteArray();
+
+ Signature sign = Signature.getInstance("SHA512withRSA");
+ sign.initVerify(getPublicKey(BASE64_X509ENCODEDKEYSPEC));
+ sign.update(body);
+ boolean truth = sign.verify(signed);
+ if (!truth) throw new SignatureException("DG SIGNATURE FORGED");
+ }
+
+ ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(body));
+ ZipEntry zipEntry;
+ while ((zipEntry=zipInputStream.getNextEntry()) != null) {
+ byte[] buffer = new byte[256];
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ int p;
+ while((p = zipInputStream.read(buffer)) > 0) {
+ byteArrayOutputStream.write(buffer, 0, p);
+ }
+ this.loadedResources.put(zipEntry.getName(), byteArrayOutputStream.toByteArray());
+ }
+ }
+
+
+ public static PublicKey getPublicKey(String base64X509EncodedKeySpec) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decodeBase64(base64X509EncodedKeySpec));
+
+ return KeyFactory.getInstance("RSA").generatePublic(spec);
+ }
+
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/AuthProvider.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/AuthProvider.java
new file mode 100644
index 00000000..d469c729
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/AuthProvider.java
@@ -0,0 +1,16 @@
+package kr.syeyoung.dungeonsguide.launcher.auth.authprovider;
+
+import com.mojang.authlib.exceptions.AuthenticationException;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+
+public interface AuthProvider {
+ String getToken();
+
+ KeyPair getRsaKey();
+
+
+ AuthProvider createAuthProvider() throws NoSuchAlgorithmException, AuthenticationException, IOException;
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/DgAuth/DgAuth.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/DgAuth/DgAuth.java
new file mode 100644
index 00000000..dca9ce33
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/DgAuth/DgAuth.java
@@ -0,0 +1,46 @@
+package kr.syeyoung.dungeonsguide.launcher.auth.authprovider.DgAuth;
+
+import com.mojang.authlib.exceptions.AuthenticationException;
+import kr.syeyoung.dungeonsguide.launcher.auth.AuthUtil;
+import kr.syeyoung.dungeonsguide.launcher.auth.authprovider.AuthProvider;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+
+public class DgAuth implements AuthProvider {
+
+ private final String authServerUrl;
+
+ public DgAuth(String authServerUrl){
+ this.authServerUrl = authServerUrl;
+ }
+
+ private String token;
+ private KeyPair rsaKey;
+
+ @Override
+ public String getToken() {
+ return token;
+ }
+
+ @Override
+ public KeyPair getRsaKey() {
+ return rsaKey;
+ }
+
+
+ @Override
+ public AuthProvider createAuthProvider() throws NoSuchAlgorithmException, AuthenticationException, IOException {
+ this.rsaKey = AuthUtil.getKeyPair();
+
+ String tempToken = DgAuthUtil.requestAuth(this.authServerUrl);
+
+ DgAuthUtil.checkSessionAuthenticity(tempToken);
+
+ this.token = DgAuthUtil.verifyAuth(tempToken, rsaKey.getPublic(), authServerUrl);
+
+ return this;
+ }
+
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/DgAuth/DgAuthUtil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/DgAuth/DgAuthUtil.java
new file mode 100644
index 00000000..53b57e1a
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/DgAuth/DgAuthUtil.java
@@ -0,0 +1,88 @@
+package kr.syeyoung.dungeonsguide.launcher.auth.authprovider.DgAuth;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.mojang.authlib.GameProfile;
+import com.mojang.authlib.exceptions.AuthenticationException;
+import com.mojang.authlib.minecraft.MinecraftSessionService;
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.Session;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+
+import javax.net.ssl.HttpsURLConnection;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.URL;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+
+public class DgAuthUtil {
+ private DgAuthUtil(){}
+
+ public static String requestAuth(String baseurl) throws IOException {
+ GameProfile profile = Minecraft.getMinecraft().getSession().getProfile();
+
+ HttpsURLConnection connection = (HttpsURLConnection) new URL(baseurl + "/auth/requestAuth").openConnection();
+ connection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
+ connection.setRequestProperty("Content-Type", "application/json");
+ connection.setRequestMethod("POST");
+ connection.setDoInput(true);
+ connection.setDoOutput(true);
+
+ connection.getOutputStream().write(("{\"uuid\":\""+profile.getId().toString()+"\",\"nickname\":\""+profile.getName()+"\"}").getBytes());
+ String payload = String.join("\n", IOUtils.readLines(connection.getErrorStream() == null ? connection.getInputStream() : connection.getErrorStream()));
+
+ JsonObject json = (JsonObject) new JsonParser().parse(payload);
+
+ if (!"ok".equals(json.get("status").getAsString())) {
+ return null;
+ }
+ return json.get("data").getAsString();
+ }
+
+ public static void checkSessionAuthenticity(String tempToken) throws NoSuchAlgorithmException, AuthenticationException {
+ JsonObject d = getJwtPayload(tempToken);
+ byte[] sharedSecret = Base64.decodeBase64(d.get("sharedSecret").getAsString());
+ byte[] publicKey =Base64.decodeBase64(d.get("publicKey").getAsString());
+ String hash = calculateServerHash(sharedSecret, publicKey);
+
+ Session session = Minecraft.getMinecraft().getSession();
+ MinecraftSessionService yggdrasilMinecraftSessionService = Minecraft.getMinecraft().getSessionService();
+ yggdrasilMinecraftSessionService.joinServer(session.getProfile(), session.getToken(), hash);
+ }
+
+ public static String verifyAuth(String tempToken, PublicKey clientKey, String baseurl) throws IOException {
+ HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(baseurl + "/auth/authenticate").openConnection();
+ urlConnection.setRequestMethod("POST");
+ urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
+ urlConnection.setRequestProperty("Content-Type", "application/json");
+ urlConnection.setDoInput(true);
+ urlConnection.setDoOutput(true);
+
+ urlConnection.getOutputStream().write(("{\"jwt\":\""+tempToken+"\",\"publicKey\":\""+Base64.encodeBase64URLSafeString(clientKey.getEncoded())+"\"}").getBytes());
+ String payload = String.join("\n", IOUtils.readLines(urlConnection.getErrorStream() == null ? urlConnection.getInputStream() : urlConnection.getErrorStream()));
+
+ JsonObject jsonObject = (JsonObject) new JsonParser().parse(payload);
+ if (!"ok".equals(jsonObject.get("status").getAsString())) {
+ return null;
+ }
+ return jsonObject.get("data").getAsString();
+ }
+
+ public static JsonObject getJwtPayload(String jwt) {
+ String midPart = jwt.split("\\.")[1].replace("+", "-").replace("/", "_");
+ String base64Decode = new String(Base64.decodeBase64(midPart)); // padding
+ return (JsonObject) new JsonParser().parse(base64Decode);
+ }
+
+ public static String calculateServerHash(byte[] a, byte[] b) throws NoSuchAlgorithmException {
+ MessageDigest c = MessageDigest.getInstance("SHA-1");
+ c.update("".getBytes());
+ c.update(a);
+ c.update(b);
+ byte[] d = c.digest();
+ return new BigInteger(d).toString(16);
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/NullAuth.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/NullAuth.java
new file mode 100644
index 00000000..303cfb0a
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/authprovider/NullAuth.java
@@ -0,0 +1,62 @@
+package kr.syeyoung.dungeonsguide.launcher.auth.authprovider;
+
+import com.mojang.authlib.exceptions.AuthenticationException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+public class NullAuth implements AuthProvider {
+
+ Logger logger = LogManager.getLogger("NullAuth");
+
+ @Override
+ public String getToken() {
+ return "TOKEN";
+ }
+
+ @Override
+ public KeyPair getRsaKey() {
+ return new KeyPair(new PublicKey() {
+ @Override
+ public String getAlgorithm() {
+ return null;
+ }
+
+ @Override
+ public String getFormat() {
+ return null;
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return new byte[0];
+ }
+ }, new PrivateKey() {
+ @Override
+ public String getAlgorithm() {
+ return null;
+ }
+
+ @Override
+ public String getFormat() {
+ return null;
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return new byte[0];
+ }
+ });
+ }
+
+ @Override
+ public AuthProvider createAuthProvider() throws NoSuchAlgorithmException, AuthenticationException, IOException {
+ return new NullAuth();
+ }
+
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/Authenticator.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/Authenticator.java
new file mode 100755
index 00000000..ac30c5e3
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/Authenticator.java
@@ -0,0 +1,257 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.authentication;
+
+import com.mojang.authlib.exceptions.AuthenticationException;
+import com.mojang.authlib.minecraft.MinecraftSessionService;
+import kr.syeyoung.dungeonsguide.launcher.Main;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.AuthServerException;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.PrivacyPolicyRequiredException;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.TokenExpiredException;
+import lombok.Getter;
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.Session;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+import org.json.JSONObject;
+import sun.reflect.Reflection;
+
+import javax.crypto.*;
+import java.io.*;
+import java.math.BigInteger;
+import java.net.*;
+import java.security.*;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.time.Instant;
+import java.util.UUID;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class Authenticator {
+ private String dgAccessToken;
+ @Getter
+ private TokenStatus tokenStatus = TokenStatus.UNAUTHENTICATED;
+
+ private final SecureRandom secureRandom = new SecureRandom();
+
+ private Lock authenticationLock = new ReentrantLock();
+
+ static {
+ Reflection.registerFieldsToFilter(Authenticator.class, "dgAccessToken"); // Please do not touch this field. I know there is a way to block it completely, but I won't do it here.
+ }
+
+ public String getRawToken() {
+ return dgAccessToken;
+ }
+ public String getUnexpiredToken() { // THIS METHOD MAY BLOCK.
+ if (tokenStatus != TokenStatus.AUTHENTICATED) throw new IllegalStateException("Token is not available");
+ long expiry = getJwtPayload(dgAccessToken).getLong("exp");
+ if (System.currentTimeMillis() >= expiry-2000 || tokenStatus == TokenStatus.EXPIRED) {
+ tokenStatus = TokenStatus.EXPIRED;
+ try {
+ repeatAuthenticate(5);
+ } catch (Throwable t) {
+ Main.getMain().setLastFatalError(t);
+ throw new TokenExpiredException(t);
+ }
+ }
+ return dgAccessToken;
+ }
+
+
+ private byte[] generateSharedSecret() {
+ byte[] bts = new byte[32];
+ secureRandom.nextBytes(bts);
+ return bts;
+ }
+
+ public String repeatAuthenticate(int tries) {
+ int cnt = 0;
+ while(true) {
+ try {
+ reauthenticate();
+ break;
+ } catch (IOException | AuthenticationException | NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ if (cnt == tries) throw new RuntimeException(e);
+ try {
+ Thread.sleep((long) Math.max(Math.pow(2, tries)* 100, 1000 * 10));
+ } catch (InterruptedException ex) {}
+ }
+ cnt++;
+ }
+ return dgAccessToken;
+ }
+ public String reauthenticate() throws IOException, AuthenticationException, NoSuchAlgorithmException {
+ try {
+ authenticationLock.lock();
+
+ tokenStatus = TokenStatus.UNAUTHENTICATED;
+ dgAccessToken = null;
+
+ MinecraftSessionService yggdrasilMinecraftSessionService = Minecraft.getMinecraft().getSessionService();
+
+ Session SECURE_USER_SESSION = Minecraft.getMinecraft().getSession();
+ dgAccessToken = requestAuth(SECURE_USER_SESSION.getProfile().getId(), SECURE_USER_SESSION.getProfile().getName()); // id: uuid, name: username
+
+ JSONObject d = getJwtPayload(dgAccessToken);
+ byte[] sharedSecret = generateSharedSecret(); // Notice.... shared secret is generated on the client side unlike dg 3.0. Yep, I was a stupid when making 3.0.
+
+ String hash = calculateServerHash(sharedSecret, Base64.decodeBase64(d.getString("publicKey"))); // Public Key here is server's public key.
+
+ byte[] encodedSharedSecret;
+ try {
+ Cipher cipher = Cipher.getInstance("RSA");
+ cipher.init(Cipher.ENCRYPT_MODE, KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(d.getString("publicKey")))));
+ encodedSharedSecret = cipher.doFinal(sharedSecret);
+ } catch (NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException |
+ InvalidKeySpecException |
+ InvalidKeyException e) {
+ throw new RuntimeException(e);
+ } // Server connection is SSL but I still encrypt it using publicKey. Additional layer of security considering the request goes through cloudflare. (it's not like I don't trust cloudflare, but idk)
+
+ yggdrasilMinecraftSessionService.joinServer(SECURE_USER_SESSION.getProfile(), SECURE_USER_SESSION.getToken(), hash); // Sent to "MOJANG" Server.
+
+ JSONObject furtherStuff = verifyAuth(dgAccessToken, encodedSharedSecret);
+
+ dgAccessToken = furtherStuff.getString("jwt");
+ if ("TOS_PRIVACY_POLICY_ACCEPT_REQUIRED".equals(furtherStuff.getString("result"))) {
+ tokenStatus = TokenStatus.PP_REQUIRED;
+ throw new PrivacyPolicyRequiredException();
+ }
+ tokenStatus = TokenStatus.AUTHENTICATED;
+ return this.dgAccessToken;
+ } finally {
+ authenticationLock.unlock();
+ }
+ }
+
+ public String acceptLatestTOS() throws IOException {
+ try {
+ authenticationLock.lock();
+ if (tokenStatus != TokenStatus.PP_REQUIRED) throw new IllegalStateException("Already accepted TOS");
+ JSONObject furtherStuff = acceptPrivacyPolicy(dgAccessToken);
+ dgAccessToken = furtherStuff.getString("jwt");
+ if ("TOS_PRIVACY_POLICY_ACCEPT_REQUIRED".equals(furtherStuff.getString("result"))) {
+ tokenStatus = TokenStatus.PP_REQUIRED;
+ throw new PrivacyPolicyRequiredException();
+ }
+ tokenStatus = TokenStatus.AUTHENTICATED;
+ return this.dgAccessToken;
+ } finally {
+ authenticationLock.unlock();
+ }
+ }
+
+ public JSONObject getJwtPayload(String jwt) {
+ String midPart = jwt.split("\\.")[1].replace("+", "-").replace("/", "_");
+ String base64Decode = new String(Base64.decodeBase64(midPart)); // padding
+ return new JSONObject(base64Decode);
+ }
+
+ private String requestAuth(UUID uuid, String nickname) throws IOException {
+ HttpURLConnection urlConnection = request("POST", "/auth/v2/requestAuth");
+ urlConnection.setRequestProperty("Content-Type", "application/json");
+
+ urlConnection.getOutputStream().write(("{\"uuid\":\""+uuid.toString()+"\",\"nickname\":\""+nickname+"\"}").getBytes());
+ try (InputStream is = obtainInputStream(urlConnection)) {
+ String payload = String.join("\n", IOUtils.readLines(is));
+ if (urlConnection.getResponseCode() != 200)
+ System.out.println("/auth/requestAuth :: Received " + urlConnection.getResponseCode() + " along with\n" + payload);
+
+ JSONObject json = new JSONObject(payload);
+
+ if ("Success".equals(json.getString("status"))) {
+ return json.getString("data");
+ } else {
+ throw new AuthServerException(json);
+ }
+ }
+ }
+ private JSONObject verifyAuth(String tempToken, byte[] encryptedSecret) throws IOException {
+ HttpURLConnection urlConnection = request("POST", "/auth/v2/authenticate");
+ urlConnection.setRequestProperty("Content-Type", "application/json");
+
+ urlConnection.getOutputStream().write(("{\"jwt\":\""+tempToken+"\",\"sharedSecret\":\""+Base64.encodeBase64String(encryptedSecret)+"\"}").getBytes());
+ try (InputStream is = obtainInputStream(urlConnection)) {
+ String payload = String.join("\n", IOUtils.readLines(is));
+ if (urlConnection.getResponseCode() != 200)
+ System.out.println("/auth/authenticate :: Received " + urlConnection.getResponseCode() + " along with\n" + payload);
+
+ JSONObject json = new JSONObject(payload);
+
+ if ("Success".equals(json.getString("status"))) {
+ return json.getJSONObject("data");
+ } else {
+ throw new AuthServerException(json);
+ }
+ }
+ }
+ private JSONObject acceptPrivacyPolicy(String tempToken) throws IOException {
+ HttpURLConnection urlConnection = request("POST", "/auth/v2/acceptPrivacyPolicy");
+
+ urlConnection.getOutputStream().write(tempToken.getBytes());
+ try (InputStream is = obtainInputStream(urlConnection)) {
+ String payload = String.join("\n", IOUtils.readLines(is));
+ if (urlConnection.getResponseCode() != 200)
+ System.out.println("/auth/authenticate :: Received " + urlConnection.getResponseCode() + " along with\n" + payload);
+
+ JSONObject json = new JSONObject(payload);
+
+ if ("Success".equals(json.getString("status"))) {
+ return json.getJSONObject("data");
+ } else {
+ throw new AuthServerException(json);
+ }
+ }
+ }
+
+
+ private String calculateServerHash(byte[] a, byte[] b) throws NoSuchAlgorithmException {
+ MessageDigest c = MessageDigest.getInstance("SHA-1");
+ c.update("".getBytes());
+ c.update(a);
+ c.update(b);
+ byte[] d = c.digest();
+ return new BigInteger(d).toString(16);
+ }
+
+ public InputStream obtainInputStream(HttpURLConnection huc) {
+ InputStream inputStream = null;
+ try {
+ inputStream = huc.getInputStream();
+ } catch (Exception e) {
+ inputStream = huc.getErrorStream();
+ }
+ return inputStream;
+ }
+
+ public HttpURLConnection request(String method, String url) throws IOException {
+ HttpURLConnection urlConnection = (HttpURLConnection) new URL(Main.DOMAIN+url).openConnection();
+ urlConnection.setRequestMethod(method); // TODO: setup SSL certificate here, because.... SOME PEOPLE HAVE THAT ISSUE, I HAVE NO IDEA WHY THEY DONT HAVE CLOUDFLARE CERTS INSTALLED ON THEM
+ urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
+ urlConnection.setDoInput(true);
+ urlConnection.setDoOutput(true);
+ urlConnection.setAllowUserInteraction(true);
+ if (tokenStatus == TokenStatus.AUTHENTICATED)
+ urlConnection.setRequestProperty("Authorization", "Bearer "+getUnexpiredToken());
+ return urlConnection;
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/TokenStatus.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/TokenStatus.java
new file mode 100644
index 00000000..a83818b8
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/TokenStatus.java
@@ -0,0 +1,27 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.authentication;
+
+public enum TokenStatus {
+ UNAUTHENTICATED,
+ BANNED,
+ PP_REQUIRED,
+ AUTHENTICATED,
+ EXPIRED
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/Update.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/Update.java
new file mode 100644
index 00000000..565ac5aa
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/Update.java
@@ -0,0 +1,46 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.branch;
+
+import lombok.Data;
+import org.json.JSONObject;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@Data
+public class Update {
+ private long id;
+ private long branchId;
+ private String name;
+ private String updateLog;
+ private JSONObject metadata;
+ private Instant releaseDate;
+ private List<Asset> assets = new ArrayList<>();
+
+ @Data
+ public static class Asset {
+ private String name;
+ private long size;
+ private String objectId;
+ private UUID assetId;
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateBranch.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateBranch.java
new file mode 100644
index 00000000..f0b05ee4
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateBranch.java
@@ -0,0 +1,29 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.branch;
+
+import lombok.Data;
+import org.json.JSONObject;
+
+@Data
+public class UpdateBranch {
+ private Long id;
+ private String name;
+ private JSONObject metadata;
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/AuthServerException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/AuthServerException.java
new file mode 100644
index 00000000..be610c3f
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/AuthServerException.java
@@ -0,0 +1,34 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.exceptions;
+
+import org.json.JSONObject;
+
+public class AuthServerException extends RuntimeException {
+ private JSONObject payload;
+
+ public AuthServerException(JSONObject payload) {
+ super(payload.getString("errorMessage"));
+ this.payload = payload;
+ }
+
+ public String getQRCode() {
+ return payload.getString("data");
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/NoSuitableLoaderFoundException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/NoSuitableLoaderFoundException.java
new file mode 100644
index 00000000..c2f9a907
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/NoSuitableLoaderFoundException.java
@@ -0,0 +1,32 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.exceptions;
+
+import lombok.AllArgsConstructor;
+
+public class NoSuitableLoaderFoundException extends RuntimeException {
+ private String jvmFlag;
+ private String configuration;
+
+ public NoSuitableLoaderFoundException(String jvmFlag, String configuration) {
+ super("No suitable loader found: (flag: "+jvmFlag+" / config: "+configuration+")");
+ this.jvmFlag = jvmFlag;
+ this.configuration = configuration;
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/PrivacyPolicyRequiredException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/PrivacyPolicyRequiredException.java
new file mode 100644
index 00000000..006e03ca
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/PrivacyPolicyRequiredException.java
@@ -0,0 +1,22 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.exceptions;
+
+public class PrivacyPolicyRequiredException extends RuntimeException{
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/ReferenceLeakedException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/ReferenceLeakedException.java
new file mode 100644
index 00000000..3a88b862
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/ReferenceLeakedException.java
@@ -0,0 +1,22 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.exceptions;
+
+public class ReferenceLeakedException extends Exception {
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/TokenExpiredException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/TokenExpiredException.java
new file mode 100644
index 00000000..2c115e2f
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/TokenExpiredException.java
@@ -0,0 +1,26 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.exceptions;
+
+public class TokenExpiredException extends RuntimeException{
+
+ public TokenExpiredException(Throwable parent) {
+ super(parent);
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/GuiLoadingError.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/GuiLoadingError.java
new file mode 100644
index 00000000..f699b1d7
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/GuiLoadingError.java
@@ -0,0 +1,90 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.gui;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.*;
+import net.minecraftforge.fml.common.FMLCommonHandler;
+import org.lwjgl.opengl.GL11;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class GuiLoadingError extends GuiScreen {
+ static Throwable cause;
+ private final String stacktrace;
+ private final GuiScreen originalGUI;
+ public GuiLoadingError(GuiScreen originalGUI) {
+
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(byteArrayOutputStream);
+ cause.printStackTrace(printStream);
+ this.stacktrace = byteArrayOutputStream.toString();
+
+ this.originalGUI = originalGUI;
+ }
+
+ @Override
+ public void initGui() {
+ ScaledResolution sr = new ScaledResolution(Minecraft.getMinecraft());
+ this.buttonList.add(new GuiButton(0, sr.getScaledWidth()/2-100,sr.getScaledHeight()-70 ,"Close Minecraft"));
+ this.buttonList.add(new GuiButton(1, sr.getScaledWidth()/2-100,sr.getScaledHeight()-40 ,"Play Without DG"));
+ }
+
+ @Override
+ protected void actionPerformed(GuiButton button) throws IOException {
+ super.actionPerformed(button);
+ if (button.id == 0) {
+ FMLCommonHandler.instance().exitJava(-1,true);
+ } else if (button.id == 1) {
+ Minecraft.getMinecraft().displayGuiScreen(originalGUI);
+ }
+ }
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ super.drawBackground(1);
+
+ ScaledResolution sr = new ScaledResolution(Minecraft.getMinecraft());
+ FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj;
+ fontRenderer.drawString("DungeonsGuide has ran into error while loading itself", (sr.getScaledWidth()-fontRenderer.getStringWidth("DungeonsGuide has ran into error while loading itself"))/2,40,0xFFFF0000);
+ fontRenderer.drawString("Please contact DungeonsGuide support with this screen", (sr.getScaledWidth()-fontRenderer.getStringWidth("Please contact developer with this screen"))/2, (int) (40+fontRenderer.FONT_HEIGHT*1.5),0xFFFF0000);
+
+ int tenth = sr.getScaledWidth() / 10;
+
+ Gui.drawRect(tenth, 70,sr.getScaledWidth()-tenth, sr.getScaledHeight()-80, 0xFF5B5B5B);
+ String[] split = stacktrace.split("\n");
+ clip(sr, tenth, 70,sr.getScaledWidth()-2*tenth, sr.getScaledHeight()-150);
+ GL11.glEnable(GL11.GL_SCISSOR_TEST);
+ for (int i = 0; i < split.length; i++) {
+ fontRenderer.drawString(split[i].replace("\t", " "), tenth+2,i*fontRenderer.FONT_HEIGHT + 72, 0xFFFFFFFF);
+ }
+ GL11.glDisable(GL11.GL_SCISSOR_TEST);
+
+ super.drawScreen(mouseX, mouseY, partialTicks);
+ }
+
+ public static void clip(ScaledResolution resolution, int x, int y, int width, int height) {
+ if (width < 0 || height < 0) return;
+
+ int scale = resolution.getScaleFactor();
+ GL11.glScissor((x ) * scale, Minecraft.getMinecraft().displayHeight - (y + height) * scale, (width) * scale, height * scale);
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/GuiPrivacyPolicy.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/GuiPrivacyPolicy.java
new file mode 100644
index 00000000..b7d21b16
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/gui/GuiPrivacyPolicy.java
@@ -0,0 +1,53 @@
+package kr.syeyoung.dungeonsguide.launcher.gui;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.*;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.WorldRenderer;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import net.minecraftforge.fml.common.FMLCommonHandler;
+import org.lwjgl.opengl.GL11;
+
+import java.io.IOException;
+
+public class GuiPrivacyPolicy extends GuiScreen {
+ @Override
+ public void initGui() {
+ ScaledResolution sr = new ScaledResolution(Minecraft.getMinecraft());
+ this.buttonList.add(new GuiButton(0, sr.getScaledWidth()/2+50,sr.getScaledHeight()-40, 300, 20,"Accept Privacy Policy"));
+ this.buttonList.add(new GuiButton(1, sr.getScaledWidth()/2-50-300,sr.getScaledHeight()-40, 300, 20,"Deny and Play Without DG"));
+ }
+
+
+ @Override
+ protected void actionPerformed(GuiButton button) throws IOException {
+ super.actionPerformed(button);
+ if (button.id == 0) {
+ // accept
+ } else if (button.id == 1) {
+ Minecraft.getMinecraft().displayGuiScreen(null);
+ }
+ }
+
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ super.drawBackground(0);
+
+ ScaledResolution sr = new ScaledResolution(Minecraft.getMinecraft());
+ FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj;
+
+ fontRenderer.drawString("", (sr.getScaledWidth()-fontRenderer.getStringWidth("Please accept or deny Dungeons Guide Privacy Policy to continue"))/2,40,0xFFFF0000);
+
+
+ super.drawScreen(mouseX, mouseY, partialTicks);
+ }
+
+ public static void clip(ScaledResolution resolution, int x, int y, int width, int height) {
+ if (width < 0 || height < 0) return;
+
+ int scale = resolution.getScaleFactor();
+ GL11.glScissor((x ) * scale, Minecraft.getMinecraft().displayHeight - (y + height) * scale, (width) * scale, height * scale);
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java
new file mode 100644
index 00000000..53d9b70d
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java
@@ -0,0 +1,39 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.loader;
+
+import kr.syeyoung.dungeonsguide.launcher.DGInterface;
+import kr.syeyoung.dungeonsguide.launcher.authentication.Authenticator;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException;
+import net.minecraftforge.common.config.Configuration;
+
+import java.io.InputStream;
+
+public interface IDGLoader {
+ void loadJar(Authenticator authenticator) throws InstantiationException, IllegalAccessException, ClassNotFoundException;
+ DGInterface getInstance();
+ void unloadJar() throws ReferenceLeakedException;
+
+ boolean isUnloadable();
+
+ boolean isLoaded();
+
+ String strategyName();
+ String version();
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java
new file mode 100644
index 00000000..0b6cf124
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java
@@ -0,0 +1,134 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.loader;
+
+import kr.syeyoung.dungeonsguide.launcher.DGInterface;
+import kr.syeyoung.dungeonsguide.launcher.Main;
+import kr.syeyoung.dungeonsguide.launcher.authentication.Authenticator;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException;
+
+import java.io.InputStream;
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public class JarLoader implements IDGLoader {
+ private DGInterface dgInterface;
+ private ReferenceQueue<ClassLoader> refQueue = new ReferenceQueue<>();
+ private PhantomReference<ClassLoader> phantomReference;
+
+ private boolean loaded;
+
+ public static class JarClassLoader extends URLClassLoader {
+ public JarClassLoader(URL[] urls, ClassLoader parent) {
+ super(urls, parent);
+ }
+
+ @Override
+ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+
+ synchronized (getClassLoadingLock(name)) {
+ Class<?> c = findLoadedClass(name);
+ if (c == null) {
+
+ try {
+ if (c == null) {
+ long t0 = System.nanoTime();
+ c = findClass(name);
+
+ sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t0);
+ sun.misc.PerfCounter.getFindClasses().increment();
+ }
+ } catch (ClassNotFoundException e) {
+ // ClassNotFoundException thrown if class not found
+ // from the non-null parent class loader
+ }
+ if (getParent() != null && c == null) {
+ long t0 = System.nanoTime();
+ c = getParent().loadClass(name);
+ long t1 = System.nanoTime();
+ sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
+ }
+ }
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+ }
+
+ public Class<?> loadClassResolve(String name, boolean resolve) throws ClassNotFoundException {
+ return this.loadClass(name, resolve);
+ }
+ }
+
+ private JarClassLoader classLoader;
+
+ @Override
+ public void loadJar(Authenticator authenticator) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+ if (dgInterface != null) throw new IllegalStateException("Already loaded");
+
+ classLoader = new JarClassLoader(new URL[] {
+ Main.class.getResource("/mod.jar")
+ }, this.getClass().getClassLoader());
+
+ dgInterface = (DGInterface) classLoader.loadClassResolve("kr.syeyoung.dungeonsguide.DungeonsGuide", true).newInstance();
+ phantomReference = new PhantomReference<>(classLoader, refQueue);
+ }
+
+ @Override
+ public DGInterface getInstance() {
+ return dgInterface;
+ }
+
+ @Override
+ public void unloadJar() throws ReferenceLeakedException {
+ classLoader = null;
+ dgInterface.unload();
+ dgInterface = null;
+ System.gc();// pls do
+ Reference<? extends ClassLoader> t = refQueue.poll();
+ if (t == null) throw new ReferenceLeakedException(); // Why do you have to be that strict? Well, to tell them to actually listen on DungeonsGuideReloadListener. If it starts causing issues then I will remove check cus it's not really loaded (classes are loaded by child classloader)
+ t.clear();
+ phantomReference = null;
+ }
+
+ @Override
+ public boolean isUnloadable() {
+ return true;
+ }
+
+ @Override
+ public boolean isLoaded() {
+ return dgInterface != null;
+ }
+
+ @Override
+ public String strategyName() {
+ return "jar";
+ }
+
+ @Override
+ public String version() {
+ return "unknown"; // maybe read the thing...
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java
new file mode 100644
index 00000000..1338138d
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java
@@ -0,0 +1,64 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.loader;
+
+import kr.syeyoung.dungeonsguide.launcher.DGInterface;
+import kr.syeyoung.dungeonsguide.launcher.authentication.Authenticator;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException;
+
+import java.io.InputStream;
+
+public class LocalLoader implements IDGLoader {
+ private DGInterface dgInterface;
+
+ @Override
+ public void loadJar(Authenticator authenticator) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+ if (dgInterface != null) throw new IllegalStateException("Already loaded");
+ dgInterface = (DGInterface) Class.forName("kr.syeyoung.dungeonsguide.DungeonsGuide").newInstance();
+ }
+
+ @Override
+ public DGInterface getInstance() {
+ return dgInterface;
+ }
+
+ @Override
+ public void unloadJar() throws ReferenceLeakedException {
+ throw new UnsupportedOperationException();
+ }
+ @Override
+ public boolean isUnloadable() {
+ return false;
+ }
+
+ @Override
+ public boolean isLoaded() {
+ return dgInterface != null;
+ }
+
+ @Override
+ public String strategyName() {
+ return "local";
+ }
+
+ @Override
+ public String version() {
+ return "unknown";
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGConnection.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGConnection.java
new file mode 100755
index 00000000..5a310738
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGConnection.java
@@ -0,0 +1,49 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.url;
+
+import kr.syeyoung.dungeonsguide.launcher.auth.ResourceManager;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+public class DGConnection extends URLConnection {
+
+ protected DGConnection(URL url) {
+ super(url);
+ connected = false;
+ }
+
+ @Override
+ public void connect() throws IOException {
+ }
+ @Override
+ public InputStream getInputStream() throws IOException {
+ if (ResourceManager.getInstance().getResources() != null) {
+ String path = url.getPath().substring(1);
+ if (!ResourceManager.getInstance().getResources().containsKey(path)) throw new FileNotFoundException();
+ return new ByteArrayInputStream(ResourceManager.getInstance().getResources().get(path));
+ }
+ throw new FileNotFoundException();
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGStreamHandler.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGStreamHandler.java
new file mode 100755
index 00000000..456e7f8c
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGStreamHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.url;
+
+import lombok.AllArgsConstructor;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+@AllArgsConstructor
+public class DGStreamHandler extends URLStreamHandler {
+ @Override
+ protected URLConnection openConnection(URL url) throws IOException {
+ return new DGConnection(url);
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGStreamHandlerFactory.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGStreamHandlerFactory.java
new file mode 100755
index 00000000..a7eefa37
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/url/DGStreamHandlerFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.url;
+
+import lombok.AllArgsConstructor;
+
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+
+@AllArgsConstructor
+public class DGStreamHandlerFactory implements URLStreamHandlerFactory {
+ @Override
+ public URLStreamHandler createURLStreamHandler(String protocol) {
+ if ("z".equals(protocol)) {
+ return new DGStreamHandler();
+ }
+ return null;
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/QRCodeGenerator.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/QRCodeGenerator.java
new file mode 100644
index 00000000..d0c3306a
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/QRCodeGenerator.java
@@ -0,0 +1,43 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2021 cyoung06
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.launcher.util;
+
+import io.nayuki.qrcodegen.QrCode;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.GZIPOutputStream;
+
+public class QRCodeGenerator {
+ public static BufferedImage generateQRCode(byte[] bytes) throws IOException {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
+ gzipOutputStream.write(bytes);
+ gzipOutputStream.flush();
+ gzipOutputStream.close();
+
+
+ byte[] array = byteArrayOutputStream.toByteArray();
+
+ QrCode qrCode = QrCode.encodeBinary(array, QrCode.Ecc.LOW);
+ return qrCode.toImage(1, 0);
+ }
+}
diff --git a/loader/src/main/resources/mcmod.info b/loader/src/main/resources/mcmod.info
new file mode 100755
index 00000000..2e1a5e46
--- /dev/null
+++ b/loader/src/main/resources/mcmod.info
@@ -0,0 +1,16 @@
+[
+{
+ "modid": "skyblock_dungeons_guide",
+ "name": "Skyblock Dungeons Guide",
+ "description": "A mod to help dungeon players to find and solve secrets and puzzles most efficiently.",
+ "version": "${version}",
+ "mcversion": "1.8.9",
+ "url": "https://discord.gg/dg",
+ "updateUrl": "",
+ "authorList": ["syeyoung", "kokoniara"],
+ "credits": "The guild Jerry's Crew, for nothing.",
+ "logoFile": "",
+ "screenshots": [],
+ "dependencies": []
+}
+]