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/
/buildSrc/.gradle
/buildSrc/build/
/*/build
# Ignore Gradle GUI config
gradle-app.setting

View File

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

View File

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

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.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) {

View File

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

View File

@@ -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,9 +28,12 @@ object PriceData : FirmamentFeature {
override val config get() = TConfig
override fun onLoad() {
ItemTooltipEvent.subscribe {
}
@Subscribe
fun function(it: ItemTooltipEvent) {
if (!TConfig.tooltipEnabled && !TConfig.enableKeybinding.isPressed()) {
return@subscribe
return
}
val sbId = it.stack.skyBlockId
val bazaarData = HypixelStaticData.bazaarData[sbId]
@@ -36,17 +41,19 @@ object PriceData : FirmamentFeature {
if (bazaarData != null) {
it.lines.add(Text.literal(""))
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(
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) {
it.lines.add(Text.literal(""))
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.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,7 +167,10 @@ object SlotLocking : FirmamentFeature {
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 isUUIDLocked = (it.slot.stack?.skyblockUUID) in (lockedUUIDs ?: setOf())
if (isSlotLocked || isUUIDLocked) {
@@ -190,5 +194,4 @@ object SlotLocking : FirmamentFeature {
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)
}
}