diff --git a/gradle.properties b/gradle.properties index 17c0ff57..685cec84 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ makeevrserg.java.ktarget=21 # Project makeevrserg.project.name=AspeKt makeevrserg.project.group=ru.astrainteractive.aspekt -makeevrserg.project.version.string=2.27.0 +makeevrserg.project.version.string=2.27.1 makeevrserg.project.description=Essentials plugin for EmpireProjekt makeevrserg.project.developers=makeevrserg|Makeev Roman|makeevrserg@gmail.com makeevrserg.project.url=https://empireprojekt.ru diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ae69104c..20dd55ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,7 @@ kotlin-kaml = "0.61.0" # https://github.com/charleskorn/kaml # Drivers driver-jdbc = "3.46.1.0" # https://github.com/xerial/sqlite-jdbc driver-mysql = "8.0.33" # https://github.com/mysql/mysql-connector-j +driver-h2 = "2.2.224" # https://github.com/h2database/h2database # klibs klibs-gradleplugin = "1.3.4" # https://github.com/makeevrserg/gradle-plugin @@ -22,7 +23,7 @@ minecraft-spigot = "1.21-R0.1-SNAPSHOT" # https://github.com/PaperMC/Paper minecraft-papi = "2.11.6" # https://github.com/PlaceholderAPI/PlaceholderAPI minecraft-protocollib = "5.1.0" # https://github.com/dmulloy2/ProtocolLib minecraft-vault = "1.7.1" # https://github.com/MilkBowl/VaultAPI -minecraft-astralibs = "3.13.0" # https://github.com/Astra-Interactive/AstraLibs +minecraft-astralibs = "3.14.1" # https://github.com/Astra-Interactive/AstraLibs minecraft-bstats = "3.0.2" # https://github.com/Bastian/bStats minecraft-mockbukkit = "3.108.0" #https://github.com/MockBukkit/MockBukkit minecraft-luckperms = "5.4" # https://github.com/LuckPerms/LuckPerms @@ -64,6 +65,7 @@ gradle-shadow = { module = "gradle.plugin.com.github.johnrengelman:shadow", vers # Drivers driver-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "driver-jdbc" } driver-mysql = { module = "mysql:mysql-connector-java", version.ref = "driver-mysql" } +driver-h2 = { module = "com.h2database:h2", version.ref = "driver-h2" } # Testing tests-kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test" } @@ -100,6 +102,7 @@ klibs-kstorage = { module = "ru.astrainteractive.klibs:kstorage", version.ref = # AstraLibs minecraft-astralibs-orm = { module = "ru.astrainteractive.astralibs:orm", version.ref = "minecraft-astralibs" } +minecraft-astralibs-exposed = { module = "ru.astrainteractive.astralibs:exposed", version.ref = "minecraft-astralibs" } minecraft-astralibs-core = { module = "ru.astrainteractive.astralibs:core", version.ref = "minecraft-astralibs" } minecraft-astralibs-menu-bukkit = { module = "ru.astrainteractive.astralibs:menu-bukkit", version.ref = "minecraft-astralibs" } minecraft-astralibs-core-bukkit = { module = "ru.astrainteractive.astralibs:core-bukkit", version.ref = "minecraft-astralibs" } @@ -111,7 +114,7 @@ cache4k = { module = "io.github.reactivecircus.cache4k:cache4k", version.ref = " [bundles] exposed = ["exposed-java-time", "exposed-jdbc", "exposed-dao", "exposed-core"] minecraft-bukkit = ["minecraft-paper-api", "minecraft-spigot-api", "minecraft-spigot-core", "minecraft-vaultapi", "minecraft-papi"] -testing-kotlin = ["kotlin-coroutines-core", "kotlin-coroutines-coreJvm", "kotlin-coroutines-test", "driver-jdbc", "driver-mysql", "kotlin-serialization", "kotlin-serializationJson"] +testing-kotlin = ["kotlin-coroutines-core", "kotlin-coroutines-coreJvm", "kotlin-coroutines-test", "driver-jdbc", "driver-mysql", "driver-h2", "kotlin-serialization", "kotlin-serializationJson"] kotlin = ["kotlin-coroutines-core", "kotlin-coroutines-coreJvm", "kotlin-serialization", "kotlin-serializationJson", "kotlin-serializationKaml", "kotlin-gradle"] diff --git a/modules/adminprivate/src/main/kotlin/ru/astrainteractive/aspekt/module/adminprivate/command/adminprivate/AdminPrivateCommandParser.kt b/modules/adminprivate/src/main/kotlin/ru/astrainteractive/aspekt/module/adminprivate/command/adminprivate/AdminPrivateCommandParser.kt index 088a1b5b..d540136c 100644 --- a/modules/adminprivate/src/main/kotlin/ru/astrainteractive/aspekt/module/adminprivate/command/adminprivate/AdminPrivateCommandParser.kt +++ b/modules/adminprivate/src/main/kotlin/ru/astrainteractive/aspekt/module/adminprivate/command/adminprivate/AdminPrivateCommandParser.kt @@ -8,7 +8,7 @@ import ru.astrainteractive.astralibs.command.api.argumenttype.PrimitiveArgumentT import ru.astrainteractive.astralibs.command.api.context.BukkitCommandContext import ru.astrainteractive.astralibs.command.api.context.BukkitCommandContextExt.requireArgument import ru.astrainteractive.astralibs.command.api.context.BukkitCommandContextExt.requirePermission -import ru.astrainteractive.astralibs.command.api.exception.BadArgumentException +import ru.astrainteractive.astralibs.command.api.exception.DefaultCommandException import ru.astrainteractive.astralibs.command.api.parser.CommandParser internal class AdminPrivateCommandParser : CommandParser { @@ -47,7 +47,7 @@ internal class AdminPrivateCommandParser : CommandParser throw BadArgumentException(args.getOrNull(0), PrimitiveArgumentType.String) + else -> throw DefaultCommandException.BadArgumentException(args.getOrNull(0), PrimitiveArgumentType.String) } } } diff --git a/modules/adminprivate/src/main/kotlin/ru/astrainteractive/aspekt/module/adminprivate/command/adminprivate/AdminPrivateCommandRegistry.kt b/modules/adminprivate/src/main/kotlin/ru/astrainteractive/aspekt/module/adminprivate/command/adminprivate/AdminPrivateCommandRegistry.kt index e288e13a..fc508320 100644 --- a/modules/adminprivate/src/main/kotlin/ru/astrainteractive/aspekt/module/adminprivate/command/adminprivate/AdminPrivateCommandRegistry.kt +++ b/modules/adminprivate/src/main/kotlin/ru/astrainteractive/aspekt/module/adminprivate/command/adminprivate/AdminPrivateCommandRegistry.kt @@ -2,8 +2,7 @@ package ru.astrainteractive.aspekt.module.adminprivate.command.adminprivate import ru.astrainteractive.aspekt.module.adminprivate.command.di.AdminPrivateCommandDependencies import ru.astrainteractive.aspekt.module.adminprivate.model.ChunkFlag -import ru.astrainteractive.astralibs.command.api.exception.BadArgumentException -import ru.astrainteractive.astralibs.command.api.exception.NoPermissionException +import ru.astrainteractive.astralibs.command.api.exception.DefaultCommandException import ru.astrainteractive.astralibs.command.api.util.PluginExt.registerCommand import ru.astrainteractive.astralibs.util.StringListExt.withEntry @@ -37,12 +36,20 @@ internal class AdminPrivateCommandRegistry( context.sender.sendMessage(translation.general.onlyPlayerCommand.component) } - is BadArgumentException -> with(kyoriComponentSerializer) { - context.sender.sendMessage(translation.general.wrongUsage.component) - } + is DefaultCommandException -> with(kyoriComponentSerializer) { + when (throwable) { + is DefaultCommandException.ArgumentTypeException -> { + context.sender.sendMessage(translation.general.wrongUsage.component) + } + + is DefaultCommandException.BadArgumentException -> { + context.sender.sendMessage(translation.general.wrongUsage.component) + } - is NoPermissionException -> with(kyoriComponentSerializer) { - context.sender.sendMessage(translation.general.noPermission.component) + is DefaultCommandException.NoPermissionException -> { + context.sender.sendMessage(translation.general.noPermission.component) + } + } } } } diff --git a/modules/core/src/main/kotlin/ru/astrainteractive/aspekt/util/FlowExt.kt b/modules/core/src/main/kotlin/ru/astrainteractive/aspekt/util/FlowExt.kt deleted file mode 100644 index 2e668046..00000000 --- a/modules/core/src/main/kotlin/ru/astrainteractive/aspekt/util/FlowExt.kt +++ /dev/null @@ -1,22 +0,0 @@ -package ru.astrainteractive.aspekt.util - -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.map - -object FlowExt { - /** - * Allows to map a flow and get in map history of previous mapped flow - */ - inline fun Flow.mapHistory( - crossinline transform: suspend (value: T, previous: R?) -> R - ): Flow = flow { - var latest: R? = null - this@mapHistory.map { - val current = transform.invoke(it, latest) - emit(current) - latest = current - }.collect() - } -} diff --git a/modules/economy/build.gradle.kts b/modules/economy/build.gradle.kts index 10752d5d..e18dfe0e 100644 --- a/modules/economy/build.gradle.kts +++ b/modules/economy/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { // AstraLibs implementation(libs.minecraft.astralibs.core) implementation(libs.minecraft.astralibs.orm) + implementation(libs.minecraft.astralibs.exposed) implementation(libs.minecraft.astralibs.command) implementation(libs.minecraft.astralibs.command.bukkit) implementation(libs.klibs.kdi) diff --git a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/command/ekon/EkonCommandParser.kt b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/command/ekon/EkonCommandParser.kt index 18a82a4f..4a968908 100644 --- a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/command/ekon/EkonCommandParser.kt +++ b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/command/ekon/EkonCommandParser.kt @@ -9,7 +9,7 @@ import ru.astrainteractive.astralibs.command.api.context.BukkitCommandContext import ru.astrainteractive.astralibs.command.api.context.BukkitCommandContextExt.argumentOrElse import ru.astrainteractive.astralibs.command.api.context.BukkitCommandContextExt.requireArgument import ru.astrainteractive.astralibs.command.api.context.BukkitCommandContextExt.requirePermission -import ru.astrainteractive.astralibs.command.api.exception.BadArgumentException +import ru.astrainteractive.astralibs.command.api.exception.DefaultCommandException import ru.astrainteractive.astralibs.command.api.parser.CommandParser internal class EkonCommandParser( @@ -72,7 +72,7 @@ internal class EkonCommandParser( } } - else -> throw BadArgumentException(args.getOrNull(0), PrimitiveArgumentType.String) + else -> throw DefaultCommandException.BadArgumentException(args.getOrNull(0), PrimitiveArgumentType.String) } } } diff --git a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/command/ekon/EkonCommandRegistry.kt b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/command/ekon/EkonCommandRegistry.kt index 0f300cd4..1a7378bd 100644 --- a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/command/ekon/EkonCommandRegistry.kt +++ b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/command/ekon/EkonCommandRegistry.kt @@ -9,9 +9,7 @@ import ru.astrainteractive.aspekt.module.economy.database.dao.CachedDao import ru.astrainteractive.aspekt.module.economy.database.dao.EconomyDao import ru.astrainteractive.aspekt.module.economy.model.CurrencyModel import ru.astrainteractive.aspekt.plugin.PluginTranslation -import ru.astrainteractive.astralibs.command.api.exception.ArgumentTypeException -import ru.astrainteractive.astralibs.command.api.exception.BadArgumentException -import ru.astrainteractive.astralibs.command.api.exception.NoPermissionException +import ru.astrainteractive.astralibs.command.api.exception.DefaultCommandException import ru.astrainteractive.astralibs.command.api.util.PluginExt.registerCommand import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer import ru.astrainteractive.astralibs.logging.JUtiltLogger @@ -93,12 +91,20 @@ internal class EkonCommandRegistry( ), errorHandler = { context, throwable -> when (throwable) { - is BadArgumentException -> with(kyori) { - context.sender.sendMessage(translation.general.wrongUsage.component) - } + is DefaultCommandException -> with(kyori) { + when (throwable) { + is DefaultCommandException.ArgumentTypeException -> { + context.sender.sendMessage(translation.general.wrongUsage.component) + } - is ArgumentTypeException -> with(kyori) { - context.sender.sendMessage(translation.general.wrongUsage.component) + is DefaultCommandException.BadArgumentException -> { + context.sender.sendMessage(translation.general.wrongUsage.component) + } + + is DefaultCommandException.NoPermissionException -> { + context.sender.sendMessage(translation.general.noPermission.component) + } + } } is CurrencyArgument.CurrencyNotFoundException -> with(kyori) { @@ -109,10 +115,6 @@ internal class EkonCommandRegistry( context.sender.sendMessage(translation.economy.playerNotFound.component) } - is NoPermissionException -> with(kyori) { - context.sender.sendMessage(translation.general.noPermission.component) - } - else -> with(kyori) { error { "#errorHandler handler for ${throwable::class} not found" } context.sender.sendMessage(translation.general.noPermission.component) diff --git a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/core/StringIdTable.kt b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/core/StringIdTable.kt deleted file mode 100644 index 07a0fdd2..00000000 --- a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/core/StringIdTable.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ru.astrainteractive.aspekt.module.economy.database.core - -import net.kyori.adventure.text.Component.text -import org.jetbrains.exposed.dao.id.EntityID -import org.jetbrains.exposed.dao.id.IdTable -import org.jetbrains.exposed.sql.Column - -internal open class StringIdTable(name: String = "", columnName: String = "id") : IdTable(name) { - final override val id: Column> = text(columnName).entityId() - final override val primaryKey = PrimaryKey(id) -} diff --git a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/di/EconomyDatabaseModule.kt b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/di/EconomyDatabaseModule.kt index edb37a0e..f34fb664 100644 --- a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/di/EconomyDatabaseModule.kt +++ b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/di/EconomyDatabaseModule.kt @@ -15,9 +15,10 @@ import ru.astrainteractive.aspekt.module.economy.database.dao.impl.CachedDaoImpl import ru.astrainteractive.aspekt.module.economy.database.dao.impl.EconomyDaoImpl import ru.astrainteractive.aspekt.module.economy.database.table.CurrencyTable import ru.astrainteractive.aspekt.module.economy.database.table.PlayerCurrencyTable -import ru.astrainteractive.aspekt.module.economy.model.DatabaseConfiguration -import ru.astrainteractive.aspekt.util.FlowExt.mapHistory +import ru.astrainteractive.astralibs.exposed.factory.DatabaseFactory +import ru.astrainteractive.astralibs.exposed.model.DatabaseConfiguration import ru.astrainteractive.astralibs.lifecycle.Lifecycle +import ru.astrainteractive.astralibs.util.FlowExt.mapCached import ru.astrainteractive.klibs.kstorage.api.flow.StateFlowKrate import java.io.File import kotlin.coroutines.CoroutineContext @@ -33,22 +34,9 @@ internal interface EconomyDatabaseModule { coroutineScope: CoroutineScope, ioDispatcher: CoroutineContext ) : EconomyDatabaseModule { - private val databaseFlow: Flow = dbConfig.cachedStateFlow.mapHistory { dbConfig, previous -> + private val databaseFlow: Flow = dbConfig.cachedStateFlow.mapCached { dbConfig, previous -> previous?.connector?.invoke()?.close() - - val database = when (dbConfig) { - is DatabaseConfiguration.H2 -> Database.connect( - url = "jdbc:sqlite:${dataFolder.resolve("${dbConfig.name}.db").absolutePath}", - driver = "org.sqlite.JDBC" - ) - - is DatabaseConfiguration.MySql -> Database.connect( - url = "jdbc:mysql://${dbConfig.host}:${dbConfig.port}/${dbConfig.name}", - driver = dbConfig.driver, - user = dbConfig.user, - password = dbConfig.password - ) - } + val database = DatabaseFactory(dataFolder).create(dbConfig) TransactionManager.manager.defaultIsolationLevel = java.sql.Connection.TRANSACTION_SERIALIZABLE runBlocking { transaction(database) { diff --git a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/table/CurrencyTable.kt b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/table/CurrencyTable.kt index 5fa03fff..068a5fb3 100644 --- a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/table/CurrencyTable.kt +++ b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/table/CurrencyTable.kt @@ -1,6 +1,6 @@ package ru.astrainteractive.aspekt.module.economy.database.table -import ru.astrainteractive.aspekt.module.economy.database.core.StringIdTable +import ru.astrainteractive.astralibs.exposed.table.StringIdTable internal object CurrencyTable : StringIdTable(name = "CURRENCY") { val name = text("name") diff --git a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/table/PlayerCurrencyTable.kt b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/table/PlayerCurrencyTable.kt index 73c7cae1..437744be 100644 --- a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/table/PlayerCurrencyTable.kt +++ b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/database/table/PlayerCurrencyTable.kt @@ -1,6 +1,6 @@ package ru.astrainteractive.aspekt.module.economy.database.table -import ru.astrainteractive.aspekt.module.economy.database.core.StringIdTable +import ru.astrainteractive.astralibs.exposed.table.StringIdTable internal object PlayerCurrencyTable : StringIdTable(name = "PLAYER_CURRENCY", columnName = "uuid") { val currencyId = reference("currency_id", CurrencyTable) diff --git a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/di/EconomyConfigModule.kt b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/di/EconomyConfigModule.kt index 3b931d3b..46718801 100644 --- a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/di/EconomyConfigModule.kt +++ b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/di/EconomyConfigModule.kt @@ -2,7 +2,7 @@ package ru.astrainteractive.aspekt.module.economy.di import ru.astrainteractive.aspekt.di.CoreModule import ru.astrainteractive.aspekt.module.economy.model.CurrencyConfiguration -import ru.astrainteractive.aspekt.module.economy.model.DatabaseConfiguration +import ru.astrainteractive.astralibs.exposed.model.DatabaseConfiguration import ru.astrainteractive.astralibs.lifecycle.Lifecycle import ru.astrainteractive.astralibs.logging.JUtiltLogger import ru.astrainteractive.astralibs.logging.Logger @@ -22,13 +22,19 @@ internal interface EconomyConfigModule { override val folder = coreModule.plugin.value.dataFolder.resolve("economy") override val databaseConfiguration = DefaultStateFlowMutableKrate( - factory = { DatabaseConfiguration.H2() }, + factory = { DatabaseConfiguration.H2(name = "db", arguments = emptyList()) }, loader = { folder.mkdirs() val file = folder.resolve("db.yml") if (!file.exists() || file.length() == 0L) { file.createNewFile() - coreModule.yamlFormat.writeIntoFile(DatabaseConfiguration.H2(), file) + coreModule.yamlFormat.writeIntoFile( + value = DatabaseConfiguration.H2( + name = "db", + arguments = emptyList() + ), + file = file + ) } coreModule.yamlFormat.parse(file) .onFailure { error { "#databaseConfiguration could not read db.yml: ${it.message}" } } diff --git a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/model/DatabaseConfiguration.kt b/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/model/DatabaseConfiguration.kt deleted file mode 100644 index 2f8307c6..00000000 --- a/modules/economy/src/main/kotlin/ru/astrainteractive/aspekt/module/economy/model/DatabaseConfiguration.kt +++ /dev/null @@ -1,31 +0,0 @@ -package ru.astrainteractive.aspekt.module.economy.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient - -@Serializable -internal sealed interface DatabaseConfiguration { - @Transient - val driver: String - - @SerialName("MySql") - @Serializable - class MySql( - val host: String, - val port: Int, - val user: String, - val password: String, - val name: String - ) : DatabaseConfiguration { - @Transient - override val driver: String = "com.mysql.cj.jdbc.Driver" - } - - @SerialName("H2") - @Serializable - data class H2(val name: String = "economy") : DatabaseConfiguration { - @Transient - override val driver: String = "org.h2.Driver" - } -} diff --git a/modules/economy/src/test/kotlin/ru/astrainteractive/aspekt/module/economy/database/dao/EconomyDaoTest.kt b/modules/economy/src/test/kotlin/ru/astrainteractive/aspekt/module/economy/database/dao/EconomyDaoTest.kt index d6e0e577..583819df 100644 --- a/modules/economy/src/test/kotlin/ru/astrainteractive/aspekt/module/economy/database/dao/EconomyDaoTest.kt +++ b/modules/economy/src/test/kotlin/ru/astrainteractive/aspekt/module/economy/database/dao/EconomyDaoTest.kt @@ -5,9 +5,9 @@ import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.runTest import ru.astrainteractive.aspekt.module.economy.database.di.EconomyDatabaseModule import ru.astrainteractive.aspekt.module.economy.model.CurrencyModel -import ru.astrainteractive.aspekt.module.economy.model.DatabaseConfiguration import ru.astrainteractive.aspekt.module.economy.model.PlayerCurrency import ru.astrainteractive.aspekt.module.economy.model.PlayerModel +import ru.astrainteractive.astralibs.exposed.model.DatabaseConfiguration import ru.astrainteractive.klibs.kstorage.api.impl.DefaultStateFlowMutableKrate import ru.astrainteractive.klibs.kstorage.util.KrateExt.update import java.io.File diff --git a/modules/moneydrop/build.gradle.kts b/modules/moneydrop/build.gradle.kts index 012c3562..3889c4ce 100644 --- a/modules/moneydrop/build.gradle.kts +++ b/modules/moneydrop/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { // AstraLibs implementation(libs.minecraft.astralibs.core) implementation(libs.minecraft.astralibs.orm) + implementation(libs.minecraft.astralibs.exposed) implementation(libs.klibs.kdi) implementation(libs.klibs.mikro.core) implementation(libs.minecraft.astralibs.menu.bukkit)