Skip to content

Commit

Permalink
Backend: Auto load annotation (#1904)
Browse files Browse the repository at this point in the history
Co-authored-by: Brady <thatgravyboat@gmail.com>
  • Loading branch information
CalMWolfs and ThatGravyBoat authored Jun 1, 2024
1 parent 2698b9d commit a20da5a
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ format like "- #821" to illustrate the dependency.
- All new classes should be written in Kotlin, with a few exceptions:
- Config files in `at.hannibal2.skyhanni.config.features`
- Mixin classes in `at.hannibal2.skyhanni.mixins.transformers`
- New features should be made in Kotlin objects unless there is a specific reason for it not to.
- If the feature needs to use forge events or a repo pattern, annotate it with `@SkyHanniModule`
- This will automatically register it to the forge event bus and load the repo patterns
- Avoid using deprecated functions.
- These functions are marked for removal in future versions.
- If you're unsure why a function is deprecated or how to replace it, please ask for guidance.
Expand Down
27 changes: 27 additions & 0 deletions annotation-processors/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
idea
kotlin("jvm")
java
}

repositories {
mavenCentral()
}

dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation("com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.8")
}

tasks.withType<JavaCompile> {
if (JavaVersion.current().isJava9Compatible) {
options.release.set(8)
}
}

val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package at.hannibal2.skyhanni.skyhannimodule

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.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.validate
import java.io.OutputStreamWriter

class ModuleProcessor(private val codeGenerator: CodeGenerator, private val logger: KSPLogger) : SymbolProcessor {

override fun process(resolver: Resolver): List<KSAnnotated> {

val symbols = resolver.getSymbolsWithAnnotation(SkyHanniModule::class.qualifiedName!!).toList()
val validSymbols = symbols.mapNotNull { validateSymbol(it) }

if (validSymbols.isNotEmpty()) {
generateFile(validSymbols)
}

return emptyList()
}

private fun validateSymbol(symbol: KSAnnotated): KSClassDeclaration? {
if (!symbol.validate()) {
logger.warn("Symbol is not valid: $symbol")
return null
}

if (symbol !is KSClassDeclaration) {
logger.error("@SkyHanniModule is only valid on class declarations", symbol)
return null
}

if (symbol.classKind != ClassKind.OBJECT) {
logger.error("@SkyHanniModule is only valid on kotlin objects", symbol)
return null
}

return symbol
}

private fun generateFile(symbols: List<KSClassDeclaration>) {
val dependencies = symbols.mapNotNull { it.containingFile }.toTypedArray()
val deps = Dependencies(true, *dependencies)

val file = codeGenerator.createNewFile(deps, "at.hannibal2.skyhanni.skyhannimodule", "LoadedModules")

OutputStreamWriter(file).use {
it.write("package at.hannibal2.skyhanni.skyhannimodule\n\n")
it.write("object LoadedModules {\n")
it.write(" val modules: List<Any> = listOf(\n")

symbols.forEach { symbol ->
it.write(" ${symbol.qualifiedName!!.asString()},\n")
}

it.write(" )\n")
it.write("}\n")
}

logger.warn("Generated LoadedModules file with ${symbols.size} modules")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package at.hannibal2.skyhanni.skyhannimodule

import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider

class ModuleProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return ModuleProcessor(environment.codeGenerator, environment.logger)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package at.hannibal2.skyhanni.skyhannimodule

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class SkyHanniModule
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
at.hannibal2.skyhanni.skyhannimodule.ModuleProvider
14 changes: 14 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ plugins {
kotlin("jvm") version "1.9.0"
id("com.bnorm.power.kotlin-power-assert") version "0.13.0"
`maven-publish`
id("com.google.devtools.ksp") version "1.9.0-1.0.13"
id("moe.nea.shot") version "1.0.0"
}

Expand Down Expand Up @@ -90,6 +91,8 @@ dependencies {

headlessLwjgl(libs.headlessLwjgl)

compileOnly(ksp(project(":annotation-processors"))!!)

shadowImpl("org.spongepowered:mixin:0.7.11-SNAPSHOT") {
isTransitive = false
}
Expand Down Expand Up @@ -129,6 +132,11 @@ dependencies {

implementation("net.hypixel:mod-api:0.3.1")
}

ksp {
arg("symbolProcessor", "at.hannibal2.skyhanni.loadmodule.LoadModuleProvider")
}

configurations.getByName("minecraftNamed").dependencies.forEach {
shot.applyTo(it as HasConfigurableAttributes<*>)
}
Expand All @@ -147,6 +155,12 @@ kotlin {
enableLanguageFeature("BreakContinueInInlineLambdas")
}
}
sourceSets.main {
kotlin.srcDir("build/generated/ksp/main/kotlin")
}
sourceSets.test {
kotlin.srcDir("build/generated/ksp/test/kotlin")
}
}

// Minecraft configuration:
Expand Down
5 changes: 4 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
loom.platform=forge
org.gradle.jvmargs=-Xmx2g
org.gradle.jvmargs=-Xmx2g
org.gradle.parallel=true
org.gradle.caching=true
kotlin.incremental.useClasspathSnapshot=true
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version("0.6.0")
}

include("annotation-processors")
rootProject.name = "SkyHanni"
11 changes: 8 additions & 3 deletions src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import at.hannibal2.skyhanni.data.ActionBarStatsData
import at.hannibal2.skyhanni.data.BitsAPI
import at.hannibal2.skyhanni.data.BlockData
import at.hannibal2.skyhanni.data.BossbarData
import at.hannibal2.skyhanni.data.ChatManager
import at.hannibal2.skyhanni.data.CropAccessoryData
import at.hannibal2.skyhanni.data.EntityData
import at.hannibal2.skyhanni.data.EntityMovementData
Expand Down Expand Up @@ -453,6 +452,7 @@ import at.hannibal2.skyhanni.features.stranded.HighlightPlaceableNpcs
import at.hannibal2.skyhanni.features.summonings.SummoningMobManager
import at.hannibal2.skyhanni.features.summonings.SummoningSoulsName
import at.hannibal2.skyhanni.mixins.hooks.RenderLivingEntityHelper
import at.hannibal2.skyhanni.skyhannimodule.LoadedModules
import at.hannibal2.skyhanni.test.HighlightMissingRepoItems
import at.hannibal2.skyhanni.test.PacketTest
import at.hannibal2.skyhanni.test.ParkourWaypointSaver
Expand Down Expand Up @@ -512,9 +512,10 @@ class SkyHanniMod {

HotswapSupport.load()

// data
loadModule(this)
loadModule(ChatManager)
LoadedModules.modules.forEach { loadModule(it) }

// data
loadModule(PlayerChatManager())
loadModule(PlayerNameFormatter())
loadModule(HypixelData())
Expand Down Expand Up @@ -1007,9 +1008,13 @@ class SkyHanniMod {
} catch (e: Exception) {
Exception("Error reading repo data", e).printStackTrace()
}
loadedClasses.clear()
}

private val loadedClasses = mutableSetOf<Any>()

fun loadModule(obj: Any) {
if (!loadedClasses.add(obj.javaClass.name)) throw IllegalStateException("Module ${obj.javaClass.name} is already loaded")
modules.add(obj)
MinecraftForge.EVENT_BUS.register(obj)
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/at/hannibal2/skyhanni/data/ChatManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.MessageSendToServerEvent
import at.hannibal2.skyhanni.events.PacketEvent
import at.hannibal2.skyhanni.features.chat.ChatFilterGui
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.IdentityCharacteristics
import at.hannibal2.skyhanni.utils.LorenzLogger
Expand All @@ -26,6 +27,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.relauncher.ReflectionHelper
import java.lang.invoke.MethodHandles

@SkyHanniModule
object ChatManager {

private val loggerAll = LorenzLogger("chat/all")
Expand Down

0 comments on commit a20da5a

Please sign in to comment.