Started work on new persistent data
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package com.willfp.eco.core.data.handlers;
|
||||
|
||||
import com.willfp.eco.core.data.keys.PersistentDataKey;
|
||||
import com.willfp.eco.core.registry.Registrable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class PersistentDataHandler implements Registrable {
|
||||
/**
|
||||
* The id of the handler.
|
||||
*/
|
||||
private final String id;
|
||||
|
||||
/**
|
||||
* Create a new persistent data handler.
|
||||
*
|
||||
* @param id The id of the handler.
|
||||
*/
|
||||
protected PersistentDataHandler(@NotNull final String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a key from persistent data.
|
||||
*
|
||||
* @param uuid The uuid.
|
||||
* @param key The key.
|
||||
* @param <T> The type of the key.
|
||||
* @return The value, or null if not found.
|
||||
*/
|
||||
@Nullable
|
||||
public abstract <T> T read(@NotNull UUID uuid, @NotNull PersistentDataKey<T> key);
|
||||
|
||||
/**
|
||||
* Write a key to persistent data.
|
||||
*
|
||||
* @param uuid The uuid.
|
||||
* @param key The key.
|
||||
* @param value The value.
|
||||
* @param <T> The type of the key.
|
||||
*/
|
||||
public abstract <T> void write(@NotNull UUID uuid, @NotNull PersistentDataKey<T> key, @NotNull T value);
|
||||
|
||||
/**
|
||||
* Serialize data.
|
||||
*
|
||||
* @param keys The keys to serialize.
|
||||
* @return The serialized data.
|
||||
*/
|
||||
@NotNull
|
||||
public abstract Set<SerializedProfile> serializeData(@NotNull final Set<PersistentDataKey<?>> keys);
|
||||
|
||||
/**
|
||||
* Load profile data.
|
||||
*
|
||||
* @param data The data.
|
||||
*/
|
||||
public abstract void loadProfileData(@NotNull Set<SerializedProfile> data);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.willfp.eco.core.data.handlers;
|
||||
|
||||
import com.willfp.eco.core.registry.Registry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Utility class to manage persistent data handlers.
|
||||
*/
|
||||
public final class PersistentDataHandlers {
|
||||
private static final Registry<PersistentDataHandler> REGISTRY = new Registry<>();
|
||||
|
||||
/**
|
||||
* Register a persistent data handler.
|
||||
*
|
||||
* @param handler The handler.
|
||||
*/
|
||||
public static void register(@NotNull final PersistentDataHandler handler) {
|
||||
REGISTRY.register(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a persistent data handler by id.
|
||||
*
|
||||
* @param id The id.
|
||||
* @return The handler.
|
||||
* @throws IllegalArgumentException if no handler with that id is found.
|
||||
*/
|
||||
@NotNull
|
||||
public static PersistentDataHandler get(@NotNull final String id) {
|
||||
PersistentDataHandler handler = REGISTRY.get(id);
|
||||
|
||||
if (handler == null) {
|
||||
throw new IllegalArgumentException("No handler with id: " + id);
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
private PersistentDataHandlers() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.willfp.eco.core.data.handlers;
|
||||
|
||||
import com.willfp.eco.core.data.keys.PersistentDataKey;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Serialized profile.
|
||||
*
|
||||
* @param uuid The uuid.
|
||||
* @param data The data.
|
||||
*/
|
||||
public record SerializedProfile(
|
||||
@NotNull UUID uuid,
|
||||
@NotNull Map<PersistentDataKey<?>, Object> data
|
||||
) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.willfp.eco.internal.spigot.data.handlers
|
||||
|
||||
import com.willfp.eco.core.config.interfaces.Config
|
||||
import com.willfp.eco.core.data.handlers.PersistentDataHandler
|
||||
import com.willfp.eco.core.data.handlers.SerializedProfile
|
||||
import com.willfp.eco.core.data.keys.PersistentDataKey
|
||||
import com.willfp.eco.core.data.keys.PersistentDataKeyType
|
||||
import com.willfp.eco.internal.spigot.EcoSpigotPlugin
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.Executors
|
||||
import com.mongodb.kotlin.client.coroutine.MongoClient
|
||||
import com.willfp.eco.core.config.Configs
|
||||
import com.willfp.eco.internal.spigot.data.storage.UUIDProfile
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.forEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.bson.Document
|
||||
|
||||
class MongoPersistentDataHandler(
|
||||
config: Config,
|
||||
plugin: EcoSpigotPlugin
|
||||
) : PersistentDataHandler("yaml") {
|
||||
|
||||
private val url: String = config.getString("url") ?: error("MongoDB URL not found in config")
|
||||
private val databaseName: String = config.getString("database") ?: error("Database name not found in config")
|
||||
private val client = MongoClient.create(url)
|
||||
private val database = client.getDatabase(databaseName)
|
||||
private val collection = database.getCollection<UUIDProfile>("uuidprofile")
|
||||
private val executor = Executors.newCachedThreadPool()
|
||||
|
||||
override fun <T: Any> read(uuid: UUID, key: PersistentDataKey<T>): T? {
|
||||
return runBlocking {
|
||||
doRead(uuid, key)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T: Any> doRead(uuid: UUID, key: PersistentDataKey<T>): T? {
|
||||
val document = collection.find(Document("uuid", uuid.toString())).firstOrNull() ?: return null
|
||||
val data = document.data[key.key.toString()] as? T
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
override fun <T : Any> write(uuid: UUID, key: PersistentDataKey<T>, value: T) {
|
||||
executor.submit {
|
||||
runBlocking {
|
||||
doWrite(uuid, key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T : Any> doWrite(uuid: UUID, key: PersistentDataKey<T>, value: T) {
|
||||
val document = collection.find(Document("uuid", uuid.toString())).firstOrNull() ?: return null
|
||||
document.data[key.key.toString()] = value
|
||||
|
||||
collection.replaceOne(Document("uuid", uuid.toString()), document)
|
||||
}
|
||||
|
||||
override fun serializeData(keys: Set<PersistentDataKey<*>>): Set<SerializedProfile> {
|
||||
val profiles = mutableSetOf<SerializedProfile>()
|
||||
|
||||
collection.find().forEach { document ->
|
||||
val uuid = UUID.fromString(document.getString("uuid"))
|
||||
val data = document.get("data") as Document
|
||||
val profileData = keys.associateWith { key ->
|
||||
when (key.type) {
|
||||
PersistentDataKeyType.STRING -> data.getString(key.key.key)
|
||||
PersistentDataKeyType.BOOLEAN -> data.getBoolean(key.key.key)
|
||||
PersistentDataKeyType.INT -> data.getInteger(key.key.key)
|
||||
PersistentDataKeyType.DOUBLE -> data.getDouble(key.key.key)
|
||||
PersistentDataKeyType.STRING_LIST -> data.getList(key.key.key, String::class.java)
|
||||
PersistentDataKeyType.BIG_DECIMAL -> data.getDecimal128(key.key.key)?.bigDecimalValue()
|
||||
PersistentDataKeyType.CONFIG -> data.get(key.key.key)
|
||||
else -> null
|
||||
} ?: key.defaultValue
|
||||
}
|
||||
|
||||
profiles.add(SerializedProfile(uuid, profileData as Map<PersistentDataKey<*>, Any>))
|
||||
}
|
||||
|
||||
return profiles
|
||||
}
|
||||
|
||||
override fun loadProfileData(data: Set<SerializedProfile>) {
|
||||
data.forEach { profile ->
|
||||
val document = Document("uuid", profile.uuid.toString())
|
||||
val profileData = Document()
|
||||
|
||||
profile.data.forEach { (key, value) ->
|
||||
profileData.put(key.key.key, value)
|
||||
}
|
||||
|
||||
document.put("data", profileData)
|
||||
collection.replaceOne(Document("uuid", profile.uuid.toString()), document, com.mongodb.client.model.ReplaceOptions().upsert(true))
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
internal data class UUIDProfile(
|
||||
// Storing UUID as strings for serialization
|
||||
@SerialName("_id") val uuid: String,
|
||||
// Storing NamespacedKeys as strings for serialization
|
||||
val data: MutableMap<String, @Contextual Any>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.willfp.eco.internal.spigot.data.handlers
|
||||
|
||||
import com.willfp.eco.core.data.handlers.PersistentDataHandler
|
||||
import com.willfp.eco.core.data.handlers.SerializedProfile
|
||||
import com.willfp.eco.core.data.keys.PersistentDataKey
|
||||
import com.willfp.eco.core.data.keys.PersistentDataKeyType
|
||||
import com.willfp.eco.internal.spigot.EcoSpigotPlugin
|
||||
import java.util.UUID
|
||||
|
||||
class YamlPersistentDataHandler(
|
||||
plugin: EcoSpigotPlugin
|
||||
) : PersistentDataHandler("yaml") {
|
||||
private val dataYml = plugin.dataYml
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : Any> read(uuid: UUID, key: PersistentDataKey<T>): T? {
|
||||
// Separate `as T?` for each branch to prevent compiler warnings.
|
||||
val value = when (key.type) {
|
||||
PersistentDataKeyType.INT -> dataYml.getIntOrNull("player.$uuid.${key.key}") as T?
|
||||
PersistentDataKeyType.DOUBLE -> dataYml.getDoubleOrNull("player.$uuid.${key.key}") as T?
|
||||
PersistentDataKeyType.STRING -> dataYml.getStringOrNull("player.$uuid.${key.key}") as T?
|
||||
PersistentDataKeyType.BOOLEAN -> dataYml.getBoolOrNull("player.$uuid.${key.key}") as T?
|
||||
PersistentDataKeyType.STRING_LIST -> dataYml.getStringsOrNull("player.$uuid.${key.key}") as T?
|
||||
PersistentDataKeyType.CONFIG -> dataYml.getSubsectionOrNull("player.$uuid.${key.key}") as T?
|
||||
PersistentDataKeyType.BIG_DECIMAL -> dataYml.getBigDecimalOrNull("player.$uuid.${key.key}") as T?
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
override fun <T : Any> write(uuid: UUID, key: PersistentDataKey<T>, value: T) {
|
||||
dataYml.set("player.$uuid.$key", value)
|
||||
}
|
||||
|
||||
override fun serializeData(keys: Set<PersistentDataKey<*>>): Set<SerializedProfile> {
|
||||
val profiles = mutableSetOf<SerializedProfile>()
|
||||
val uuids = dataYml.getSubsection("player").getKeys(false).map { UUID.fromString(it) }
|
||||
|
||||
for (uuid in uuids) {
|
||||
val data = mutableMapOf<PersistentDataKey<*>, Any>()
|
||||
|
||||
for (key in keys) {
|
||||
data[key] = read(uuid, key) ?: continue
|
||||
}
|
||||
|
||||
profiles.add(SerializedProfile(uuid, data))
|
||||
}
|
||||
|
||||
return profiles
|
||||
}
|
||||
|
||||
override fun loadProfileData(data: Set<SerializedProfile>) {
|
||||
for (profile in data) {
|
||||
for ((key, value) in profile.data) {
|
||||
// Dirty cast, but it's fine because we know it's the same type
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
write(profile.uuid, key as PersistentDataKey<Any>, value as Any)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.willfp.eco.internal.spigot.data.storage
|
||||
|
||||
import com.mongodb.client.model.Filters
|
||||
import com.mongodb.client.model.ReplaceOptions
|
||||
import com.mongodb.client.model.UpdateOptions
|
||||
import com.mongodb.client.model.Updates
|
||||
import com.mongodb.kotlin.client.coroutine.MongoClient
|
||||
import com.mongodb.kotlin.client.coroutine.MongoCollection
|
||||
@@ -11,15 +10,13 @@ import com.willfp.eco.internal.spigot.EcoSpigotPlugin
|
||||
import com.willfp.eco.internal.spigot.data.ProfileHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.bson.codecs.pojo.annotations.BsonId
|
||||
import java.util.UUID
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.bukkit.Bukkit
|
||||
import java.util.UUID
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class MongoDataHandler(
|
||||
|
||||
Reference in New Issue
Block a user