Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a1112ea
feat: introduce hook framework with processor, annotations, and integ…
twisti-dev Jan 24, 2026
f4a0340
chore: update ABI and bump version [skip ci]
github-actions[bot] Jan 24, 2026
c5619cf
feat: refactor VelocityHookService to use classloader for resource re…
twisti-dev Jan 24, 2026
25db719
Merge remote-tracking branch 'origin/feat/hook-api' into feat/hook-api
twisti-dev Jan 24, 2026
f693a76
Fix hook metadata emission on unresolved deps
twisti-dev Jan 24, 2026
c29cbc4
Fix hook metadata emission when class deps are unresolved (#189)
twisti-dev Jan 24, 2026
153a7dc
Update ABI reference
twisti-dev Jan 24, 2026
cdfca6c
feat: improve error handling for hooks metadata loading
twisti-dev Jan 24, 2026
b342e50
feat: introduce shared hook metadata and configuration classes
twisti-dev Jan 25, 2026
1c47e83
feat: refactor hook architecture to use shared Hook interface and add…
twisti-dev Jan 25, 2026
e12ea89
feat: add conditional hook evaluation with custom conditions support
twisti-dev Jan 25, 2026
41f5bdd
feat: implement conditional hook support and enhance configuration ha…
twisti-dev Jan 25, 2026
e4f9736
feat: add shared Hook interface and enhance hook condition handling
twisti-dev Jan 25, 2026
ef61b30
feat: implement topological sort algorithm for directed graphs
twisti-dev Jan 25, 2026
0471e83
Initial plan
Copilot Jan 25, 2026
4d18670
feat: handle unresolved class dependencies in hook processing
twisti-dev Jan 25, 2026
16cc998
feat: remove unused Kotlin compiler options for InternalSurfApi
twisti-dev Jan 25, 2026
ea677c5
fix: preserve topological order by using priority as tie-breaker duri…
Copilot Jan 25, 2026
85b4f1b
chore: dump abi
twisti-dev Jan 25, 2026
1a1a0d3
refactor: filter dependencies before sorting by priority
Copilot Jan 25, 2026
0d30fd4
refactor: use Kahn's algorithm with priority queue for topological sort
Copilot Jan 25, 2026
2b3abbb
Fix topological sort violating hook dependencies by using Kahn's algo…
twisti-dev Jan 25, 2026
db1645a
fix: ensure safe null handling for incoming edges in hook processing
twisti-dev Jan 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ subprojects {
afterEvaluate {
extensions.findByType<KotlinJvmExtension>()?.apply {
compilerOptions {
optIn.add("dev.slne.surf.surfapi.core.api.util.InternalSurfApi")
optIn.add("dev.slne.surf.surfapi.shared.api.util.InternalSurfApi")
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion buildSrc/src/main/kotlin/core-convention.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ repositories {

dependencies {
compileOnly(libs.auto.service.annotations)
ksp(project(":surf-api-gradle-plugin:surf-api-processor"))
if (!project.path.contains("surf-api-shared")) {
ksp(project(":surf-api-gradle-plugin:surf-api-processor"))
}

compileOnlyApi("org.jetbrains:annotations:26.0.2-1")
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
javaVersion=25
mcVersion=1.21.11
group=dev.slne.surf
version=1.21.11-2.55.1
version=1.21.11-2.56.0
relocationPrefix=dev.slne.surf.surfapi.libs
snapshot=false
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-milestone-5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
5 changes: 1 addition & 4 deletions gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions gradlew.bat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ if (!ci) {
include(":surf-api-bukkit:surf-api-bukkit-plugin-test")
// include("surf-api-generator")
include("surf-api-modern-generator")
}
}
include("surf-api-shared")
include("surf-api-shared:surf-api-shared-public")
include("surf-api-shared:surf-api-shared-internal")
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import dev.jorel.commandapi.kotlindsl.*
import dev.jorel.commandapi.wrappers.NativeProxyCommandSender
import dev.slne.surf.surfapi.core.api.messages.Colors
import dev.slne.surf.surfapi.core.api.messages.adventure.text
import dev.slne.surf.surfapi.core.api.util.InternalSurfApi
import dev.slne.surf.surfapi.shared.api.util.InternalSurfApi
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,29 @@ import dev.slne.surf.surfapi.bukkit.test.command.subcommands.inventory.TestInven
import dev.slne.surf.surfapi.bukkit.test.command.subcommands.reflection.Reflection
import dev.slne.surf.surfapi.bukkit.test.config.ModernTestConfig
import dev.slne.surf.surfapi.bukkit.test.listener.ChatListener
import dev.slne.surf.surfapi.core.api.hook.surfHookApi

@OptIn(NmsUseWithCaution::class)
class BukkitPluginMain : SuspendingJavaPlugin() {
override fun onLoad() {
override suspend fun onLoadAsync() {
ModernTestConfig.init()
ModernTestConfig.randomise()

surfHookApi.load(this)
packetListenerApi.registerListeners(ChatListener())
TestInventoryView.register()
}

override fun onEnable() {
override suspend fun onEnableAsync() {
SurfApiTestCommand().register()
Reflection::class.java.getClassLoader() // initialize Reflection

surfHookApi.enable(this)
}

override fun onDisable() {
override suspend fun onDisableAsync() {
CommandAPI.unregister("surfapitest")
surfHookApi.disable(this)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dev.slne.surf.surfapi.bukkit.test.hook

import dev.slne.surf.surfapi.bukkit.test.hook.condition.EnabledCondition
import dev.slne.surf.surfapi.core.api.hook.AbstractHook
import dev.slne.surf.surfapi.core.api.util.logger
import dev.slne.surf.surfapi.shared.api.hook.HookMeta
import dev.slne.surf.surfapi.shared.api.hook.requirement.ConditionalOnCustom

@ConditionalOnCustom(EnabledCondition::class)
@HookMeta
class PrimaryTestHook : AbstractHook() {
private val log = logger()

override suspend fun onBootstrap() {
log.atInfo().log("PrimaryTestHook bootstrapped")
}

override suspend fun onLoad() {
log.atInfo().log("PrimaryTestHook loaded")
}

override suspend fun onEnable() {
log.atInfo().log("PrimaryTestHook enabled")
}

override suspend fun onDisable() {
log.atInfo().log("PrimaryTestHook disabled")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.slne.surf.surfapi.bukkit.test.hook

import dev.slne.surf.surfapi.bukkit.test.BukkitPluginMain
import dev.slne.surf.surfapi.core.api.hook.AbstractHook
import dev.slne.surf.surfapi.core.api.util.logger
import dev.slne.surf.surfapi.shared.api.hook.HookMeta
import dev.slne.surf.surfapi.shared.api.hook.requirement.*

@HookMeta
@DependsOnClass(BukkitPluginMain::class)
@DependsOnClassName("dev.slne.surf.surfapi.bukkit.test.config.ModernTestConfig")
@DependsOnPlugin("SurfBukkitPluginTest")
@DependsOnOnePlugin(["SurfBukkitPlugin", "surf-bukkit-plugin", "SurfBukkitPluginTest"])
@DependsOnHook(PrimaryTestHook::class)
class TestHook : AbstractHook() {
private val log = logger()

override suspend fun onBootstrap() {
log.atInfo().log("TestHook bootstrapped")
}

override suspend fun onLoad() {
log.atInfo().log("TestHook loaded")
}

override suspend fun onEnable() {
log.atInfo().log("TestHook enabled")
}

override suspend fun onDisable() {
log.atInfo().log("TestHook disabled")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.slne.surf.surfapi.bukkit.test.hook.condition

import dev.slne.surf.surfapi.bukkit.test.config.ModernTestConfig
import dev.slne.surf.surfapi.shared.api.hook.condition.HookCondition
import dev.slne.surf.surfapi.shared.api.hook.condition.HookConditionContext

class EnabledCondition : HookCondition {
override suspend fun evaluate(context: HookConditionContext): Boolean {
return ModernTestConfig.getConfig().enabled
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package dev.slne.surf.surfapi.bukkit.server.hook

import com.google.auto.service.AutoService
import dev.slne.surf.surfapi.bukkit.api.extensions.pluginManager
import dev.slne.surf.surfapi.bukkit.server.reflection.Reflection
import dev.slne.surf.surfapi.core.server.hook.HookService
import net.kyori.adventure.text.logger.slf4j.ComponentLogger
import org.bukkit.plugin.java.JavaPlugin
import java.io.InputStream
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract

@AutoService(HookService::class)
class PaperHookService : HookService() {
override fun readHooksFileFromResources(owner: Any, fileName: String): InputStream? {
ensureOwnerIsPlugin(owner)
return owner.getResource(fileName)
}

override fun getClassloader(owner: Any): ClassLoader {
ensureOwnerIsPlugin(owner)
return Reflection.JAVA_PLUGIN_PROXY.getClassLoader(owner)
}

override fun isPluginLoaded(pluginId: String): Boolean {
return pluginManager.getPlugin(pluginId) != null
}

override fun getLogger(owner: Any): ComponentLogger {
ensureOwnerIsPlugin(owner)
return owner.componentLogger
}

@OptIn(ExperimentalContracts::class)
private fun ensureOwnerIsPlugin(owner: Any): JavaPlugin {
contract {
returns() implies (owner is JavaPlugin)
}

return owner as? JavaPlugin ?: error("Owner must be a JavaPlugin")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ data class EntityGlowingData(
@OptIn(NmsUseWithCaution::class)
fun removeFromTeam(): PacketOperation {
val color = color ?: return PacketOperationImpl.empty()
val teamData = TeamData.Companion.getByColorOrNull(color) ?: return PacketOperationImpl.empty()
val teamData = TeamData.getByColorOrNull(color) ?: return PacketOperationImpl.empty()

val operation = PacketOperation.start()
if (teamData.removeSeen(playerData.uuid)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.slne.surf.surfapi.bukkit.server.reflection

import dev.slne.surf.surfapi.core.api.reflection.Name
import dev.slne.surf.surfapi.core.api.reflection.SurfProxy
import org.bukkit.plugin.java.JavaPlugin

@SurfProxy(JavaPlugin::class)
interface JavaPluginProxy {

@Name("getClassLoader")
fun getClassLoader(instance: JavaPlugin): ClassLoader
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ object Reflection {
val ITEM_PROXY: ItemProxy
val ENTITY_PROXY: EntityProxy
val SERVER_CONNECTION_LISTENER_PROXY: ServerConnectionListenerProxy
val JAVA_PLUGIN_PROXY: JavaPluginProxy

init {
val remapper = ReflectionRemapper.forReobfMappingsInPaperJar()
val proxyFactory =
ReflectionProxyFactory.create(remapper, Reflection::class.java.getClassLoader())
ReflectionProxyFactory.create(remapper, Reflection::class.java.classLoader)

SERVER_STATS_COUNTER_PROXY = proxyFactory.reflectionProxy<ServerStatsCounterProxy>()
ITEM_PROXY = surfReflection.createProxy<ItemProxy>()
ENTITY_PROXY = proxyFactory.reflectionProxy<EntityProxy>()
SERVER_CONNECTION_LISTENER_PROXY = proxyFactory.reflectionProxy<ServerConnectionListenerProxy>()
JAVA_PLUGIN_PROXY = surfReflection.createProxy<JavaPluginProxy>()

// gc the remapper
System.gc()
Expand Down
45 changes: 42 additions & 3 deletions surf-api-core/surf-api-core-api/api/surf-api-core-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public final class dev/slne/surf/surfapi/core/api/algorithms/ConvexHull2DKt {
public static final fun convexHull2D ([Lorg/spongepowered/math/vector/Vectord;)Lit/unimi/dsi/fastutil/objects/ObjectList;
}

public final class dev/slne/surf/surfapi/core/api/algorithms/Kahn_topological_sortKt {
public static final fun topologicalSort (Ljava/util/Map;)Ljava/util/List;
public static final fun topologicalSortSafe (Ljava/util/Map;)Ljava/lang/Object;
}

public final class dev/slne/surf/surfapi/core/api/collection/TransformingObjectSet : it/unimi/dsi/fastutil/objects/ObjectSet {
public fun <init> (Lit/unimi/dsi/fastutil/objects/ObjectSet;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
public fun add (Ljava/lang/Object;)Z
Expand Down Expand Up @@ -6389,6 +6394,43 @@ public final class dev/slne/surf/surfapi/core/api/generated/VanillaAdvancementKe
public static final field UPGRADE_TOOLS Lnet/kyori/adventure/key/Key;
}

public abstract class dev/slne/surf/surfapi/core/api/hook/AbstractHook : dev/slne/surf/surfapi/shared/api/hook/Hook {
public fun <init> ()V
public final fun bootstrap (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun compareTo (Ldev/slne/surf/surfapi/shared/api/hook/Hook;)I
public synthetic fun compareTo (Ljava/lang/Object;)I
public final fun disable (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun enable (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun getPriority ()S
public final fun load (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected fun onBootstrap (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected fun onDisable (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected fun onEnable (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected fun onLoad (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class dev/slne/surf/surfapi/core/api/hook/SurfHookApi {
public static final field Companion Ldev/slne/surf/surfapi/core/api/hook/SurfHookApi$Companion;
public abstract fun bootstrap (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun disable (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun enable (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun hooks (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun hooksLoaded (Ljava/lang/Object;)Ljava/util/List;
public abstract fun hooksOfType (Ljava/lang/Class;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun hooksOfType (Ljava/lang/Object;Ljava/lang/Class;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun hooksOfTypeLoaded (Ljava/lang/Class;)Ljava/util/List;
public abstract fun hooksOfTypeLoaded (Ljava/lang/Object;Ljava/lang/Class;)Ljava/util/List;
public abstract fun load (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class dev/slne/surf/surfapi/core/api/hook/SurfHookApi$Companion {
public final fun getInstance ()Ldev/slne/surf/surfapi/core/api/hook/SurfHookApi;
}

public final class dev/slne/surf/surfapi/core/api/hook/SurfHookApiKt {
public static final fun getSurfHookApi ()Ldev/slne/surf/surfapi/core/api/hook/SurfHookApi;
}

public final class dev/slne/surf/surfapi/core/api/math/VoxelLineTracer {
public static final field INSTANCE Ldev/slne/surf/surfapi/core/api/math/VoxelLineTracer;
public final fun trace (Lorg/spongepowered/math/vector/Vector3d;Lorg/spongepowered/math/vector/Vector3d;)Lkotlin/sequences/Sequence;
Expand Down Expand Up @@ -10214,9 +10256,6 @@ public final class dev/slne/surf/surfapi/core/api/util/Fast_util_utilKt {
public static final fun toShortSet ([Ljava/lang/Short;)Lit/unimi/dsi/fastutil/shorts/ShortSet;
}

public abstract interface annotation class dev/slne/surf/surfapi/core/api/util/InternalSurfApi : java/lang/annotation/Annotation {
}

public abstract interface class dev/slne/surf/surfapi/core/api/util/ItemStackFactory {
public static final field Companion Ldev/slne/surf/surfapi/core/api/util/ItemStackFactory$Companion;
}
Expand Down
Loading