diff options
6 files changed, 241 insertions, 6 deletions
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandDungeonsGuide.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandDungeonsGuide.java index 28ce3735..b0b29e89 100644 --- a/mod/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandDungeonsGuide.java +++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/commands/CommandDungeonsGuide.java @@ -46,6 +46,7 @@ import kr.syeyoung.dungeonsguide.roomedit.EditingContext; import kr.syeyoung.dungeonsguide.roomedit.gui.GuiDungeonRoomEdit; import kr.syeyoung.dungeonsguide.roomprocessor.GeneralRoomProcessor; import kr.syeyoung.dungeonsguide.roomprocessor.bossfight.BossfightProcessor; +import kr.syeyoung.dungeonsguide.stomp.StompPayload; import kr.syeyoung.dungeonsguide.utils.AhUtils; import kr.syeyoung.dungeonsguide.utils.MapUtils; import kr.syeyoung.dungeonsguide.utils.ShortUtils; diff --git a/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java b/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/DungeonsGuideReloadListener.java new file mode 100644 index 00000000..7252a9db --- /dev/null +++ b/wrapper/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/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java b/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java index f7db1b44..bb3e5909 100755 --- a/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java +++ b/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java @@ -26,6 +26,8 @@ import kr.syeyoung.dungeonsguide.launcher.exceptions.PrivacyPolicyRequiredExcept import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException; import kr.syeyoung.dungeonsguide.launcher.exceptions.TokenExpiredException; 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; @@ -44,6 +46,9 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import java.io.*; import java.security.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; @Mod(modid = Main.MOD_ID, version = Main.VERSION) public class Main @@ -60,6 +65,14 @@ public class Main 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; @@ -86,18 +99,25 @@ public class Main throw new UnsupportedOperationException("Current version is not unloadable"); } dgInterface = null; + for (DungeonsGuideReloadListener listener : listeners) { + listener.unloadReference(); + } if (currentLoader != null) { currentLoader.unloadJar(); } currentLoader = null; } - public void load(IDGLoader newLoader) { + public void load(IDGLoader newLoader) throws ClassNotFoundException, InstantiationException, IllegalAccessException { if (dgInterface != null) throw new IllegalStateException("DG is loaded"); newLoader.loadJar(authenticator); dgInterface = newLoader.getInstance(); currentLoader = newLoader; dgInterface.init(configDir); + + for (DungeonsGuideReloadListener listener : listeners) { + listener.onLoad(dgInterface); + } } public void reload(IDGLoader newLoader) { @@ -113,10 +133,12 @@ public class Main } public void tryOpenError() { + Minecraft.getMinecraft().displayGuiScreen(obtainErrorGUI()); } public GuiScreen obtainErrorGUI() { // when gets called init and stuff remove thing + return null; } @SubscribeEvent(priority = EventPriority.LOWEST) @@ -144,12 +166,13 @@ public class Main if ("local".equals(loader) || (loader.equals("auto") && this.getClass().getResourceAsStream("/kr/syeyoung/dungeonsguide/DungeonsGuide.class") == null)) { - - } else if ("jar".equals("loader") || - (loader.equals("auto") && this.getClass().getResourceAsStream("/mod.jar") == 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()); } @@ -231,6 +254,7 @@ public class Main public void setLastError(Throwable t) { lastError = t; + tryOpenError(); } public static Main getMain() { diff --git a/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java b/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java index 607fc668..ceff437e 100644 --- a/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java +++ b/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java @@ -24,7 +24,7 @@ import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException; import net.minecraftforge.common.config.Configuration; public interface IDGLoader { - void loadJar(Authenticator authenticator); + void loadJar(Authenticator authenticator) throws InstantiationException, IllegalAccessException, ClassNotFoundException; DGInterface getInstance(); void unloadJar() throws ReferenceLeakedException; diff --git a/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java b/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java new file mode 100644 index 00000000..2012412a --- /dev/null +++ b/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java @@ -0,0 +1,128 @@ +/* + * 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.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)) { + // First, check if the class has already been loaded + Class<?> c = findLoadedClass(name); + if (c == null) { + long t0 = System.nanoTime(); + + if (c == null) { + // If still not found, then invoke findClass in order + // to find the class. + long t1 = System.nanoTime(); + c = findClass(name); + + // this is the defining class loader; record the stats + sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); + sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); + sun.misc.PerfCounter.getFindClasses().increment(); + } + try { + if (getParent() != null && c == null) { + c = getParent().loadClass(name); + } + } catch (ClassNotFoundException e) { + // ClassNotFoundException thrown if class not found + // from the non-null parent class loader + } + } + if (resolve) { + resolveClass(c); + } + return c; + } + } + + public Class<?> loadClassResolve(String name, boolean resolve) throws ClassNotFoundException { + return this.loadClass(name, resolve); + } + } + + @Override + public void loadJar(Authenticator authenticator) throws ClassNotFoundException, InstantiationException, IllegalAccessException { + if (dgInterface != null) throw new IllegalStateException("Already loaded"); + + JarClassLoader 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 { + 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"; + } +} diff --git a/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java b/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java new file mode 100644 index 00000000..67c43ad5 --- /dev/null +++ b/wrapper/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java @@ -0,0 +1,58 @@ +/* + * 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; + +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"; + } +} |