diff options
Diffstat (limited to 'loader/src/main/java/kr')
22 files changed, 1707 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/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/ModDownloader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/ModDownloader.java new file mode 100644 index 00000000..45eacee5 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/ModDownloader.java @@ -0,0 +1,302 @@ +/* + * 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 com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import kr.syeyoung.dungeonsguide.launcher.authentication.Authenticator; +import lombok.Getter; +import net.minecraftforge.fml.common.ProgressManager; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.IOUtils; +import org.json.JSONArray; +import org.json.JSONObject; + +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.HttpsURLConnection; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.*; +import java.security.cert.CertificateException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class ModDownloader { + private Authenticator authenticator; + + @Getter + private List<UpdateBranch> accessibleBranches = null; + + public ModDownloader(Authenticator authenticator) { + this.au |
