aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/test/hotswap
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-01-18 21:39:21 +0100
committerGitHub <noreply@github.com>2024-01-18 21:39:21 +0100
commitb8adb961f50d17c02dbccbbb00376a2f728c819b (patch)
tree5cc607aa60c46f2556d9c7552f20336e362aec91 /src/main/java/at/hannibal2/skyhanni/test/hotswap
parent1bf29e31e35c4eb1a40fe0a7dfd111955ef48322 (diff)
downloadskyhanni-b8adb961f50d17c02dbccbbb00376a2f728c819b.tar.gz
skyhanni-b8adb961f50d17c02dbccbbb00376a2f728c819b.tar.bz2
skyhanni-b8adb961f50d17c02dbccbbb00376a2f728c819b.zip
Add hotswap agent support (#910)
Added hotswap detection and reloading all listeners on hotswap. #910
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/test/hotswap')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupport.kt22
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupportHandle.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupportImpl.kt57
3 files changed, 85 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupport.kt b/src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupport.kt
new file mode 100644
index 000000000..87acab62d
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupport.kt
@@ -0,0 +1,22 @@
+package at.hannibal2.skyhanni.test.hotswap
+
+import java.util.function.Supplier
+
+object HotswapSupport {
+ private val isForgeSidePresent =
+ runCatching { Class.forName("moe.nea.hotswapagentforge.forge.HotswapEvent") }.isSuccess
+ private val obj = if (isForgeSidePresent) {
+ Supplier<HotswapSupportHandle?> { HotswapSupportImpl() }
+ } else {
+ Supplier<HotswapSupportHandle?> { null }
+ }.get()
+
+ fun isLoaded(): Boolean {
+ return obj?.isLoaded() ?: false
+ }
+
+ fun load() {
+ obj?.load()
+ }
+}
+
diff --git a/src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupportHandle.kt b/src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupportHandle.kt
new file mode 100644
index 000000000..c006753a0
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupportHandle.kt
@@ -0,0 +1,6 @@
+package at.hannibal2.skyhanni.test.hotswap
+
+interface HotswapSupportHandle {
+ fun load()
+ fun isLoaded(): Boolean
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupportImpl.kt b/src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupportImpl.kt
new file mode 100644
index 000000000..df74eedbb
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/test/hotswap/HotswapSupportImpl.kt
@@ -0,0 +1,57 @@
+package at.hannibal2.skyhanni.test.hotswap
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.makeAccessible
+import at.hannibal2.skyhanni.utils.LorenzUtils.removeFinal
+import moe.nea.hotswapagentforge.forge.ClassDefinitionEvent
+import moe.nea.hotswapagentforge.forge.HotswapEvent
+import moe.nea.hotswapagentforge.forge.HotswapFinishedEvent
+import net.minecraft.client.Minecraft
+import net.minecraftforge.common.MinecraftForge
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class HotswapSupportImpl : HotswapSupportHandle {
+ override fun load() {
+ MinecraftForge.EVENT_BUS.register(this)
+ println("Hotswap Client in Skyhanni loaded")
+ }
+
+ @SubscribeEvent
+ fun onHotswapClass(event: ClassDefinitionEvent.Redefinition) {
+ val instance = SkyHanniMod.modules.find { it.javaClass.name == event.fullyQualifiedName }
+ if (instance == null) return
+ val primaryConstructor = runCatching { instance.javaClass.getDeclaredConstructor() }.getOrNull()
+ Minecraft.getMinecraft().addScheduledTask(Runnable {
+ LorenzUtils.chat("Refreshing event subscriptions for module $instance!")
+ MinecraftForge.EVENT_BUS.unregister(instance)
+ if (primaryConstructor == null) {
+ MinecraftForge.EVENT_BUS.register(instance)
+ } else {
+ SkyHanniMod.modules.remove(instance)
+ primaryConstructor.isAccessible = true
+ val newInstance = primaryConstructor.newInstance()
+ LorenzUtils.chat("Reconstructing $instance -> $newInstance!")
+ val instanceField = runCatching { instance.javaClass.getDeclaredField("INSTANCE") }.getOrNull()
+ ?.takeIf { it.type == instance.javaClass }
+ ?.makeAccessible()
+ ?.removeFinal()
+ if (instanceField != null) {
+ LorenzUtils.chat("Reinjected static instance $newInstance!")
+ instanceField.set(null, newInstance)
+ }
+ SkyHanniMod.modules.add(newInstance)
+ MinecraftForge.EVENT_BUS.register(newInstance)
+ }
+ })
+ }
+
+ @SubscribeEvent
+ fun onHotswapDetected(event: HotswapFinishedEvent) {
+ LorenzUtils.chat("Hotswap finished!")
+ }
+
+ override fun isLoaded(): Boolean {
+ return HotswapEvent.isReady()
+ }
+}