Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ 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
import dev.slne.surf.surfapi.core.api.component.surfComponentApi

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

surfHookApi.load(this)
surfComponentApi.load(this)
packetListenerApi.registerListeners(ChatListener())
TestInventoryView.register()
}
Expand All @@ -27,12 +27,12 @@ class BukkitPluginMain : SuspendingJavaPlugin() {
SurfApiTestCommand().register()
Reflection::class.java.getClassLoader() // initialize Reflection

surfHookApi.enable(this)
surfComponentApi.enable(this)
}

override suspend fun onDisableAsync() {
CommandAPI.unregister("surfapitest")
surfHookApi.disable(this)
surfComponentApi.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.component

import dev.slne.surf.surfapi.bukkit.test.component.condition.EnabledCondition
import dev.slne.surf.surfapi.core.api.component.AbstractComponent
import dev.slne.surf.surfapi.core.api.util.logger
import dev.slne.surf.surfapi.shared.api.component.ComponentMeta
import dev.slne.surf.surfapi.shared.api.component.requirement.ConditionalOn

@ConditionalOn(EnabledCondition::class)
@ComponentMeta
class PrimaryTestComponent : AbstractComponent() {
private val log = logger()

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

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

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

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

import dev.slne.surf.surfapi.bukkit.test.BukkitPluginMain
import dev.slne.surf.surfapi.core.api.component.AbstractComponent
import dev.slne.surf.surfapi.core.api.util.logger
import dev.slne.surf.surfapi.shared.api.component.ComponentMeta
import dev.slne.surf.surfapi.shared.api.component.Priority
import dev.slne.surf.surfapi.shared.api.component.requirement.*

@ComponentMeta
@Priority(10)
@DependsOnClass(BukkitPluginMain::class)
@DependsOnClassName("dev.slne.surf.surfapi.bukkit.test.config.ModernTestConfig")
@DependsOnPlugin("SurfBukkitPluginTest")
@DependsOnOnePlugin(["SurfBukkitPlugin", "surf-bukkit-plugin", "SurfBukkitPluginTest"])
@DependsOnComponent(PrimaryTestComponent::class)
class TestComponent : AbstractComponent() {
private val log = logger()

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

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

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

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

import dev.slne.surf.surfapi.bukkit.test.config.ModernTestConfig
import dev.slne.surf.surfapi.shared.api.component.condition.ComponentCondition
import dev.slne.surf.surfapi.shared.api.component.condition.ComponentConditionContext

class EnabledCondition : ComponentCondition {
override suspend fun evaluate(context: ComponentConditionContext): Boolean {
return ModernTestConfig.getConfig().enabled
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.slne.surf.surfapi.bukkit.test.component.processor

import dev.slne.surf.surfapi.core.api.util.logger
import dev.slne.surf.surfapi.shared.api.component.Component
import dev.slne.surf.surfapi.shared.api.component.processor.ComponentContext
import dev.slne.surf.surfapi.shared.api.component.processor.ComponentPostProcessor

/**
* Example ComponentPostProcessor implementation that is auto-discovered.
* No annotation needed - just implementing the interface is enough.
*/
class TestLoggingPostProcessor : ComponentPostProcessor {
private val log = logger()

override val priority: Int = 5

override suspend fun postProcessAfterInitialization(
component: Component,
componentName: String,
context: ComponentContext
): Component {
log.atInfo().log("Component initialized: $componentName")
return component
}

override suspend fun postProcessBeforeDestruction(
component: Component,
componentName: String,
context: ComponentContext
) {
log.atInfo().log("Component being destroyed: $componentName")
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package dev.slne.surf.surfapi.bukkit.server.hook
package dev.slne.surf.surfapi.bukkit.server.component

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 dev.slne.surf.surfapi.core.server.component.ComponentService
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? {
@AutoService(ComponentService::class)
class PaperComponentService : ComponentService() {
override fun readComponentsFileFromResources(owner: Any, fileName: String): InputStream? {
ensureOwnerIsPlugin(owner)
return owner.getResource(fileName)
}
Expand All @@ -39,4 +39,4 @@ class PaperHookService : HookService() {

return owner as? JavaPlugin ?: error("Owner must be a JavaPlugin")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package dev.slne.surf.surfapi.core.api.component

import dev.slne.surf.surfapi.shared.api.component.Component
import dev.slne.surf.surfapi.shared.api.component.ComponentMeta
import dev.slne.surf.surfapi.shared.api.component.Priority
import dev.slne.surf.surfapi.shared.api.util.InternalSurfApi
import java.util.concurrent.atomic.AtomicBoolean

/**
* Abstract base class for components that provides lifecycle management.
*
* This class handles the component lifecycle by ensuring each phase is only
* executed once and in the correct order. Subclasses should override the
* protected `on*` methods to implement their functionality.
*
* The priority is determined by the [@Priority][Priority] annotation on the class
* or its meta-annotations. If no priority is specified, the default is 0.
*
* Example:
* ```kotlin
* @ComponentMeta
* @Priority(10)
* class MyComponent : AbstractComponent() {
* override suspend fun onEnable() {
* // Initialize component
* }
*
* override suspend fun onDisable() {
* // Cleanup
* }
* }
* ```
*
* @see Component
* @see ComponentMeta
* @see Priority
*/
abstract class AbstractComponent : Component {
private val bootstrapped = AtomicBoolean(false)
private val loaded = AtomicBoolean(false)
private val enabled = AtomicBoolean(false)
private val disabled = AtomicBoolean(false)

init {
// Validate that the class has @ComponentMeta (directly or via meta-annotation)
val hasComponentMeta = javaClass.getAnnotation(ComponentMeta::class.java) != null
|| findMetaAnnotation<ComponentMeta>() != null
if (!hasComponentMeta) {
error("ComponentMeta annotation is missing on component class ${this::class.qualifiedName}")
}
}

final override val priority: Short = findPriority()

/**
* Finds the priority from @Priority annotation on the class or its meta-annotations.
* Direct annotations take precedence over meta-annotations.
*/
private fun findPriority(): Short {
// First check for direct @Priority annotation
javaClass.getAnnotation(Priority::class.java)?.let { return it.value }

// Then check meta-annotations for @Priority
findMetaAnnotation<Priority>()?.let { return it.value }

// Default priority
return 0
}

/**
* Searches for an annotation on the class's annotations (meta-annotation support)
*/
private inline fun <reified T : Annotation> findMetaAnnotation(): T? {
return javaClass.annotations
.mapNotNull { it.annotationClass.java.getAnnotation(T::class.java) }
.firstOrNull()
}

@InternalSurfApi
final override suspend fun bootstrap() {
if (bootstrapped.compareAndSet(false, true)) {
onBootstrap()
}
}

@InternalSurfApi
final override suspend fun load() {
if (loaded.compareAndSet(false, true)) {
bootstrap()
onLoad()
}
}

@InternalSurfApi
final override suspend fun enable() {
if (enabled.compareAndSet(false, true)) {
load()
onEnable()
}
}

@InternalSurfApi
final override suspend fun disable() {
if (disabled.compareAndSet(false, true)) {
onDisable()
}
}

final override fun compareTo(other: Component): Int {
return this.priority.compareTo(other.priority)
}

/**
* Called during the bootstrap phase.
* Override to perform early initialization.
*/
protected open suspend fun onBootstrap() {}

/**
* Called during the load phase.
* Override to load configuration and resources.
*/
protected open suspend fun onLoad() {}

/**
* Called during the enable phase.
* Override to activate component functionality.
*/
protected open suspend fun onEnable() {}

/**
* Called during the disable phase.
* Override to clean up resources.
*/
protected open suspend fun onDisable() {}
}
Loading