diff --git a/build.gradle b/build.gradle index 09c11776..d79e928d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,13 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21" + } +} + plugins { id 'java-library' id 'com.github.johnrengelman.shadow' version '7.0.0' @@ -14,6 +24,7 @@ allprojects { apply plugin: 'java' apply plugin: 'maven-publish' apply plugin: 'com.github.johnrengelman.shadow' + apply plugin: 'kotlin' repositories { mavenCentral() @@ -53,6 +64,7 @@ allprojects { compileOnly 'com.willfp:eco:6.9.4' compileOnly 'org.jetbrains:annotations:19.0.0' + compileOnly 'org.jetbrains.kotlin:kotlin-stdlib:1.5.21' compileOnly 'org.projectlombok:lombok:1.18.20' annotationProcessor 'org.projectlombok:lombok:1.18.20' diff --git a/eco-core/core-plugin/build.gradle b/eco-core/core-plugin/build.gradle index be5052be..2a12f2eb 100644 --- a/eco-core/core-plugin/build.gradle +++ b/eco-core/core-plugin/build.gradle @@ -9,4 +9,8 @@ dependencies { compileOnly 'net.essentialsx:EssentialsX:2.19.0' compileOnly 'io.papermc.paper:paper-api:1.17.1-R0.1-SNAPSHOT' compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.4-SNAPSHOT' + compileOnly 'org.jetbrains.exposed:exposed-core:0.34.1' + compileOnly 'org.jetbrains.exposed:exposed-dao:0.34.1' + compileOnly 'org.jetbrains.exposed:exposed-jdbc:0.34.1' + compileOnly 'mysql:mysql-connector-java:5.1.48' } \ No newline at end of file diff --git a/eco-core/core-plugin/src/main/java/com/willfp/ecoenchants/EcoEnchantsPlugin.java b/eco-core/core-plugin/src/main/java/com/willfp/ecoenchants/EcoEnchantsPlugin.java index 21cca9d2..16261677 100644 --- a/eco-core/core-plugin/src/main/java/com/willfp/ecoenchants/EcoEnchantsPlugin.java +++ b/eco-core/core-plugin/src/main/java/com/willfp/ecoenchants/EcoEnchantsPlugin.java @@ -11,6 +11,9 @@ import com.willfp.ecoenchants.command.CommandEnchantinfo; import com.willfp.ecoenchants.config.RarityYml; import com.willfp.ecoenchants.config.TargetYml; import com.willfp.ecoenchants.config.VanillaEnchantsYml; +import com.willfp.ecoenchants.data.storage.DataHandler; +import com.willfp.ecoenchants.data.storage.MySQLDataHandler; +import com.willfp.ecoenchants.data.storage.YamlDataHandler; import com.willfp.ecoenchants.display.EnchantDisplay; import com.willfp.ecoenchants.enchantments.EcoEnchant; import com.willfp.ecoenchants.enchantments.EcoEnchants; @@ -60,6 +63,12 @@ public class EcoEnchantsPlugin extends EcoPlugin { @Getter private final VanillaEnchantsYml vanillaEnchantsYml; + /** + * The data handler. + */ + @Getter + private final DataHandler dataHandler; + /** * Internal constructor called by bukkit on plugin load. */ @@ -70,6 +79,8 @@ public class EcoEnchantsPlugin extends EcoPlugin { rarityYml = new RarityYml(this); targetYml = new TargetYml(this); vanillaEnchantsYml = new VanillaEnchantsYml(this); + dataHandler = this.getConfigYml().getBool("mysql.enabled") + ? new MySQLDataHandler(this) : new YamlDataHandler(this); } @Override diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/SaveHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/SaveHandler.kt new file mode 100644 index 00000000..ccbce855 --- /dev/null +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/SaveHandler.kt @@ -0,0 +1,30 @@ +package com.willfp.ecoenchants.data + +import com.willfp.ecoenchants.EcoEnchantsPlugin +import com.willfp.ecoenchants.data.storage.PlayerProfile +import org.bukkit.Bukkit + +class SaveHandler { + companion object { + fun save(plugin: EcoEnchantsPlugin) { + if (Bukkit.getOnlinePlayers().isEmpty()) { + return + } + if (plugin.configYml.getBool("autosave.log")) { + plugin.logger.info("Auto-Saving player data!") + } + PlayerProfile.saveAll(plugin.configYml.getBool("autosave.async")) + if (plugin.configYml.getBool("autosave.log")) { + plugin.logger.info("Saved data!") + } + } + } + + class Runnable( + private val plugin: EcoEnchantsPlugin + ) : java.lang.Runnable { + override fun run() { + save(plugin) + } + } +} \ No newline at end of file diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/DataHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/DataHandler.kt new file mode 100644 index 00000000..272ea939 --- /dev/null +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/DataHandler.kt @@ -0,0 +1,13 @@ +package com.willfp.ecoenchants.data.storage + +import java.util.* + +interface DataHandler { + fun save() + + fun <T> write(uuid: UUID, key: String, value: T) + fun <T> read(uuid: UUID, key: String): T? + fun <T : Any> read(uuid: UUID, key: String, default: T): T { + return read<T>(uuid, key) ?: default + } +} \ No newline at end of file diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/MySQLDataHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/MySQLDataHandler.kt new file mode 100644 index 00000000..fb8f4fb6 --- /dev/null +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/MySQLDataHandler.kt @@ -0,0 +1,69 @@ +package com.willfp.ecoenchants.data.storage + +import com.willfp.ecoenchants.EcoEnchantsPlugin +import org.jetbrains.exposed.dao.id.EntityID +import org.jetbrains.exposed.dao.id.UUIDTable +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.util.* + +@Suppress("UNCHECKED_CAST") +class MySQLDataHandler( + plugin: EcoEnchantsPlugin +) : DataHandler { + init { + Database.connect( + "jdbc:mysql://" + + "${plugin.configYml.getString("mysql.host")}:" + + "${plugin.configYml.getString("mysql.port")}/" + + plugin.configYml.getString("mysql.database"), + driver = "com.mysql.cj.jdbc.Driver", + user = plugin.configYml.getString("mysql.user"), + password = plugin.configYml.getString("mysql.password") + ) + + transaction { + Players.apply { + /* + Optional for auto-add fields + */ + } + + SchemaUtils.create(Players) + } + } + + override fun save() { + // Do nothing + } + + override fun <T> write(uuid: UUID, key: String, value: T) { + transaction { + Players.select { Players.id eq uuid }.firstOrNull() ?: run { + Players.insert { + it[this.id] = uuid + } + } + val column: Column<T> = Players.columns.stream().filter { it.name == key }.findFirst().get() as Column<T> + Players.update({ Players.id eq uuid }) { + it[column] = value + } + } + } + + override fun <T> read(uuid: UUID, key: String): T? { + var value: T? = null + transaction { + val player = Players.select { Players.id eq uuid }.firstOrNull() ?: return@transaction + value = player[Players.columns.stream().filter { it.name == key }.findFirst().get()] as T? + } + return value + } + + object Players : UUIDTable("EcoEnchants_Players") { + override val id: Column<EntityID<UUID>> = uuid("uuid") + .entityId() + val descriptions = bool("descriptions") + .default(true) + } +} \ No newline at end of file diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/PlayerProfile.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/PlayerProfile.kt new file mode 100644 index 00000000..6dc28943 --- /dev/null +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/PlayerProfile.kt @@ -0,0 +1,85 @@ +package com.willfp.ecoenchants.data.storage + +import com.willfp.ecoenchants.EcoEnchantsPlugin +import org.bukkit.OfflinePlayer +import java.util.* + +@Suppress("UNCHECKED_CAST") +class PlayerProfile private constructor( + private val data: MutableMap<String, Any> +) { + fun <T : Any> write(key: String, value: T) { + data[key] = value + } + + fun <T : Any> read(key: String, default: T): T { + return data[key] as T? ?: default + } + + companion object { + private val handler = EcoEnchantsPlugin.getInstance().dataHandler + private val loaded = mutableMapOf<UUID, PlayerProfile>() + private val keys = mutableMapOf<String, Type>() + + private fun load(uuid: UUID): PlayerProfile { + val found = loaded[uuid] + if (found != null) { + return found + } + + val data = mutableMapOf<String, Any>() + for ((key, type) in keys) { + when (type) { + Type.INT -> data[key] = handler.read(uuid, key, 0) + Type.DOUBLE -> data[key] = handler.read(uuid, key, 0.0) + Type.STRING -> data[key] = handler.read(uuid, key, "Unknown") + Type.BOOLEAN -> data[key] = handler.read(uuid, key, false) + } + } + + val profile = PlayerProfile(data) + loaded[uuid] = profile + return profile + } + + fun saveAll(async: Boolean) { + val saver = { + for ((uuid, profile) in loaded) { + for ((key, type) in keys) { + when (type) { + Type.INT -> handler.write(uuid, key, profile.read(key, 0)) + Type.DOUBLE -> handler.write(uuid, key, profile.read(key, 0.0)) + Type.STRING -> handler.write(uuid, key, profile.read(key, "Unknown Value")) + Type.BOOLEAN -> handler.write(uuid, key, profile.read(key, false)) + } + } + } + + handler.save() + } + + if (async) { + EcoEnchantsPlugin.getInstance().scheduler.runAsync(saver) + } else { + saver.invoke() + } + } + + @JvmStatic + val OfflinePlayer.profile: PlayerProfile + get() { + return load(this.uniqueId) + } + + init { + keys["descriptions"] = Type.BOOLEAN + } + } + + private enum class Type { + STRING, + DOUBLE, + BOOLEAN, + INT + } +} \ No newline at end of file diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/YamlDataHandler.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/YamlDataHandler.kt new file mode 100644 index 00000000..ccde1a13 --- /dev/null +++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/ecoenchants/data/storage/YamlDataHandler.kt @@ -0,0 +1,32 @@ +package com.willfp.ecoenchants.data.storage + +import com.willfp.eco.core.config.yaml.YamlBaseConfig +import com.willfp.ecoenchants.EcoEnchantsPlugin +import java.util.* + +@Suppress("UNCHECKED_CAST") +class YamlDataHandler( + plugin: EcoEnchantsPlugin +) : DataHandler { + private val dataYml = DataYml(plugin) + + override fun save() { + dataYml.save() + } + + override fun <T> write(uuid: UUID, key: String, value: T) { + dataYml.set("player.$uuid.$key", value) + } + + override fun <T> read(uuid: UUID, key: String): T? { + return dataYml.get("player.$uuid.$key") as T? + } + + class DataYml( + plugin: EcoEnchantsPlugin + ) : YamlBaseConfig( + "data", + false, + plugin + ) +} \ No newline at end of file diff --git a/eco-core/core-plugin/src/main/resources/config.yml b/eco-core/core-plugin/src/main/resources/config.yml index 66e29fcc..4fd8d60b 100644 --- a/eco-core/core-plugin/src/main/resources/config.yml +++ b/eco-core/core-plugin/src/main/resources/config.yml @@ -3,6 +3,18 @@ # by Auxilor # +mysql: + enabled: false # Set to false, data.yml will be used instead. + host: localhost + port: 3306 + database: database + user: username + password: passy + +autosave: + log: true # If auto-save messages should be sent to console + async: false # If saves should be performed asynchronously. May cause bugs without MySQL + allow-on-npc: false # If enchantments should activate against NPCs. commands: diff --git a/eco-core/core-plugin/src/main/resources/data.yml b/eco-core/core-plugin/src/main/resources/data.yml new file mode 100644 index 00000000..305b7f45 --- /dev/null +++ b/eco-core/core-plugin/src/main/resources/data.yml @@ -0,0 +1 @@ +# Do not modify this file, it is purely for internal storage. \ No newline at end of file diff --git a/eco-core/core-plugin/src/main/resources/plugin.yml b/eco-core/core-plugin/src/main/resources/plugin.yml index fae8dec4..123619df 100644 --- a/eco-core/core-plugin/src/main/resources/plugin.yml +++ b/eco-core/core-plugin/src/main/resources/plugin.yml @@ -22,6 +22,12 @@ softdepend: - Essentials - PlaceholderAPI - mcMMO +libraries: + - 'org.jetbrains.kotlin:kotlin-stdlib:1.5.21' + - 'org.jetbrains.exposed:exposed-core:0.34.1' + - 'org.jetbrains.exposed:exposed-dao:0.34.1' + - 'org.jetbrains.exposed:exposed-jdbc:0.34.1' + - 'mysql:mysql-connector-java:5.1.48' commands: enchantinfo: