From 07c0e725646c19d1dc9efee3f4eaf42209a32c7a Mon Sep 17 00:00:00 2001 From: Auxilor Date: Wed, 25 May 2022 20:08:24 +0100 Subject: [PATCH] Added MongoDB data handler --- build.gradle.kts | 1 + eco-core/core-plugin/build.gradle | 2 + .../willfp/eco/internal/spigot/EcoHandler.kt | 3 +- .../internal/spigot/data/EcoProfileHandler.kt | 12 ++- .../spigot/data/storage/DataHandler.kt | 5 +- .../spigot/data/storage/HandlerType.kt | 7 ++ .../spigot/data/storage/MongoDataHandler.kt | 101 ++++++++++++++++++ .../spigot/data/storage/MySQLDataHandler.kt | 1 - .../core-plugin/src/main/resources/config.yml | 7 +- 9 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/HandlerType.kt create mode 100644 eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/MongoDataHandler.kt diff --git a/build.gradle.kts b/build.gradle.kts index 03856c86..e6e47764 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -84,6 +84,7 @@ allprojects { dependencies { // Kotlin implementation(kotlin("stdlib", version = "1.6.21")) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1") // Included in spigot jar, no need to move to implementation compileOnly("org.jetbrains:annotations:23.0.0") diff --git a/eco-core/core-plugin/build.gradle b/eco-core/core-plugin/build.gradle index 2d2b161f..89f7d3ee 100644 --- a/eco-core/core-plugin/build.gradle +++ b/eco-core/core-plugin/build.gradle @@ -14,6 +14,8 @@ dependencies { implementation 'com.zaxxer:HikariCP:5.0.0' implementation 'net.kyori:adventure-platform-bukkit:4.1.0' implementation 'org.javassist:javassist:3.28.0-GA' + implementation 'org.mongodb:mongo-java-driver:2.12.3' + implementation 'org.litote.kmongo:kmongo-coroutine:4.6.0' // Included in spigot jar compileOnly 'com.google.code.gson:gson:2.8.8' diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoHandler.kt index c18deba0..887f08a9 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoHandler.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoHandler.kt @@ -29,6 +29,7 @@ import com.willfp.eco.internal.scheduling.EcoScheduler import com.willfp.eco.internal.spigot.data.DataYml import com.willfp.eco.internal.spigot.data.EcoKeyRegistry import com.willfp.eco.internal.spigot.data.EcoProfileHandler +import com.willfp.eco.internal.spigot.data.storage.HandlerType import com.willfp.eco.internal.spigot.integrations.bstats.MetricHandler import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy @@ -57,7 +58,7 @@ class EcoHandler : EcoSpigotPlugin(), Handler { private var adventure: BukkitAudiences? = null private val keyRegistry = EcoKeyRegistry() - private val playerProfileHandler = EcoProfileHandler(this.configYml.getBool("mysql.enabled"), this) + private val playerProfileHandler = EcoProfileHandler(HandlerType.valueOf(this.configYml.getString("data-handler").uppercase()), this) @Suppress("RedundantNullableReturnType") private val keyFactory: InternalNamespacedKeyFactory? = diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoProfileHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoProfileHandler.kt index 83064da0..bd91f4e1 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoProfileHandler.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoProfileHandler.kt @@ -7,6 +7,8 @@ import com.willfp.eco.core.data.ServerProfile import com.willfp.eco.core.data.keys.PersistentDataKey import com.willfp.eco.internal.spigot.EcoSpigotPlugin import com.willfp.eco.internal.spigot.data.storage.DataHandler +import com.willfp.eco.internal.spigot.data.storage.HandlerType +import com.willfp.eco.internal.spigot.data.storage.MongoDataHandler import com.willfp.eco.internal.spigot.data.storage.MySQLDataHandler import com.willfp.eco.internal.spigot.data.storage.YamlDataHandler import java.util.UUID @@ -14,12 +16,16 @@ import java.util.UUID val serverProfileUUID = UUID(0, 0) class EcoProfileHandler( - useSql: Boolean, + type: HandlerType, plugin: EcoSpigotPlugin ) : ProfileHandler { private val loaded = mutableMapOf() - val handler: DataHandler = if (useSql) MySQLDataHandler(plugin, this) else - YamlDataHandler(plugin, this) + + val handler: DataHandler = when(type) { + HandlerType.YAML -> YamlDataHandler(plugin, this) + HandlerType.MYSQL -> MySQLDataHandler(plugin, this) + HandlerType.MONGO -> MongoDataHandler(plugin) + } fun loadGenericProfile(uuid: UUID): Profile { val found = loaded[uuid] diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/DataHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/DataHandler.kt index 3994b5bb..2315d706 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/DataHandler.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/DataHandler.kt @@ -6,7 +6,10 @@ import org.bukkit.NamespacedKey import java.util.UUID interface DataHandler { - fun save() + fun save() { + + } + fun saveAll(uuids: Iterable) fun categorize(key: PersistentDataKey<*>, category: KeyRegistry.KeyCategory) { diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/HandlerType.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/HandlerType.kt new file mode 100644 index 00000000..87534e30 --- /dev/null +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/HandlerType.kt @@ -0,0 +1,7 @@ +package com.willfp.eco.internal.spigot.data.storage + +enum class HandlerType { + YAML, + MYSQL, + MONGO +} diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/MongoDataHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/MongoDataHandler.kt new file mode 100644 index 00000000..26954c35 --- /dev/null +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/MongoDataHandler.kt @@ -0,0 +1,101 @@ +package com.willfp.eco.internal.spigot.data.storage + +import com.willfp.eco.core.EcoPlugin +import com.willfp.eco.core.data.keys.PersistentDataKey +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.bson.codecs.pojo.annotations.BsonId +import org.bukkit.NamespacedKey +import org.litote.kmongo.coroutine.CoroutineClient +import org.litote.kmongo.coroutine.CoroutineCollection +import org.litote.kmongo.coroutine.coroutine +import org.litote.kmongo.eq +import org.litote.kmongo.reactivestreams.KMongo +import org.litote.kmongo.setValue +import java.util.UUID + +@Suppress("UNCHECKED_CAST") +class MongoDataHandler( + plugin: EcoPlugin +) : DataHandler { + private val client: CoroutineClient + private val collection: CoroutineCollection + + init { + val url = plugin.configYml.getString("mongodb.url") + + client = KMongo.createClient(url).coroutine + collection = client.getDatabase("eco").getCollection() + } + + override fun saveAll(uuids: Iterable) { + for (uuid in uuids) { + saveKeysFor(uuid, PersistentDataKey.values()) + } + } + + override fun write(uuid: UUID, key: NamespacedKey, value: T) { + runBlocking { + launch { + doWrite(uuid, key, value) + } + } + } + + private suspend fun doWrite(uuid: UUID, key: NamespacedKey, value: T) { + val profile = getOrCreateDocument(uuid) + + val newData = profile.data.apply { + if (value == null) { + this.remove(key) + } else { + this[key] = value + } + } + + collection.updateOne(SerializableProfile::uuid eq uuid, setValue(SerializableProfile::data, newData)) + } + + override fun saveKeysFor(uuid: UUID, keys: Set>) { + runBlocking { + launch { + for (key in keys) { + doWrite(uuid, key.key, read(uuid, key)) + } + } + } + } + + override fun read(uuid: UUID, key: PersistentDataKey): T? { + return runBlocking { + doRead(uuid, key) + } + } + + private suspend fun doRead(uuid: UUID, key: PersistentDataKey): T? { + val profile = collection.findOne(SerializableProfile::uuid eq uuid) ?: return key.defaultValue + return profile.data[key.key] as? T? + } + + private suspend fun getOrCreateDocument(uuid: UUID): SerializableProfile { + val profile = collection.findOne(SerializableProfile::uuid eq uuid) + return if (profile == null) { + collection.insertOne( + SerializableProfile( + uuid, + mutableMapOf() + ) + ) + + getOrCreateDocument(uuid) + } else { + profile + } + } +} + +private data class SerializableProfile( + @BsonId + val uuid: UUID, + val data: MutableMap +) diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/MySQLDataHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/MySQLDataHandler.kt index 4c18dbe8..2a9ba232 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/MySQLDataHandler.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/storage/MySQLDataHandler.kt @@ -45,7 +45,6 @@ class MySQLDataHandler( private val serverHandler: ImplementedMySQLHandler init { - val config = HikariConfig() config.driverClassName = "com.mysql.cj.jdbc.Driver" config.username = plugin.configYml.getString("mysql.user") diff --git a/eco-core/core-plugin/src/main/resources/config.yml b/eco-core/core-plugin/src/main/resources/config.yml index baa4f6ec..0c448cc1 100644 --- a/eco-core/core-plugin/src/main/resources/config.yml +++ b/eco-core/core-plugin/src/main/resources/config.yml @@ -3,8 +3,13 @@ # by Auxilor # +handler-type: yaml # Pick from yaml/mongo/mysql - MongoDB is recommended over MySQL for networks. + +mongodb: + # The full MongoDB connection URL. + url: "" + mysql: - enabled: false # Set to false, data.yml will be used instead. # How many threads to execute statements on. Higher numbers can be faster however # very high numbers can cause issues with OS configuration. If writes are taking # too long, increase this value.