Created
March 10, 2023 00:07
-
-
Save JSH32/70cedfc288b933da875267c35ea44d61 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.jsh32.paradoxia.commons | |
import net.minestom.server.MinecraftServer | |
import net.minestom.server.command.builder.Command | |
import net.minestom.server.event.EventNode | |
import org.koin.core.component.KoinComponent | |
import org.koin.core.context.loadKoinModules | |
import org.koin.core.context.unloadKoinModules | |
import org.koin.core.definition.Definition | |
import org.koin.core.module.Module | |
import org.koin.core.qualifier.qualifier | |
import org.koin.dsl.module | |
import org.koin.java.KoinJavaComponent | |
import org.slf4j.Logger | |
import org.slf4j.LoggerFactory | |
import java.util.* | |
import kotlin.reflect.KClass | |
internal typealias FeatureDefinition = Pair<KClass<out Feature>, Definition<out Feature>> | |
/** | |
* Builder for initializing features | |
*/ | |
class FeatureBuilder { | |
/** | |
* Features that will be added later | |
*/ | |
internal val features = mutableListOf<FeatureDefinition>() | |
fun addFeature(qualifier: KClass<out Feature>, definition: Definition<out Feature>) { features.add(Pair(qualifier, definition)) } | |
inline fun <reified T: Feature> feature(noinline definition: Definition<T>) { addFeature(T::class, definition) } | |
} | |
/** | |
* Feature manager. | |
*/ | |
internal class FeatureManager { | |
private data class FeatureBox(val ctx: FeatureContext, val module: Module) | |
private val features = IdentityHashMap<KClass<*>, FeatureBox>() | |
/** | |
* Initialize all features. This will inject them into Koin. | |
*/ | |
suspend fun initialize(toAdd: List<FeatureDefinition>) { | |
// This should create and inject everything in proper order | |
val modules = toAdd.map { (type, feature) -> | |
// Inject with class name and with qualifier. | |
Pair(type, module { single(qualifier(type.qualifiedName!!), definition = feature) }) | |
} | |
// Load all modules. | |
loadKoinModules(modules.map { it.second }) | |
// Initialize the modules after injection. | |
modules.forEach { (type, module) -> | |
if (features[type] != null) { | |
throw IllegalArgumentException("Feature ${type.simpleName} already loaded.") | |
} | |
val context = FeatureContext(type) | |
KoinJavaComponent.getKoin().get<Feature>(qualifier(type.qualifiedName!!)).initialize(context) | |
context.logger.info("${type.simpleName} initialized.") | |
features[type] = FeatureBox(context, module) | |
} | |
} | |
/** | |
* De-initialize all features. | |
*/ | |
suspend fun deInitialize() { | |
val koin = KoinJavaComponent.getKoin() | |
features.forEach { (type, box) -> | |
val feature = koin.get<Feature>(qualifier(type.qualifiedName!!)) | |
feature.deInitialize(box.ctx) | |
box.ctx.destroy() | |
features.remove(type) | |
// Remove from the DI system. | |
unloadKoinModules(box.module) | |
} | |
} | |
} | |
class FeatureContext internal constructor(type: KClass<out Feature>) { | |
private val commands = mutableSetOf<Command>() | |
/** | |
* Logger for the | |
*/ | |
val logger: Logger = LoggerFactory.getLogger(type.simpleName!!) | |
val eventNode = run { | |
val eventNode = EventNode.all(type.simpleName!!) | |
MinecraftServer.getGlobalEventHandler().addChild(eventNode) | |
eventNode | |
} | |
fun registerCommand(command: Command) { | |
commands.add(command) | |
MinecraftServer.getCommandManager().register(command) | |
} | |
internal fun destroy() { | |
MinecraftServer.getGlobalEventHandler().removeChild(eventNode) | |
commands.forEach { MinecraftServer.getCommandManager().unregister(it) } | |
} | |
} | |
/** | |
* A concept for loading modules of the server, similar-ish to extensions. | |
* Common registration functions are provided on [FeatureContext]. These should be used over accessing the | |
* server directly, because they can be transparently extended to support unloading facets if this ever | |
* becomes a desired behavior. | |
*/ | |
abstract class Feature : KoinComponent { | |
abstract suspend fun initialize(context: FeatureContext) | |
abstract suspend fun deInitialize(context: FeatureContext) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment