aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-05-07 20:26:04 +0200
committerLinnea Gräf <nea@nea.moe>2024-05-07 20:26:04 +0200
commit8f3cc34740fcfe1572d23c8f1c1db1a309217b84 (patch)
tree1a4d7922b8ec1785e0c1f52fcdf85f3e33f16859
parent0cb976ce078b9b0217c9a8e5763638764d89a31e (diff)
downloadFirmament-8f3cc34740fcfe1572d23c8f1c1db1a309217b84.tar.gz
Firmament-8f3cc34740fcfe1572d23c8f1c1db1a309217b84.tar.bz2
Firmament-8f3cc34740fcfe1572d23c8f1c1db1a309217b84.zip
Add @Subscribe annotation
[no changelog]
-rw-r--r--.gitignore1
-rw-r--r--build.gradle.kts8
-rw-r--r--settings.gradle.kts11
-rw-r--r--src/main/kotlin/moe/nea/firmament/events/subscription/Subscription.kt18
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt18
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt4
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/PriceData.kt49
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt47
-rw-r--r--symbols/build.gradle.kts19
-rw-r--r--symbols/src/main/kotlin/Subscribe.kt12
-rw-r--r--symbols/src/main/kotlin/process/SubscribeAnnotationProcessor.kt116
11 files changed, 254 insertions, 49 deletions
diff --git a/.gitignore b/.gitignore
index f58ab5f..9be7af4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
/build/
/buildSrc/.gradle
/buildSrc/build/
+/*/build
# Ignore Gradle GUI config
gradle-app.setting
diff --git a/build.gradle.kts b/build.gradle.kts
index 311da61..7d358dd 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
java
`maven-publish`
+ id("com.google.devtools.ksp") version "1.9.23-1.0.20"
kotlin("jvm") version "1.9.23"
kotlin("plugin.serialization") version "1.9.23"
// id("com.bnorm.power.kotlin-power-assert") version "0.13.0"
@@ -170,6 +171,9 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
+
+ implementation(project(":symbols"))
+ ksp(project(":symbols"))
}
tasks.test {
@@ -193,8 +197,8 @@ loom {
parseEnvFile(file(".env")).forEach { (t, u) ->
environmentVariable(t, u)
}
- parseEnvFile(file(".properties")).forEach{ (t, u) ->
- property(t,u)
+ parseEnvFile(file(".properties")).forEach { (t, u) ->
+ property(t, u)
}
vmArg("-ea")
vmArg("-XX:+AllowEnhancedClassRedefinition")
diff --git a/settings.gradle.kts b/settings.gradle.kts
index bee19d4..79cf1a4 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,6 +1,10 @@
-// SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
-//
-// SPDX-License-Identifier: CC0-1.0
+/*
+ * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: CC0-1.0
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
pluginManagement {
repositories {
@@ -28,3 +32,4 @@ pluginManagement {
rootProject.name = "Firmament"
+include("symbols")
diff --git a/src/main/kotlin/moe/nea/firmament/events/subscription/Subscription.kt b/src/main/kotlin/moe/nea/firmament/events/subscription/Subscription.kt
new file mode 100644
index 0000000..1e7a6a8
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/events/subscription/Subscription.kt
@@ -0,0 +1,18 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.events.subscription
+
+import moe.nea.firmament.events.FirmamentEvent
+import moe.nea.firmament.events.FirmamentEventBus
+
+interface SubscriptionOwner
+
+data class Subscription<T : FirmamentEvent>(
+ val owner: SubscriptionOwner,
+ val invoke: (T) -> Unit,
+ val eventBus: FirmamentEventBus<T>,
+)
diff --git a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt
index a8daf63..669b3c9 100644
--- a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt
@@ -10,7 +10,10 @@ package moe.nea.firmament.features
import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer
import moe.nea.firmament.Firmament
+import moe.nea.firmament.annotations.generated.AllSubscriptions
import moe.nea.firmament.events.FeaturesInitializedEvent
+import moe.nea.firmament.events.FirmamentEvent
+import moe.nea.firmament.events.subscription.Subscription
import moe.nea.firmament.features.chat.AutoCompletions
import moe.nea.firmament.features.chat.ChatLinks
import moe.nea.firmament.features.chat.QuickCommands
@@ -80,11 +83,26 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
loadFeature(DebugView)
}
allFeatures.forEach { it.config }
+ subscribeEvents()
FeaturesInitializedEvent.publish(FeaturesInitializedEvent(allFeatures.toList()))
hasAutoloaded = true
}
}
+ private fun subscribeEvents() {
+ AllSubscriptions.provideSubscriptions {
+ subscribeSingleEvent(it)
+ }
+ }
+
+ private fun <T : FirmamentEvent> subscribeSingleEvent(it: Subscription<T>) {
+ if (it.owner in features.values) { // TODO: better check here, somehow. probably implement some interface method
+ it.eventBus.subscribe(false, it.invoke) // TODO: pass through receivesCancelled from the annotation
+ } else {
+ Firmament.logger.error("Ignoring event listener for ${it.eventBus} in ${it.owner}")
+ }
+ }
+
fun loadFeature(feature: FirmamentFeature) {
synchronized(features) {
if (feature.identifier in features) {
diff --git a/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt b/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt
index afd36a5..2416fec 100644
--- a/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/FirmamentFeature.kt
@@ -1,14 +1,16 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.features
+import moe.nea.firmament.events.subscription.SubscriptionOwner
import moe.nea.firmament.gui.config.ManagedConfig
-interface FirmamentFeature {
+interface FirmamentFeature : SubscriptionOwner {
val identifier: String
val defaultEnabled: Boolean
get() = true
diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/PriceData.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/PriceData.kt
index aa8982d..b511611 100644
--- a/src/main/kotlin/moe/nea/firmament/features/inventory/PriceData.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/inventory/PriceData.kt
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
@@ -7,6 +8,7 @@
package moe.nea.firmament.features.inventory
import net.minecraft.text.Text
+import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ItemTooltipEvent
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig
@@ -26,27 +28,32 @@ object PriceData : FirmamentFeature {
override val config get() = TConfig
override fun onLoad() {
- ItemTooltipEvent.subscribe {
- if (!TConfig.tooltipEnabled && !TConfig.enableKeybinding.isPressed()) {
- return@subscribe
- }
- val sbId = it.stack.skyBlockId
- val bazaarData = HypixelStaticData.bazaarData[sbId]
- val lowestBin = HypixelStaticData.lowestBin[sbId]
- if (bazaarData != null) {
- it.lines.add(Text.literal(""))
- it.lines.add(
- Text.stringifiedTranslatable("firmament.tooltip.bazaar.sell-order", FirmFormatters.formatCurrency(bazaarData.quickStatus.sellPrice, 1))
- )
- it.lines.add(
- Text.stringifiedTranslatable("firmament.tooltip.bazaar.buy-order", FirmFormatters.formatCurrency(bazaarData.quickStatus.buyPrice, 1))
- )
- } else if (lowestBin != null) {
- it.lines.add(Text.literal(""))
- it.lines.add(
- Text.stringifiedTranslatable("firmament.tooltip.ah.lowestbin", FirmFormatters.formatCurrency(lowestBin, 1))
- )
- }
+ }
+
+ @Subscribe
+ fun function(it: ItemTooltipEvent) {
+ if (!TConfig.tooltipEnabled && !TConfig.enableKeybinding.isPressed()) {
+ return
+ }
+ val sbId = it.stack.skyBlockId
+ val bazaarData = HypixelStaticData.bazaarData[sbId]
+ val lowestBin = HypixelStaticData.lowestBin[sbId]
+ if (bazaarData != null) {
+ it.lines.add(Text.literal(""))
+ it.lines.add(
+ Text.stringifiedTranslatable("firmament.tooltip.bazaar.sell-order",
+ FirmFormatters.formatCurrency(bazaarData.quickStatus.sellPrice, 1))
+ )
+ it.lines.add(
+ Text.stringifiedTranslatable("firmament.tooltip.bazaar.buy-order",
+ FirmFormatters.formatCurrency(bazaarData.quickStatus.buyPrice, 1))
+ )
+ } else if (lowestBin != null) {
+ it.lines.add(Text.literal(""))
+ it.lines.add(
+ Text.stringifiedTranslatable("firmament.tooltip.ah.lowestbin",
+ FirmFormatters.formatCurrency(lowestBin, 1))
+ )
}
}
}
diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt
index 9bf2182..aee985c 100644
--- a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt
@@ -20,6 +20,7 @@ import net.minecraft.entity.player.PlayerInventory
import net.minecraft.screen.GenericContainerScreenHandler
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.util.Identifier
+import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
import moe.nea.firmament.events.IsSlotProtectedEvent
import moe.nea.firmament.events.SlotRenderEvents
@@ -166,29 +167,31 @@ object SlotLocking : FirmamentFeature {
event.protectSilent()
}
}
- SlotRenderEvents.After.subscribe {
- val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())
- val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
- if (isSlotLocked || isUUIDLocked) {
- RenderSystem.disableDepthTest()
- it.context.drawSprite(
- it.slot.x, it.slot.y, 0,
- 16, 16,
- MC.guiAtlasManager.getSprite(
- when {
- isSlotLocked ->
- (Identifier("firmament:slot_locked"))
-
- isUUIDLocked ->
- (Identifier("firmament:uuid_locked"))
-
- else ->
- error("unreachable")
- }
- )
+ }
+
+ @Subscribe
+ fun function(it: SlotRenderEvents.After) {
+ val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())
+ val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
+ if (isSlotLocked || isUUIDLocked) {
+ RenderSystem.disableDepthTest()
+ it.context.drawSprite(
+ it.slot.x, it.slot.y, 0,
+ 16, 16,
+ MC.guiAtlasManager.getSprite(
+ when {
+ isSlotLocked ->
+ (Identifier("firmament:slot_locked"))
+
+ isUUIDLocked ->
+ (Identifier("firmament:uuid_locked"))
+
+ else ->
+ error("unreachable")
+ }
)
- RenderSystem.enableDepthTest()
- }
+ )
+ RenderSystem.enableDepthTest()
}
}
}
diff --git a/symbols/build.gradle.kts b/symbols/build.gradle.kts
new file mode 100644
index 0000000..fad3221
--- /dev/null
+++ b/symbols/build.gradle.kts
@@ -0,0 +1,19 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+plugins {
+ kotlin("jvm")
+ id("com.google.devtools.ksp")
+}
+
+repositories {
+ mavenCentral()
+}
+dependencies {
+ ksp("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
+ implementation("com.google.auto.service:auto-service-annotations:1.1.1")
+ implementation("com.google.devtools.ksp:symbol-processing-api:1.9.23-1.0.20")
+}
diff --git a/symbols/src/main/kotlin/Subscribe.kt b/symbols/src/main/kotlin/Subscribe.kt
new file mode 100644
index 0000000..e7a7bf4
--- /dev/null
+++ b/symbols/src/main/kotlin/Subscribe.kt
@@ -0,0 +1,12 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.annotations
+
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.SOURCE)
+annotation class Subscribe
+
diff --git a/symbols/src/main/kotlin/process/SubscribeAnnotationProcessor.kt b/symbols/src/main/kotlin/process/SubscribeAnnotationProcessor.kt
new file mode 100644
index 0000000..be817ce
--- /dev/null
+++ b/symbols/src/main/kotlin/process/SubscribeAnnotationProcessor.kt
@@ -0,0 +1,116 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.annotations.process
+
+import com.google.auto.service.AutoService
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import com.google.devtools.ksp.processing.SymbolProcessorProvider
+import com.google.devtools.ksp.symbol.ClassKind
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.Nullability
+import com.google.devtools.ksp.validate
+import java.text.SimpleDateFormat
+import java.util.Date
+import moe.nea.firmament.annotations.Subscribe
+
+class SubscribeAnnotationProcessor(
+ val logger: KSPLogger,
+ val codeGenerator: CodeGenerator,
+) : SymbolProcessor {
+ override fun finish() {
+ val dependencies = Dependencies(
+ aggregating = true,
+ *subscriptions.mapTo(mutableSetOf()) { it.parent.containingFile!! }.toTypedArray())
+ val subscriptionsFile =
+ codeGenerator
+ .createNewFile(dependencies, "moe.nea.firmament.annotations.generated", "AllSubscriptions")
+ .bufferedWriter()
+ subscriptionsFile.apply {
+ appendLine("// This file is @generated by SubscribeAnnotationProcessor at ${SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").format(
+ Date())}")
+ appendLine("// Do not edit")
+ appendLine("package moe.nea.firmament.annotations.generated")
+ appendLine()
+ appendLine("import moe.nea.firmament.events.subscription.*")
+ appendLine()
+ appendLine("object AllSubscriptions {")
+ appendLine(" fun provideSubscriptions(addSubscription: (Subscription<*>) -> Unit) {")
+ for (subscription in subscriptions) {
+ val owner = subscription.parent.qualifiedName!!.asString()
+ val method = subscription.child.simpleName.asString()
+ val type = subscription.type.declaration.qualifiedName!!.asString()
+ appendLine(" addSubscription(Subscription<$type>(")
+ appendLine(" ${owner},")
+ appendLine(" ${owner}::${method},")
+ appendLine(" ${type}))")
+ }
+ appendLine(" }")
+ appendLine("}")
+ }
+ subscriptionsFile.close()
+ }
+
+ data class Subscription(
+ val parent: KSClassDeclaration,
+ val child: KSFunctionDeclaration,
+ val type: KSType,
+ )
+
+ val subscriptions = mutableListOf<Subscription>()
+
+ fun processCandidates(list: List<KSAnnotated>) {
+ for (element in list) {
+ if (element !is KSFunctionDeclaration) {
+ logger.error("@Subscribe annotation on a not-function", element)
+ continue
+ }
+ if (element.isAbstract) {
+ logger.error("@Subscribe annotation on an abstract function", element)
+ continue
+ }
+ val parent = element.parentDeclaration
+ if (parent !is KSClassDeclaration || parent.isCompanionObject || parent.classKind != ClassKind.OBJECT) {
+ logger.error("@Subscribe on a non-object", element)
+ continue
+ }
+ val param = element.parameters.singleOrNull()
+ if (param == null) {
+ logger.error("@Subscribe annotated functions need to take exactly one parameter", element)
+ continue
+ }
+ val type = param.type.resolve()
+ if (type.nullability != Nullability.NOT_NULL) {
+ logger.error("@Subscribe annotated functions cannot take a nullable event", element)
+ continue
+ }
+ subscriptions.add(Subscription(parent, element, type))
+ }
+ }
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ val candidates = resolver.getSymbolsWithAnnotation(Subscribe::class.qualifiedName!!).toList()
+ val valid = candidates.filter { it.validate() }
+ val invalid = candidates.filter { !it.validate() }
+ processCandidates(valid)
+ return invalid
+ }
+}
+
+@AutoService(SymbolProcessorProvider::class)
+class SubscribeAnnotationProcessorProvider : SymbolProcessorProvider {
+ override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
+ return SubscribeAnnotationProcessor(environment.logger, environment.codeGenerator)
+ }
+}