diff --git a/eco-api/src/main/java/com/willfp/eco/core/data/keys/KeyRegistry.java b/eco-api/src/main/java/com/willfp/eco/core/data/keys/KeyRegistry.java index 4673ee01..f1ed8713 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/data/keys/KeyRegistry.java +++ b/eco-api/src/main/java/com/willfp/eco/core/data/keys/KeyRegistry.java @@ -1,6 +1,8 @@ package com.willfp.eco.core.data.keys; +import com.willfp.eco.core.Eco; import org.bukkit.NamespacedKey; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -9,6 +11,8 @@ import java.util.Set; /** * API to register persistent data keys. */ +@ApiStatus.Internal +@Eco.HandlerComponent public interface KeyRegistry { /** * Register a persistent data key to be stored. @@ -24,6 +28,15 @@ public interface KeyRegistry { */ Set> getRegisteredKeys(); + /** + * Mark key as category. + * + * @param key The key. + * @param category The category. + */ + void markKeyAs(@NotNull PersistentDataKey key, + @NotNull KeyRegistry.KeyCategory category); + /** * Get persistent data key from namespaced key. * @@ -32,4 +45,19 @@ public interface KeyRegistry { */ @Nullable PersistentDataKey getKeyFrom(@NotNull NamespacedKey namespacedKey); + + /** + * Locations for key categorization. + */ + enum KeyCategory { + /** + * Player keys. + */ + PLAYER, + + /** + * Server keys. + */ + SERVER + } } diff --git a/eco-api/src/main/java/com/willfp/eco/core/data/keys/PersistentDataKey.java b/eco-api/src/main/java/com/willfp/eco/core/data/keys/PersistentDataKey.java index 063f19d7..35f26e95 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/data/keys/PersistentDataKey.java +++ b/eco-api/src/main/java/com/willfp/eco/core/data/keys/PersistentDataKey.java @@ -1,7 +1,6 @@ package com.willfp.eco.core.data.keys; import com.willfp.eco.core.Eco; -import com.willfp.eco.util.NamespacedKeyUtils; import org.bukkit.NamespacedKey; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,7 +13,7 @@ import java.util.Set; * * @param The type of the data. */ -public class PersistentDataKey { +public final class PersistentDataKey { /** * The key of the persistent data value. */ @@ -83,6 +82,32 @@ public class PersistentDataKey { return this.type; } + /** + * Categorize key as a server key, will register new column to MySQL + * database immediately rather than waiting for auto-categorization. + *

+ * This will improve performance. + * + * @return The key. + */ + public PersistentDataKey server() { + Eco.getHandler().getKeyRegistry().markKeyAs(this, KeyRegistry.KeyCategory.SERVER); + return this; + } + + /** + * Categorize key as a player key, will register new column to MySQL + * database immediately rather than waiting for auto-categorization. + *

+ * This will improve performance. + * + * @return The key. + */ + public PersistentDataKey player() { + Eco.getHandler().getKeyRegistry().markKeyAs(this, KeyRegistry.KeyCategory.PLAYER); + return this; + } + /** * Get all persistent data keys. * diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt index 26e9675e..52611123 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt @@ -187,10 +187,11 @@ abstract class EcoSpigotPlugin : EcoPlugin() { (Eco.getHandler() as EcoHandler).setAdventure(BukkitAudiences.create(this)) } - this.logger.info("Ignore messages about deprecated events!") - // Init FIS this.getProxy(FastItemStackFactoryProxy::class.java).create(ItemStack(Material.AIR)).unwrap() + + // Preload categorized persistent data keys + (Eco.getHandler().profileHandler as EcoProfileHandler).initialize() } override fun handleDisable() { @@ -216,7 +217,6 @@ abstract class EcoSpigotPlugin : EcoPlugin() { CustomItemsManager.registerAllItems() CustomEntitiesManager.registerAllEntities() ShopManager.registerEcoProvider() - (Eco.getHandler().profileHandler as EcoProfileHandler).runPostInit() } override fun loadIntegrationLoaders(): List { diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoKeyRegistry.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoKeyRegistry.kt index 1497bab9..efbc4c50 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoKeyRegistry.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoKeyRegistry.kt @@ -1,12 +1,14 @@ package com.willfp.eco.internal.spigot.data +import com.willfp.eco.core.Eco import com.willfp.eco.core.data.keys.KeyRegistry import com.willfp.eco.core.data.keys.PersistentDataKey import com.willfp.eco.core.data.keys.PersistentDataKeyType import org.bukkit.NamespacedKey -class EcoKeyRegistry: KeyRegistry { +class EcoKeyRegistry : KeyRegistry { private val registry = mutableMapOf>() + private val categories = mutableMapOf() override fun registerKey(key: PersistentDataKey<*>) { if (this.registry.containsKey(key.key)) { @@ -41,6 +43,11 @@ class EcoKeyRegistry: KeyRegistry { } } + override fun markKeyAs(key: PersistentDataKey<*>, category: KeyRegistry.KeyCategory) { + categories[key.key] = category + (Eco.getHandler().profileHandler as EcoProfileHandler).handler.categorize(key, category) // ew + } + override fun getKeyFrom(namespacedKey: NamespacedKey): PersistentDataKey<*>? { return registry[namespacedKey] } diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoProfile.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoProfile.kt index daba4d9e..fef4e092 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoProfile.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/EcoProfile.kt @@ -65,4 +65,4 @@ class EcoServerProfile( override fun toString(): String { return "EcoServerProfile" } -} \ No newline at end of file +} 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 2ae5bb91..83064da0 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 @@ -18,7 +18,7 @@ class EcoProfileHandler( plugin: EcoSpigotPlugin ) : ProfileHandler { private val loaded = mutableMapOf() - private val handler: DataHandler = if (useSql) MySQLDataHandler(plugin, this) else + val handler: DataHandler = if (useSql) MySQLDataHandler(plugin, this) else YamlDataHandler(plugin, this) fun loadGenericProfile(uuid: UUID): Profile { @@ -64,7 +64,7 @@ class EcoProfileHandler( handler.save() } - fun runPostInit() { - handler.runPostInit() + fun initialize() { + handler.initialize() } } \ No newline at end of file diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/KeyHelpers.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/KeyHelpers.kt index 8714f23e..2240ec9b 100644 --- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/KeyHelpers.kt +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/data/KeyHelpers.kt @@ -15,23 +15,23 @@ object KeyHelpers { val key = NamespacedKeyUtils.fromStringOrNull(split[0]) ?: return null val type = PersistentDataKeyType.valueOf(split[1]) ?: return null - return when (type.name()) { - "STRING" -> PersistentDataKey( + return when (type) { + PersistentDataKeyType.STRING -> PersistentDataKey( key, type as PersistentDataKeyType, - if(split.size >= 3) split.toList().subList(2, split.size).joinToString("") else "" + if (split.size >= 3) split.toList().subList(2, split.size).joinToString("") else "" ) - "INT" -> PersistentDataKey( + PersistentDataKeyType.INT -> PersistentDataKey( key, type as PersistentDataKeyType, split[2].toInt() ) - "DOUBLE" -> PersistentDataKey( + PersistentDataKeyType.DOUBLE -> PersistentDataKey( key, type as PersistentDataKeyType, split[2].toDouble() ) - "BOOLEAN" -> PersistentDataKey( + PersistentDataKeyType.BOOLEAN -> PersistentDataKey( key, type as PersistentDataKeyType, java.lang.Boolean.parseBoolean(split[2]) @@ -43,4 +43,4 @@ object KeyHelpers { fun serializeToString(key: PersistentDataKey<*>): String { return "${key.key};${key.type.name()};${key.defaultValue}" } -} \ No newline at end of file +} 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 6bf177ee..72310c8f 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 @@ -1,5 +1,6 @@ package com.willfp.eco.internal.spigot.data.storage +import com.willfp.eco.core.data.keys.KeyRegistry import com.willfp.eco.core.data.keys.PersistentDataKey import org.bukkit.NamespacedKey import java.util.UUID @@ -8,7 +9,11 @@ interface DataHandler { fun save() fun saveAll(uuids: Iterable) - fun runPostInit() { + fun categorize(key: PersistentDataKey<*>, category: KeyRegistry.KeyCategory) { + + } + + fun initialize() { } 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 68f71538..932be7a7 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 @@ -3,6 +3,7 @@ package com.willfp.eco.internal.spigot.data.storage import com.google.common.util.concurrent.ThreadFactoryBuilder import com.willfp.eco.core.Eco import com.willfp.eco.core.EcoPlugin +import com.willfp.eco.core.data.keys.KeyRegistry import com.willfp.eco.core.data.keys.PersistentDataKey import com.willfp.eco.core.data.keys.PersistentDataKeyType import com.willfp.eco.internal.spigot.EcoSpigotPlugin @@ -31,6 +32,7 @@ import java.util.UUID import java.util.concurrent.Callable import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Executors +import java.util.concurrent.Future @Suppress("UNCHECKED_CAST") class MySQLDataHandler( @@ -42,7 +44,8 @@ class MySQLDataHandler( UUIDTable("eco_players"), plugin, plugin.dataYml.getStrings("categorized-keys.player") - .mapNotNull { KeyHelpers.deserializeFromString(it) } + .mapNotNull { KeyHelpers.deserializeFromString(it) }, + KeyRegistry.KeyCategory.PLAYER ) private val serverHandler = ImplementedMySQLHandler( @@ -50,7 +53,8 @@ class MySQLDataHandler( UUIDTable("eco_server"), plugin, plugin.dataYml.getStrings("categorized-keys.server") - .mapNotNull { KeyHelpers.deserializeFromString(it) } + .mapNotNull { KeyHelpers.deserializeFromString(it) }, + KeyRegistry.KeyCategory.SERVER ) override fun saveAll(uuids: Iterable) { @@ -84,6 +88,14 @@ class MySQLDataHandler( } } + override fun categorize(key: PersistentDataKey<*>, category: KeyRegistry.KeyCategory) { + if (category == KeyRegistry.KeyCategory.SERVER) { + serverHandler.ensureKeyRegistration(key.key) + } else { + playerHandler.ensureKeyRegistration(key.key) + } + } + override fun save() { plugin.dataYml.set( "categorized-keys.player", @@ -98,9 +110,9 @@ class MySQLDataHandler( plugin.dataYml.save() } - override fun runPostInit() { - playerHandler.runPostInit() - serverHandler.runPostInit() + override fun initialize() { + playerHandler.initialize() + serverHandler.initialize() } } @@ -109,12 +121,14 @@ private class ImplementedMySQLHandler( private val handler: EcoProfileHandler, private val table: UUIDTable, private val plugin: EcoPlugin, - private val knownKeys: Collection> + private val knownKeys: Collection>, + private val category: KeyRegistry.KeyCategory ) { private val columns = mutableMapOf>() private val threadFactory = ThreadFactoryBuilder().setNameFormat("eco-mysql-thread-%d").build() private val executor = Executors.newFixedThreadPool(plugin.configYml.getInt("mysql.threads"), threadFactory) val registeredKeys = ConcurrentHashMap>() + private val currentlyProcessingRegistration = ConcurrentHashMap>() init { val config = HikariConfig() @@ -145,7 +159,7 @@ private class ImplementedMySQLHandler( } } - fun runPostInit() { + fun initialize() { transaction { for (key in knownKeys) { registerColumn(key, table) @@ -170,12 +184,21 @@ private class ImplementedMySQLHandler( return } - transaction { - registerColumn(persistentKey, table) - SchemaUtils.createMissingTablesAndColumns(table, withLogs = false) + val future = currentlyProcessingRegistration[key] + + if (future != null) { + future.get() + return } - registeredKeys[key] = persistentKey + currentlyProcessingRegistration[key] = executor.submit { + transaction { + registerColumn(persistentKey, table) + SchemaUtils.createMissingTablesAndColumns(table, withLogs = false) + } + registeredKeys[key] = persistentKey + currentlyProcessingRegistration.remove(key) + } } fun write(uuid: UUID, key: NamespacedKey, value: T) { diff --git a/eco-core/core-plugin/src/main/resources/data.yml b/eco-core/core-plugin/src/main/resources/data.yml index fe2f1f1b..3209391d 100644 --- a/eco-core/core-plugin/src/main/resources/data.yml +++ b/eco-core/core-plugin/src/main/resources/data.yml @@ -2,6 +2,7 @@ categorized-keys: # Preloading known keys (as of the release of 6.25.0) for optimal performance. + # This is only used when MySQL is enabled as the columns must be added each time a new key is registered. player: - ecoskills:crit_damage;INT;0 - ecoskills:strong_impact;INT;0