aboutsummaryrefslogtreecommitdiff
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
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
-rw-r--r--build.gradle.kts1
-rw-r--r--gradle/libs.versions.toml1
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt3
-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
-rw-r--r--src/main/resources/hotswap-agent.properties1
7 files changed, 91 insertions, 0 deletions
diff --git a/build.gradle.kts b/build.gradle.kts
index 3573835f3..e2d7e04fa 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -111,6 +111,7 @@ dependencies {
shadowModImpl(libs.moulconfig)
shadowImpl(libs.libautoupdate)
shadowImpl("org.jetbrains.kotlin:kotlin-reflect:1.9.0")
+ implementation(libs.hotswapagentforge)
// testImplementation(kotlin("test"))
testImplementation("com.github.NotEnoughUpdates:NotEnoughUpdates:v2.1.1-pre4:all") {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index ce1a16908..99aad1330 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -9,3 +9,4 @@ moulconfig = { module = "org.notenoughupdates.moulconfig:legacy", version.ref =
libautoupdate = { module = "moe.nea:libautoupdate", version.ref = "libautoupdate" }
headlessLwjgl = { module = "com.github.3arthqu4ke.HeadlessMc:headlessmc-lwjgl", version.ref = "headlessLwjgl" }
jbAnnotations = { module = "org.jetbrains:annotations", version.ref = "jbAnnotations" }
+hotswapagentforge = { module = "moe.nea:hotswapagent-forge", version = "1.0.1" }
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index f7c257706..addfbf1f0 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -341,6 +341,7 @@ import at.hannibal2.skyhanni.test.TestExportTools
import at.hannibal2.skyhanni.test.TestShowSlotNumber
import at.hannibal2.skyhanni.test.WorldEdit
import at.hannibal2.skyhanni.test.command.CopyNearbyParticlesCommand
+import at.hannibal2.skyhanni.test.hotswap.HotswapSupport
import at.hannibal2.skyhanni.utils.EntityOutlineRenderer
import at.hannibal2.skyhanni.utils.KeyboardManager
import at.hannibal2.skyhanni.utils.LorenzUtils
@@ -378,6 +379,8 @@ class SkyHanniMod {
fun preInit(event: FMLPreInitializationEvent?) {
checkIfNeuIsLoaded()
+ HotswapSupport.load()
+
// data
loadModule(this)
loadModule(ChatManager)
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()
+ }
+}
diff --git a/src/main/resources/hotswap-agent.properties b/src/main/resources/hotswap-agent.properties
new file mode 100644
index 000000000..230a7d99a
--- /dev/null
+++ b/src/main/resources/hotswap-agent.properties
@@ -0,0 +1 @@
+pluginPackages=moe.nea.hotswapagentforge.plugin