Add @Subscribe annotation

[no changelog]
This commit is contained in:
Linnea Gräf
2024-05-07 20:26:04 +02:00
parent 0cb976ce07
commit 8f3cc34740
11 changed files with 252 additions and 47 deletions

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@
/build/ /build/
/buildSrc/.gradle /buildSrc/.gradle
/buildSrc/build/ /buildSrc/build/
/*/build
# Ignore Gradle GUI config # Ignore Gradle GUI config
gradle-app.setting gradle-app.setting

View File

@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
java java
`maven-publish` `maven-publish`
id("com.google.devtools.ksp") version "1.9.23-1.0.20"
kotlin("jvm") version "1.9.23" kotlin("jvm") version "1.9.23"
kotlin("plugin.serialization") version "1.9.23" kotlin("plugin.serialization") version "1.9.23"
// id("com.bnorm.power.kotlin-power-assert") version "0.13.0" // 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") testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
implementation(project(":symbols"))
ksp(project(":symbols"))
} }
tasks.test { tasks.test {
@@ -193,8 +197,8 @@ loom {
parseEnvFile(file(".env")).forEach { (t, u) -> parseEnvFile(file(".env")).forEach { (t, u) ->
environmentVariable(t, u) environmentVariable(t, u)
} }
parseEnvFile(file(".properties")).forEach{ (t, u) -> parseEnvFile(file(".properties")).forEach { (t, u) ->
property(t,u) property(t, u)
} }
vmArg("-ea") vmArg("-ea")
vmArg("-XX:+AllowEnhancedClassRedefinition") vmArg("-XX:+AllowEnhancedClassRedefinition")

View File

@@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> /*
// * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
// SPDX-License-Identifier: CC0-1.0 * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: CC0-1.0
* SPDX-License-Identifier: GPL-3.0-or-later
*/
pluginManagement { pluginManagement {
repositories { repositories {
@@ -28,3 +32,4 @@ pluginManagement {
rootProject.name = "Firmament" rootProject.name = "Firmament"
include("symbols")

View File

@@ -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>,
)

View File

@@ -10,7 +10,10 @@ package moe.nea.firmament.features
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer import kotlinx.serialization.serializer
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.generated.AllSubscriptions
import moe.nea.firmament.events.FeaturesInitializedEvent 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.AutoCompletions
import moe.nea.firmament.features.chat.ChatLinks import moe.nea.firmament.features.chat.ChatLinks
import moe.nea.firmament.features.chat.QuickCommands import moe.nea.firmament.features.chat.QuickCommands
@@ -80,11 +83,26 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
loadFeature(DebugView) loadFeature(DebugView)
} }
allFeatures.forEach { it.config } allFeatures.forEach { it.config }
subscribeEvents()
FeaturesInitializedEvent.publish(FeaturesInitializedEvent(allFeatures.toList())) FeaturesInitializedEvent.publish(FeaturesInitializedEvent(allFeatures.toList()))
hasAutoloaded = true 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) { fun loadFeature(feature: FirmamentFeature) {
synchronized(features) { synchronized(features) {
if (feature.identifier in features) { if (feature.identifier in features) {

View File

@@ -1,14 +1,16 @@
/* /*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> * 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 * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
package moe.nea.firmament.features package moe.nea.firmament.features
import moe.nea.firmament.events.subscription.SubscriptionOwner
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
interface FirmamentFeature { interface FirmamentFeature : SubscriptionOwner {
val identifier: String val identifier: String
val defaultEnabled: Boolean val defaultEnabled: Boolean
get() = true get() = true

View File

@@ -1,5 +1,6 @@
/* /*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> * 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 * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
@@ -7,6 +8,7 @@
package moe.nea.firmament.features.inventory package moe.nea.firmament.features.inventory
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ItemTooltipEvent import moe.nea.firmament.events.ItemTooltipEvent
import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
@@ -26,9 +28,12 @@ object PriceData : FirmamentFeature {
override val config get() = TConfig override val config get() = TConfig
override fun onLoad() { override fun onLoad() {
ItemTooltipEvent.subscribe { }
@Subscribe
fun function(it: ItemTooltipEvent) {
if (!TConfig.tooltipEnabled && !TConfig.enableKeybinding.isPressed()) { if (!TConfig.tooltipEnabled && !TConfig.enableKeybinding.isPressed()) {
return@subscribe return
} }
val sbId = it.stack.skyBlockId val sbId = it.stack.skyBlockId
val bazaarData = HypixelStaticData.bazaarData[sbId] val bazaarData = HypixelStaticData.bazaarData[sbId]
@@ -36,17 +41,19 @@ object PriceData : FirmamentFeature {
if (bazaarData != null) { if (bazaarData != null) {
it.lines.add(Text.literal("")) it.lines.add(Text.literal(""))
it.lines.add( it.lines.add(
Text.stringifiedTranslatable("firmament.tooltip.bazaar.sell-order", FirmFormatters.formatCurrency(bazaarData.quickStatus.sellPrice, 1)) Text.stringifiedTranslatable("firmament.tooltip.bazaar.sell-order",
FirmFormatters.formatCurrency(bazaarData.quickStatus.sellPrice, 1))
) )
it.lines.add( it.lines.add(
Text.stringifiedTranslatable("firmament.tooltip.bazaar.buy-order", FirmFormatters.formatCurrency(bazaarData.quickStatus.buyPrice, 1)) Text.stringifiedTranslatable("firmament.tooltip.bazaar.buy-order",
FirmFormatters.formatCurrency(bazaarData.quickStatus.buyPrice, 1))
) )
} else if (lowestBin != null) { } else if (lowestBin != null) {
it.lines.add(Text.literal("")) it.lines.add(Text.literal(""))
it.lines.add( it.lines.add(
Text.stringifiedTranslatable("firmament.tooltip.ah.lowestbin", FirmFormatters.formatCurrency(lowestBin, 1)) Text.stringifiedTranslatable("firmament.tooltip.ah.lowestbin",
FirmFormatters.formatCurrency(lowestBin, 1))
) )
} }
} }
}
} }

View File

@@ -20,6 +20,7 @@ import net.minecraft.entity.player.PlayerInventory
import net.minecraft.screen.GenericContainerScreenHandler import net.minecraft.screen.GenericContainerScreenHandler
import net.minecraft.screen.slot.SlotActionType import net.minecraft.screen.slot.SlotActionType
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.HandledScreenKeyPressedEvent import moe.nea.firmament.events.HandledScreenKeyPressedEvent
import moe.nea.firmament.events.IsSlotProtectedEvent import moe.nea.firmament.events.IsSlotProtectedEvent
import moe.nea.firmament.events.SlotRenderEvents import moe.nea.firmament.events.SlotRenderEvents
@@ -166,7 +167,10 @@ object SlotLocking : FirmamentFeature {
event.protectSilent() event.protectSilent()
} }
} }
SlotRenderEvents.After.subscribe { }
@Subscribe
fun function(it: SlotRenderEvents.After) {
val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf()) val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())
val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf()) val isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
if (isSlotLocked || isUUIDLocked) { if (isSlotLocked || isUUIDLocked) {
@@ -190,5 +194,4 @@ object SlotLocking : FirmamentFeature {
RenderSystem.enableDepthTest() RenderSystem.enableDepthTest()
} }
} }
}
} }

19
symbols/build.gradle.kts Normal file
View File

@@ -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")
}

View File

@@ -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

View File

@@ -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)
}
}