aboutsummaryrefslogtreecommitdiff
path: root/loader/src
diff options
context:
space:
mode:
authorsyeyoung <cyoung06@naver.com>2022-11-16 17:45:55 +0900
committersyeyoung <cyoung06@naver.com>2022-11-16 17:59:56 +0900
commit60032db80aefe4a9f58b354b5a26938ed76f5c46 (patch)
tree17a7b6e873db2ea3c40aef508134842ce69c5991 /loader/src
parent3708965c0c22c216336f8aa28158ba22bfc03b60 (diff)
parent241893934ef119566693165589fce0921c35e4af (diff)
downloadSkyblock-Dungeons-Guide-60032db80aefe4a9f58b354b5a26938ed76f5c46.tar.gz
Skyblock-Dungeons-Guide-60032db80aefe4a9f58b354b5a26938ed76f5c46.tar.bz2
Skyblock-Dungeons-Guide-60032db80aefe4a9f58b354b5a26938ed76f5c46.zip
- Update Downloading and Signature Verification
Signed-off-by: syeyoung <cyoung06@naver.com>
Diffstat (limited to 'loader/src')
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java3
-rwxr-xr-xloader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java19
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java11
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java15
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateRetrieverUtil.java182
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/AssetNotFoundException.java35
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/InvalidSignatureException.java37
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/NoVersionFoundException.java38
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/ByteStreamURLHandler.java46
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/DGClassLoader.java81
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java4
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java70
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java54
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/RemoteLoader.java155
-rw-r--r--loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/SignatureValidator.java43
15 files changed, 681 insertions, 112 deletions
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java
index 7252a9db..a96805c1 100644
--- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java
@@ -19,6 +19,9 @@
package kr.syeyoung.dungeonsguide.launcher;
public interface DungeonsGuideReloadListener {
+ /**
+ * @implNote This is very important that you GET RID OF referene to DGInterface when this is called, or else dg is gonna crash with ReferenceLeakedException.
+ */
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
index 0ec65780..e8d3e36b 100755
--- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java
@@ -19,7 +19,9 @@
package kr.syeyoung.dungeonsguide.launcher;
import kr.syeyoung.dungeonsguide.launcher.auth.AuthManager;
+import kr.syeyoung.dungeonsguide.launcher.branch.UpdateRetrieverUtil;
import kr.syeyoung.dungeonsguide.launcher.exceptions.NoSuitableLoaderFoundException;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.NoVersionFoundException;
import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException;
import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiDisplayer;
import kr.syeyoung.dungeonsguide.launcher.gui.screen.GuiLoadingError;
@@ -28,6 +30,7 @@ import kr.syeyoung.dungeonsguide.launcher.gui.screen.SpecialGuiScreen;
import kr.syeyoung.dungeonsguide.launcher.loader.IDGLoader;
import kr.syeyoung.dungeonsguide.launcher.loader.JarLoader;
import kr.syeyoung.dungeonsguide.launcher.loader.LocalLoader;
+import kr.syeyoung.dungeonsguide.launcher.loader.RemoteLoader;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IReloadableResourceManager;
import net.minecraftforge.common.MinecraftForge;
@@ -96,7 +99,7 @@ public class Main
}
currentLoader = null;
}
- private void load(IDGLoader newLoader) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+ private void load(IDGLoader newLoader) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
if (dgInterface != null) throw new IllegalStateException("DG is loaded");
dgInterface = newLoader.loadDungeonsGuide();
currentLoader = newLoader;
@@ -157,7 +160,19 @@ public class Main
return new JarLoader();
} else if (loader.equals("auto") ){
// remote load
- throw new UnsupportedOperationException(""); // yet
+ String branch = System.getProperty("branch") == null ? configuration.get("loader", "remoteBranch", "$default").getString() : System.getProperty("branch");
+ String version = System.getProperty("version") == null ? configuration.get("loader", "remoteVersion", "latest").getString() : System.getProperty("version");
+ try {
+ UpdateRetrieverUtil.VersionInfo versionInfo = UpdateRetrieverUtil.getIds(
+ branch,
+ version
+ );
+ if (versionInfo == null) throw new NoVersionFoundException(branch, version);
+
+ return new RemoteLoader(versionInfo.getFriendlyBranchName(), versionInfo.getBranchId(), versionInfo.getUpdateId());
+ } catch (IOException e) {
+ throw new NoVersionFoundException(branch, version, e);
+ }
} else {
throw new NoSuitableLoaderFoundException(System.getProperty("dg.loader"), configuration.get("loader", "modsource", "auto").getString());
}
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
index 1613e687..c3dd747a 100644
--- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/AuthManager.java
@@ -3,6 +3,7 @@ package kr.syeyoung.dungeonsguide.launcher.auth;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.mojang.authlib.exceptions.AuthenticationException;
+import kr.syeyoung.dungeonsguide.launcher.Main;
import kr.syeyoung.dungeonsguide.launcher.auth.token.*;
import kr.syeyoung.dungeonsguide.launcher.events.AuthChangedEvent;
import kr.syeyoung.dungeonsguide.launcher.exceptions.AuthFailedExeption;
@@ -34,10 +35,6 @@ public class AuthManager {
if(INSTANCE == null) INSTANCE = new AuthManager();
return INSTANCE;
}
-
- @Setter
- private String baseserverurl = "https://dungeons.guide";
-
private AuthToken currentToken = new NullToken();
public AuthToken getToken() {
@@ -98,9 +95,9 @@ public class AuthManager {
reauthLock = true;
try {
- String token = DgAuthUtil.requestAuth(baseserverurl);
+ String token = DgAuthUtil.requestAuth();
byte[] encSecret = DgAuthUtil.checkSessionAuthenticityAndReturnEncryptedSecret(token);
- currentToken = DgAuthUtil.verifyAuth(token, encSecret, baseserverurl);
+ currentToken = DgAuthUtil.verifyAuth(token, encSecret);
MinecraftForge.EVENT_BUS.post(new AuthChangedEvent(currentToken));
if (currentToken instanceof PrivacyPolicyRequiredToken) {
@@ -130,7 +127,7 @@ public class AuthManager {
if (currentToken instanceof PrivacyPolicyRequiredToken) {
reauthLock = true;
try {
- currentToken = DgAuthUtil.acceptNewPrivacyPolicy(currentToken.getToken(), baseserverurl);
+ currentToken = DgAuthUtil.acceptNewPrivacyPolicy(currentToken.getToken());
if (currentToken instanceof PrivacyPolicyRequiredToken) throw new PrivacyPolicyRequiredException();
} catch (IOException e) {
currentToken = new FailedAuthToken(e);
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java
index f585ac20..78fc91c9 100644
--- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java
@@ -5,6 +5,7 @@ import com.google.gson.JsonParser;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.minecraft.MinecraftSessionService;
+import kr.syeyoung.dungeonsguide.launcher.Main;
import kr.syeyoung.dungeonsguide.launcher.auth.token.AuthToken;
import kr.syeyoung.dungeonsguide.launcher.auth.token.DGAuthToken;
import kr.syeyoung.dungeonsguide.launcher.auth.token.PrivacyPolicyRequiredToken;
@@ -52,14 +53,16 @@ public class DgAuthUtil {
);
} catch (Exception e) {
throw new ResponseParsingException(payload, e.getMessage());
+ } finally {
+ toRead.close();
}
}
- public static String requestAuth(String baseurl) throws IOException {
+ public static String requestAuth() throws IOException {
GameProfile profile = Minecraft.getMinecraft().getSession().getProfile();
- HttpsURLConnection connection = (HttpsURLConnection) new URL(baseurl + "/auth/requestAuth").openConnection();
+ HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/auth/requestAuth").openConnection();
connection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestMethod("POST");
@@ -96,8 +99,8 @@ public class DgAuthUtil {
return result;
}
- public static AuthToken verifyAuth(String tempToken, byte[] encSecret, String baseurl) throws IOException {
- HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(baseurl + "/auth/authenticate").openConnection();
+ public static AuthToken verifyAuth(String tempToken, byte[] encSecret) throws IOException {
+ HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(Main.DOMAIN + "/auth/authenticate").openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
urlConnection.setRequestProperty("Content-Type", "application/json");
@@ -120,8 +123,8 @@ public class DgAuthUtil {
}
}
- public static AuthToken acceptNewPrivacyPolicy(String tempToken, String baseurl) throws IOException {
- HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(baseurl + "/auth/acceptPrivacyPolicy").openConnection();
+ public static AuthToken acceptNewPrivacyPolicy(String tempToken) throws IOException {
+ HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(Main.DOMAIN + "/auth/acceptPrivacyPolicy").openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
urlConnection.setRequestProperty("Content-Type", "application/json");
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateRetrieverUtil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateRetrieverUtil.java
new file mode 100644
index 00000000..fe7e54dd
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateRetrieverUtil.java
@@ -0,0 +1,182 @@
+package kr.syeyoung.dungeonsguide.launcher.branch;
+
+import kr.syeyoung.dungeonsguide.launcher.Main;
+import kr.syeyoung.dungeonsguide.launcher.auth.DGResponse;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.AssetNotFoundException;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.NoVersionFoundException;
+import kr.syeyoung.dungeonsguide.launcher.exceptions.ResponseParsingException;
+import lombok.Builder;
+import lombok.Data;
+import org.apache.commons.io.IOUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import javax.net.ssl.HttpsURLConnection;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+public class UpdateRetrieverUtil {
+ private static String getResponse(HttpsURLConnection connection) throws IOException {
+ connection.getResponseCode();
+ InputStream toRead = connection.getErrorStream();
+ if (toRead == null)
+ toRead = connection.getInputStream();
+ String payload = IOUtils.readLines(toRead).stream().collect(Collectors.joining("\n"));
+ return payload;
+ }
+
+ public static List<UpdateBranch> getUpdateBranches() throws IOException {
+ HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/updates/").openConnection();
+ connection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
+ connection.setRequestProperty("Content-Type", "application/json");
+ connection.setRequestMethod("GET");
+ connection.setDoInput(true);
+ connection.setDoOutput(true);
+
+ JSONArray jsonArray = new JSONArray(getResponse(connection));
+ return jsonArray.toList()
+ .stream()
+ .map(a -> (JSONObject)a)
+ .map(a -> {
+ UpdateBranch updateBranch = new UpdateBranch();
+ updateBranch.setId(a.getLong("id"));
+ updateBranch.setName(a.getString("name"));
+ updateBranch.setMetadata(a.getJSONObject("metadata"));
+ return updateBranch;
+ }).collect(Collectors.toList());
+ }
+
+ public static List<Update> getLatestUpdates(long branchId, int page) throws IOException {
+ HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/updates/"+branchId+"/?page="+page).openConnection();
+ connection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
+ connection.setRequestMethod("GET");
+ connection.setDoInput(true);
+ connection.setDoOutput(true);
+
+ JSONArray jsonArray = new JSONArray(getResponse(connection));
+ return jsonArray.toList()
+ .stream()
+ .map(a -> (JSONObject)a)
+ .map(a -> {
+ Update update = new Update();
+ update.setId(a.getLong("id"));
+ update.setBranchId(a.getLong("branchId"));
+ update.setName(a.getString("name"));
+ update.setUpdateLog(a.getString("updateLog"));
+ update.setMetadata(a.getJSONObject("metadata"));
+ update.setAssets(a.getJSONObject("assets").getJSONArray("assets").toList().stream().map(b -> (JSONObject)b)
+ .map(b -> {
+ Update.Asset asset = new Update.Asset();
+ asset.setName(b.getString("name"));
+ asset.setAssetId(UUID.fromString(b.getString("assetId")));
+ asset.setSize(b.getLong("size"));
+ asset.setObjectId(b.getString("objectId"));
+ return asset;
+ }).collect(Collectors.toList()));
+ update.setReleaseDate(Instant.parse(a.getString("releaseDate")));
+ return update;
+ }).collect(Collectors.toList());
+ }
+
+ public static Update getUpdate(long branchId, long updateId) throws IOException {
+ HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/updates/"+branchId+"/"+updateId).openConnection();
+ connection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
+ connection.setRequestMethod("GET");
+ connection.setDoInput(true);
+ connection.setDoOutput(true);
+
+ JSONObject a = new JSONObject(getResponse(connection));
+
+ Update update = new Update();
+ update.setId(a.getLong("id"));
+ update.setBranchId(a.getLong("branchId"));
+ update.setName(a.getString("name"));
+ update.setUpdateLog(a.getString("updateLog"));
+ update.setMetadata(a.getJSONObject("metadata"));
+ update.setAssets(a.getJSONObject("assets").getJSONArray("assets").toList().stream().map(b -> (JSONObject)b)
+ .map(b -> {
+ Update.Asset asset = new Update.Asset();
+ asset.setName(b.getString("name"));
+ asset.setAssetId(UUID.fromString(b.getString("assetId")));
+ asset.setSize(b.getLong("size"));
+ asset.setObjectId(b.getString("objectId"));
+ return asset;
+ }).collect(Collectors.toList()));
+ update.setReleaseDate(Instant.parse(a.getString("releaseDate")));
+ return update;
+ }
+
+ public static InputStream downloadFile(Update update, String assetName) throws IOException {
+ Update.Asset asset = update.getAssets().stream().filter(a -> a.getName().equals(assetName))
+ .findFirst().orElseThrow(() -> new AssetNotFoundException(update.getBranchId()+"", update.getId()+"("+update.getName()+")", assetName));
+
+
+ HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/updates/"+update.getBranchId()+"/"+update.getId()+"/"+asset.getAssetId()).openConnection();
+ connection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
+ connection.setRequestMethod("GET");
+ connection.setDoInput(true);
+ connection.setDoOutput(true);
+
+ JSONObject result = new JSONObject(getResponse(connection));
+ String url = result.getString("url");
+ String method = result.getString("method");
+
+ connection = (HttpsURLConnection) new URL(url).openConnection();
+ connection.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
+ connection.setRequestMethod(method);
+ return connection.getInputStream();
+ }
+
+ @Data @Builder
+ public static class VersionInfo {
+ String friendlyBranchName = "";
+ long branchId;
+ String friendlyVersionName = "";
+ long updateId;
+ }
+ public static VersionInfo getIds(String branch, String version) throws IOException {
+ long branchId = -1, updateId = -1;
+ UpdateBranch branch1 = null;
+ for (UpdateBranch updateBranch : UpdateRetrieverUtil.getUpdateBranches()) {
+ if (updateBranch.getName().equals(branch) || (branch.equals("$default") &&
+ Optional.ofNullable(updateBranch.getMetadata())
+ .map(a -> a.getJSONObject("additionalMeta"))
+ .map(a -> a.getBoolean("defaultMod")).orElse(false))) {
+ branchId = updateBranch.getId();
+ branch1 = updateBranch;
+ break;
+ }
+ }
+ if (branchId == -1) return null;
+
+ Update target = null;
+ int page = 0;
+ while (updateId == -1) {
+ List<Update> updateList = UpdateRetrieverUtil.getLatestUpdates(branchId, page++);
+ if (updateList == null || updateList.isEmpty()) return null;
+ for (Update update : updateList) {
+ if (update.getName().equals(version) || version.equals("latest")) { // if latest, get the first one.
+ updateId = update.getId();
+ target = update;
+ break;
+ }
+ }
+ }
+
+
+ return VersionInfo.builder()
+ .branchId(branchId)
+ .updateId(updateId)
+ .friendlyBranchName(branch1.getName())
+ .friendlyVersionName(target.getName())
+ .build();
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/AssetNotFoundException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/AssetNotFoundException.java
new file mode 100644
index 00000000..d04608e4
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/AssetNotFoundException.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.exceptions;
+
+import lombok.Getter;
+
+@Getter
+public class AssetNotFoundException extends RuntimeException {
+ private String branch;
+ private String version;
+ private String asset;
+
+ public AssetNotFoundException(String branch, String version, String asset) {
+ super("No asset found: "+branch+" - "+version+" - "+asset);
+ this.branch = branch;
+ this.version = version;
+ this.asset = asset;
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/InvalidSignatureException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/InvalidSignatureException.java
new file mode 100644
index 00000000..bda43ea4
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/InvalidSignatureException.java
@@ -0,0 +1,37 @@
+/*
+ * 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 kr.syeyoung.dungeonsguide.launcher.branch.Update;
+import lombok.Getter;
+
+@Getter
+public class InvalidSignatureException extends RuntimeException {
+ private Update update;
+
+ public InvalidSignatureException(Update update, String message) {
+ super(message);
+ this.update = update;
+ }
+
+ public InvalidSignatureException(Update update, Throwable t) {
+ super(t);
+ this.update = update;
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/NoVersionFoundException.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/NoVersionFoundException.java
new file mode 100644
index 00000000..b99d12c1
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/exceptions/NoVersionFoundException.java
@@ -0,0 +1,38 @@
+/*
+ * 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.Getter;
+
+@Getter
+public class NoVersionFoundException extends RuntimeException {
+ private String branch;
+ private String version;
+
+ public NoVersionFoundException(String branch, String version) {
+ super("No version found: "+branch+" - "+version);
+ this.branch = branch;
+ this.version = version;
+ }
+ public NoVersionFoundException(String branch, String version, Throwable e) {
+ super("No version found: "+branch+" - "+version, e);
+ this.branch = branch;
+ this.version = version;
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/ByteStreamURLHandler.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/ByteStreamURLHandler.java
new file mode 100644
index 00000000..d0859076
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/ByteStreamURLHandler.java
@@ -0,0 +1,46 @@
+package kr.syeyoung.dungeonsguide.launcher.loader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+public class ByteStreamURLHandler extends URLStreamHandler {
+ private InputStreamGenerator converter;
+ public ByteStreamURLHandler(InputStreamGenerator converter) {
+ this.converter = converter;
+ }
+ public interface InputStreamGenerator {
+ InputStream convert(String name);
+ }
+
+ public class ByteStreamURLConnection extends URLConnection {
+
+ /**
+ * Constructs a URL connection to the specified URL. A connection to
+ * the object referenced by the URL is not created.
+ *
+ * @param url the specified URL.
+ */
+ protected ByteStreamURLConnection(URL url) {
+ super(url);
+ connected = false;
+ }
+
+ @Override
+ public void connect() throws IOException {
+ connected = true;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return converter.convert(url.getPath());
+ }
+ }
+
+ @Override
+ protected URLConnection openConnection(URL u) throws IOException {
+ return new ByteStreamURLConnection(u);
+ }
+}
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/DGClassLoader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/DGClassLoader.java
new file mode 100644
index 00000000..1fbc39b0
--- /dev/null
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/DGClassLoader.java
@@ -0,0 +1,81 @@
+package kr.syeyoung.dungeonsguide.launcher.loader;
+
+import sun.misc.Resource;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public abstract class DGClassLoader extends ClassLoader implements ByteStreamURLHandler.InputStreamGenerator{
+ public DGClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ public Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ synchronized (getClassLoadingLock(name)) {
+ // First, check if the class has already been loaded
+ 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;
+ }
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ byte[] res;
+ try {
+ res = getClassBytes(name);
+ } catch (IOException e) {
+ throw new ClassNotFoundException(name, e);
+ }
+ if (res != null) {
+ return defineClass(name, res, 0, res.length);
+ } else {
+ throw new ClassNotFoundException(name);
+ }
+ }
+
+ public abstract byte[] getClassBytes(String name) throws IOException;
+
+ public URL getResource(String name) {
+ URL url = findResource(name);
+ if (url == null && getParent() != null ) {
+ url = getParent().getResource(name);
+ }
+ return url;
+ }
+
+ private ByteStreamURLHandler urlHandler = new ByteStreamURLHandler(this);
+ @Override
+ public URL findResource(String name) {
+ try {
+ return new URL("dungeonsguide", "",0, name, urlHandler);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
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
index f5149f82..a79fdb7e 100644
--- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java
@@ -21,8 +21,10 @@ package kr.syeyoung.dungeonsguide.launcher.loader;
import kr.syeyoung.dungeonsguide.launcher.DGInterface;
import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException;
+import java.io.IOException;
+
public interface IDGLoader {
- DGInterface loadDungeonsGuide() throws InstantiationException, IllegalAccessException, ClassNotFoundException;
+ DGInterface loadDungeonsGuide() throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException;
DGInterface getInstance();
void unloadDungeonsGuide() throws ReferenceLeakedException;
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
index 6baee0f3..fa86054d 100644
--- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java
+++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java
@@ -21,7 +21,12 @@ package kr.syeyoung.dungeonsguide.launcher.loader;
import kr.syeyoung.dungeonsguide.launcher.DGInterface;
import kr.syeyoung.dungeonsguide.launcher.Main;
import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException;
+import org.apache.commons.io.IOUtils;
+import sun.misc.Resource;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
@@ -29,6 +34,9 @@ import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLClassLoader;
+import java.util.HashMap;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
public class JarLoader implements IDGLoader {
private DGInterface dgInterface;
@@ -37,60 +45,40 @@ public class JarLoader implements IDGLoader {
private boolean loaded;
- public static class JarClassLoader extends URLClassLoader {
- public JarClassLoader(URL[] urls, ClassLoader parent) {
- super(urls, parent);
- }
+ public static class JarClassLoader extends DGClassLoader {
+ public JarClassLoader(ClassLoader parent, ZipInputStream zipInputStream) throws IOException {
+ super(parent);
- @Override
- protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
-
-