diff --git a/README.md b/README.md index f7fa84e..73c975f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # Simply-minecraft-db +## Features + +- [x] Configurable sharing options +- [x] Store config to database +- [x] Sync settings across multiple servers via a database diff --git a/build.gradle.kts b/build.gradle.kts index 3a7027c..6291811 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,18 +14,21 @@ plugins { repositories { mavenCentral() maven("https://repo.papermc.io/repository/maven-public/") + maven("https://repo.codemc.io/repository/maven-public/") } dependencies { - compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT") + compileOnly("io.papermc.paper:paper-api:1.21.3-R0.1-SNAPSHOT") implementation("net.kyori:adventure-api:4.17.0") implementation("net.kyori:adventure-text-minimessage:4.17.0") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1") + // implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1") implementation("org.postgresql:postgresql:42.7.1") implementation("org.jetbrains.exposed:exposed-core:$exposedVersion") implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1") implementation("org.jetbrains.exposed:exposed-kotlin-datetime:$exposedVersion") + compileOnly("de.tr7zw:item-nbt-api-plugin:2.14.0") + implementation("com.michael-bull.kotlin-result:kotlin-result:2.0.0") } tasks { shadowJar { @@ -39,11 +42,10 @@ bukkit { name = "Simply-Minecraft-DB" description = "It provides a simple way to manage player data through a database." version = getVersion().toString() - apiVersion = "1.21.1" + apiVersion = "1.21.3" authors = - listOf( - "Hare-K02", - ) + listOf("Hare-K02") + depend = listOf("NBTAPI") permissions { register("simplydb.*") { children = listOf("simplydb.command", "simplydb.admin") diff --git a/src/main/kotlin/net/hareworks/kommandlib/main.kt b/src/main/kotlin/net/hareworks/kommandlib/main.kt index 12fbd98..66b124a 100644 --- a/src/main/kotlin/net/hareworks/kommandlib/main.kt +++ b/src/main/kotlin/net/hareworks/kommandlib/main.kt @@ -1,5 +1,6 @@ package net.hareworks.kommandlib +import com.github.michaelbull.result.* import kotlin.collections.listOf import org.bukkit.Bukkit import org.bukkit.command.CommandMap @@ -7,47 +8,52 @@ import org.bukkit.command.CommandSender import org.bukkit.command.PluginCommand import org.bukkit.command.TabCompleter import org.bukkit.plugin.java.JavaPlugin +import net.hareworks.simplymcdb.App -class KommandLib(plugin: JavaPlugin, vararg routes: Pair) { - val routes = routes.toMap() +class KommandLib(plugin: JavaPlugin, vararg routes: Argument) { + val entries = routes.toList() init { val f = Bukkit.getServer().javaClass.getDeclaredField("commandMap") f.isAccessible = true val commandMap = f.get(Bukkit.getServer()) as CommandMap - for ((name, _) in routes) { + for (route in routes) { commandMap.register( - plugin.getName(), - (PluginCommand::class - .java - .declaredConstructors - .first() - .apply { isAccessible = true } - .newInstance(name, plugin) as - PluginCommand) - .apply { - this.name = name - this.setExecutor { sender, _, alias, args -> - val route = getLastRoute(arrayOf(alias, *args)) - if (route.size == args.size + 1) route.last().onCommand(sender, args) - true - } - this.tabCompleter = TabCompleter { sender, _, alias, args -> - val route = getLastRoute(arrayOf(alias, *args)) - if (route.size == args.size) route.last().getCompletList(sender, args) - else listOf() - } - } + plugin.getName(), + (PluginCommand::class + .java + .declaredConstructors + .first() + .apply { isAccessible = true } + .newInstance(route.name, plugin) as + PluginCommand) + .apply { + this.name = name + this.setExecutor { sender, _, alias, args -> + val routeargs = routeTreeSearch(arrayOf(alias, *args)) + if (routeargs.size == args.size + 1) + routeargs.last().onCommand(sender, args) + true + } + this.tabCompleter = TabCompleter { sender, _, alias, args -> + val routeargs = routeTreeSearch(arrayOf(alias, *args)) + if (routeargs.size == args.size) + routeargs.last().getCompletList(sender, args) + else listOf() + } + } ) } } - fun getLastRoute(args: Array): List { - val list = mutableListOf(routes[args[0]] ?: throw Exception("Invalid command")) + fun routeTreeSearch(args: Array): List { + val list = + mutableListOf( + entries.find { it.name == args[0] } ?: throw Exception("Invalid command") + ) var i = 1 while (i + 1 <= args.size) { if (list.last().routes.isEmpty()) break - val route = - list.last().routes.values.sortedBy { it.priority }.find { it.typeCheck(args[i]) } ?: break + val route = list.last().routes.sortedBy { it.priority }.find { it.name == args[i] } ?: break list.add(route) i += route.unit } @@ -58,127 +64,82 @@ class KommandLib(plugin: JavaPlugin, vararg routes: Pair) { val f = Bukkit.getServer().javaClass.getDeclaredField("commandMap") f.isAccessible = true val commandMap = f.get(Bukkit.getServer()) as CommandMap - for ((name, _) in routes) { - commandMap.getCommand(name)?.unregister(commandMap) + for (route in entries) { + commandMap.getCommand(route.name)?.unregister(commandMap) } } } abstract class Argument( - vararg routes: Pair, - val execute: (CommandSender, Array) -> Unit + val argname: String, + vararg routes: Argument, + val execute: (CommandSender, Array) -> Unit ) { - val routes = routes.toMap() - var name: String = "" + val name = argname + var routes = routes.toList() var permission: String = "" set(value) { field = value if (value.isEmpty()) return - for ((_, route) in routes) { + for (route in routes) { route.permission = value + "." + route.name } - println("permission setted to $value") } - var condition: (CommandSender) -> Boolean = { true } + var condition: (CommandSender) -> Boolean = { true } - init { - for ((name, route) in routes) { - route.name = name - } + public fun addArgs(vararg routes: Argument): Argument { + this.routes += routes + return this } abstract var priority: Int open var unit: Int = 1 - abstract fun onCommand(sender: CommandSender, args: Array) - abstract fun typeCheck(arg: String): Boolean - abstract fun toValue(args: Array): Any + open fun onCommand(sender: CommandSender, args: Array) { + execute(sender, args) + } abstract fun suggest(sender: CommandSender, args: Array): List fun getCompletList(sender: CommandSender, args: Array): List { return routes - .values - .filter { sender.hasPermission(it.permission) && it.condition(sender) } - .map { it.suggest(sender, args) } - .flatten() + .filter { sender.hasPermission(it.permission) && it.condition(sender) } + .map { it.suggest(sender, args) } + .flatten() } } -class Route(vararg routes: Pair, execute: (CommandSender, Array) -> Unit) : - Argument(*routes, execute = execute) { +class Route( + name: String, + vararg routes: Argument, + execute: (CommandSender, Array) -> Unit +) : Argument(name, *routes, execute = execute) { override var priority: Int = 2 - override fun onCommand(sender: CommandSender, args: Array) { - execute(sender, args.map { it }.toTypedArray()) - } - override fun typeCheck(arg: String): Boolean { - return this.name == arg - } - override fun toValue(args: Array): Any { - return args.joinToString(" ") - } override fun suggest(sender: CommandSender, args: Array): List { - return if (sender.hasPermission(this.permission) && this.condition(sender) && this.name.startsWith(args.last())) - listOf(this.name) + return if (sender.hasPermission(this.permission) && + this.condition(sender) && + this.name.startsWith(args.last()) + ) + listOf(this.name) else listOf() } } -class Text(vararg routes: Pair, execute: (CommandSender, Array) -> Unit) : - Argument(*routes, execute = execute) { +class Text(name: String, vararg routes: Argument, execute: (CommandSender, Array) -> Unit) : + Argument(name, *routes, execute = execute) { override var priority: Int = 0 - override fun typeCheck(arg: String): Boolean { - return true - } - override fun toValue(args: Array): Any { - return args.joinToString(" ") - } - override fun onCommand(sender: CommandSender, args: Array) { - execute(sender, args.map { it }.toTypedArray()) - } override fun suggest(sender: CommandSender, args: Array): List { return listOf(args.last()) } } -class Integer(vararg routes: Pair, execute: (CommandSender, Array) -> Unit) : - Argument(*routes, execute = execute) { +class Integer( + name: String, + vararg routes: Argument, + execute: (CommandSender, Array) -> Unit +) : Argument(name, *routes, execute = execute) { override var priority: Int = 1 - override fun typeCheck(arg: String): Boolean { - return arg.toIntOrNull() != null - } - - override fun onCommand(sender: CommandSender, args: Array) { - execute(sender, args.map { it.toInt() }.toTypedArray()) - } - - override fun toValue(args: Array): Any { - return args[0].toInt() - } - - override fun suggest(sender: CommandSender, args: Array): List { - return listOf() - } -} - -class Position( - vararg routes: Pair, - execute: (CommandSender, Array) -> Unit -) : Argument(*routes, execute = execute) { - override var priority: Int = 3 - override var unit: Int = 3 - override fun typeCheck(arg: String): Boolean { - return true - } - - override fun onCommand(sender: CommandSender, args: Array) { - execute(sender, args.map { it }.toTypedArray()) - } - - override fun toValue(args: Array): Any { - return Triple(args[0].toDouble(), args[1].toDouble(), args[2].toDouble()) - } override fun suggest(sender: CommandSender, args: Array): List { return listOf() diff --git a/src/main/kotlin/net/hareworks/simplymcdb/App.kt b/src/main/kotlin/net/hareworks/simplymcdb/App.kt index 7809345..fc79b4a 100644 --- a/src/main/kotlin/net/hareworks/simplymcdb/App.kt +++ b/src/main/kotlin/net/hareworks/simplymcdb/App.kt @@ -1,11 +1,11 @@ package net.hareworks.simplymcdb -import net.hareworks.simplymcdb.Config -import net.hareworks.simplymcdb.database.Database -import net.hareworks.simplymcdb.command.smcdb import net.hareworks.kommandlib.KommandLib +import net.hareworks.simplymcdb.command.smcdb +import net.hareworks.simplymcdb.database.Database import org.bukkit.plugin.java.JavaPlugin + enum class State { ACTIVE, DISCONNECTED, @@ -24,12 +24,15 @@ public class App : JavaPlugin() { } lateinit var command: KommandLib private set + override fun onEnable() { instance = this - logger.info("simplymcdb enabled.") - Config.init() - command = KommandLib(this, "smcdb" to smcdb) + command = KommandLib(this, smcdb) + + server.pluginManager.registerEvents(EventListener, this) + + if (Config.check()) enable() } override fun onDisable() { enabled = State.DISABLED diff --git a/src/main/kotlin/net/hareworks/simplymcdb/Command.kt b/src/main/kotlin/net/hareworks/simplymcdb/Command.kt index 0cae784..7e94cb8 100644 --- a/src/main/kotlin/net/hareworks/simplymcdb/Command.kt +++ b/src/main/kotlin/net/hareworks/simplymcdb/Command.kt @@ -1,119 +1,202 @@ package net.hareworks.simplymcdb.command +import com.github.michaelbull.result.* import net.hareworks.kommandlib.* -import net.hareworks.simplymcdb.App -import net.hareworks.simplymcdb.State -import net.hareworks.simplymcdb.config.reload as reloadConfig +import net.hareworks.simplymcdb.* import net.hareworks.simplymcdb.database.Database +import net.kyori.adventure.audience.Audience import net.kyori.adventure.text.minimessage.MiniMessage +import org.bukkit.entity.Player -public val smcdb: Route = - Route( - "config" to - Route( - "reload" to - Route { sender, _ -> - sender.sendMessage("reloading config...") - reloadConfig() - sender.sendMessage("reloaded.") - } - .apply { - permission = - "simplydb.command.config.reload" - }, - "fetch" to - Route { sender, _ -> - sender.sendMessage("fetching config...") - } - .apply { - permission = - "simplydb.command.config.fetch" - }, - "upload" to - Route { sender, _ -> - sender.sendMessage("uploading config...") - } - .apply { - permission = - "simplydb.command.config.upload" - }, - "help" to - Route { sender, _ -> - var help = - MiniMessage.miniMessage() - .deserialize( - "simplymcdb config helpreload: reload the config from config.ymlfetch: fetch the config from the databaseupload: upload the current config to the database" - ) - sender.sendMessage(help) - } - .apply { - permission = - "simplydb.command.config" - }, - "on" to - Route { sender, _ -> - if (App.instance.enabled == State.ACTIVE - ) { - sender.sendMessage( - "simplymcdb is already enabled." - ) - return@Route - } - App.instance.enable() - sender.sendMessage("simplymcdb enabled.") - } - .apply { - permission = "simplydb.command.config.on" - }, - "off" to - Route { sender, _ -> - if (App.instance.enabled != State.ACTIVE - ) { - sender.sendMessage( - "simplymcdb is already disabled." - ) - return@Route - } - App.instance.disable() - sender.sendMessage("simplymcdb disabled.") - } - .apply { - permission = - "simplydb.command.config.off" - }, - ) { sender, _ -> - (sender as ).performCommand("smcdb config help") - } - .apply { permission = "simplydb.command.config" }, - "help" to Route { sender, _ -> - var help = - MiniMessage.miniMessage() - .deserialize( - "simplymcdb helpconfig: configre the pluginactivate: when the plugin is disabled, activate itdeactivate: when the plugin is enabled, deactivate it" - ) - sender.sendMessage(help) - } - .apply { permission = "simplydb.command" }, - "activate" to Route { sender, _ -> - if (App.instance.enabled == State.ACTIVE - ) { - sender.sendMessage( - "simplymcdb is already enabled." - ) - return@Route - } - App.instance.enable() - sender.sendMessage("simplymcdb enabled.") - } - .apply { permission = "simplydb.command.activate" }, - "initialize" to Route { sender, _ -> - sender.sendMessage("database initializing...") - Database.initialize() - } - .apply { permission = "simplydb.command.setup" }, - "confirm" to Route { sender, _ -> - - } - .apply { permission = "simplydb.command.confirm" }, - ) { sender, _ -> sender.sendMessage("simplymcdb command") } - .apply { name = "simplymcdb" } +public fun Audience.sendMM(message: String) { + this.sendMessage(MiniMessage.miniMessage().deserialize(message)) +} + +val command_buffer = mutableMapOf() +public val smcdb = + Route("smcdb") { sender, _ -> sender.sendMessage("simptlymcdb command") } + .addArgs( + Route("config") { sender, _ -> + (sender as Player).performCommand("smcdb config help") + } + .addArgs( + Route("reload") { sender, _ -> + sender.sendMessage("reloading config...") + Config.reload() + sender.sendMessage("reloaded.") + }, + Route("fetch") { sender, _ -> + sender.sendMessage("fetching config...") + }, + Route("upload") { sender, _ -> + sender.sendMessage("uploading config...") + }, + Route("help") { sender, _ -> + var help = + MiniMessage.miniMessage() + .deserialize( + "simplymcdb config helpreload: reload the config from config.ymlfetch: fetch the config from the databaseupload: upload the current config to the database" + ) + sender.sendMessage(help) + } + ), + Route("help") { sender, _ -> + sender.sendMM( + "simplymcdb helpconfig: configre the pluginactivate: when the plugin is disabled, activate itdeactivate: when the plugin is enabled, deactivate it" + ) + }, + Route("activate") { sender, _ -> + if (App.instance.enabled == State.ACTIVE) { + sender.sendMessage("simplymcdb is already enabled.") + return@Route + } + App.instance.enable() + sender.sendMessage("simplymcdb enabled.") + }, + Route("deactivate") { sender, _ -> + if (App.instance.enabled == State.DISABLED) { + sender.sendMessage("simplymcdb is already disabled.") + return@Route + } + App.instance.disable() + sender.sendMessage("simplymcdb disabled.") + }, + Route("database") { _, _ -> } + .addArgs( + Route("init") { sender, _ -> + Database.initialize() + sender.sendMessage("database initialized.") + }, + Route("reset") { sender, _ -> + Database.reset() + sender.sendMessage("database reset.") + }, + ), + Route("check") { sender, _ -> + sender.sendMM( + "${when (App.instance.enabled) { + State.ACTIVE -> "●" + State.DISCONNECTED -> "■" + State.DISABLED -> "○" + }} simply-minecraft-database" + ) + sender.sendMM( + "status: ${when (App.instance.enabled) { + State.ACTIVE -> "active" + State.DISCONNECTED -> "disconnected" + State.DISABLED -> "disabled"}}" + ) + sender.sendMM( + "- database test: ${if (Database.ping()) "success" else "failed"}" + ) + sender.sendMM( + "- config test: ${if (Config.config.getBoolean("enabled")) "enabled" else "disabled"}" + ) + }, + Route("register") { sender, _ -> + if (sender !is Player) { + sender.sendMM("This command is only available for players.") + return@Route + } else + when (App.instance.enabled) { + State.DISABLED -> + sender.sendMM( + "[SMCDB] simplymcdb is disabled.
Run /smcdb check to check the status." + ) + State.DISCONNECTED -> + sender.sendMM( + "[SMCDB] simplymcdb is enabled but disconnected.
Run /smcdb check to check the status." + ) + else -> { + if (!isRegistered(sender.uniqueId)) { + sender.sendMM( + "[SMCDB] The inventory of the other servers will be overwritten." + + "Are you sure you want to register?" + + "/smcdb confirm to confirm." + ) + } else { + sender.sendMM("[SMCDB] You are already registered.") + } + } + } + }, + Route("confirm") { sender, _ -> + if (sender !is Player) return@Route + when (command_buffer[sender.uniqueId]) { + "register" -> { + if (App.instance.enabled == State.ACTIVE) { + register(sender) + sender.sendMM("[SMCDB] Successfully registered.") + } else { + sender.sendMM("[SMCDB] simplymcdb is disabled.") + } + } + else -> { + sender.sendMM("[SMCDB] Invalid command.") + } + } + command_buffer.remove(sender.uniqueId) + } + // Route("update") { sender, _ -> + // if (sender !is Player) { + // sender.sendMM("This command is only available for players.") + // return@Route + // } else + // when (App.instance.enabled) { + // State.DISABLED -> + // sender.sendMM( + // "[SMCDB] simplymcdb is disabled.
Run + // /smcdb check to check the status." + // ) + // State.DISCONNECTED -> + // sender.sendMM( + // "[SMCDB] simplymcdb is enabled but + // disconnected.
Run /smcdb check to check the status." + // ) + // else -> { + // if (isRegistered(sender.uniqueId)) { + // update(sender) + // sender.sendMM("[SMCDB] Successfully updated.") + // } else { + // sender.sendMM("[SMCDB] You are not registered.") + // } + // } + // } + // }, + // Route("fetch") { sender, _ -> + // if (sender !is Player) { + // sender.sendMM("This command is only available for players.") + // return@Route + // } else + // when (App.instance.enabled) { + // State.DISABLED -> + // sender.sendMM( + // "[SMCDB] simplymcdb is disabled.
Run + // /smcdb check to check the status." + // ) + // State.DISCONNECTED -> + // sender.sendMM( + // "[SMCDB] simplymcdb is enabled but + // disconnected.
Run /smcdb check to check the status." + // ) + // else -> { + // if (isRegistered(sender.uniqueId)) { + // sender.sendMM( + // "[SMCDB] Welcome back, + // ${sender.name}.Fetching your data..." + // ) + // fetch(sender) + // } else { + // sender.sendMM( + // "[SMCDB] Welcome, ${sender.name}." + // + + // "SMCDB is active but you have already + // played before." + + // "Run /smcdb register to + // register yourself." + // ) + // } + // } + // } + // }, + ) diff --git a/src/main/kotlin/net/hareworks/simplymcdb/Config.kt b/src/main/kotlin/net/hareworks/simplymcdb/Config.kt index bf6794b..24a1f1d 100644 --- a/src/main/kotlin/net/hareworks/simplymcdb/Config.kt +++ b/src/main/kotlin/net/hareworks/simplymcdb/Config.kt @@ -3,7 +3,6 @@ package net.hareworks.simplymcdb import org.bukkit.configuration.file.YamlConfiguration import org.jetbrains.exposed.sql.Table - object ConfigTable : Table() { val uuid = varchar("uuid", 36) val name = varchar("name", 16) @@ -34,4 +33,8 @@ public object Config { config.save(App.instance.dataFolder.resolve("config.yml")) App.instance.logger.info("config saved.") } + + public fun check(): Boolean { + return config.getBoolean("enabled") + } } diff --git a/src/main/kotlin/net/hareworks/simplymcdb/Event.kt b/src/main/kotlin/net/hareworks/simplymcdb/Event.kt index 5b99687..b479701 100644 --- a/src/main/kotlin/net/hareworks/simplymcdb/Event.kt +++ b/src/main/kotlin/net/hareworks/simplymcdb/Event.kt @@ -1,14 +1,11 @@ -package net.hareworks.simplymcdb.event +package net.hareworks.simplymcdb -import net.hareworks.simplymcdb.App -import net.hareworks.simplymcdb.State -import net.hareworks.simplymcdb.database.Database import net.kyori.adventure.text.minimessage.MiniMessage import org.bukkit.event.EventHandler import org.bukkit.event.Listener import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerQuitEvent import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.transactions.transaction public object EventListener : Listener { @EventHandler @@ -16,27 +13,50 @@ public object EventListener : Listener { if (event.player.hasPermission("simplymcdb.admin")) { when (App.instance.enabled) { State.DISABLED -> { - val mm = MiniMessage.miniMessage().deserialize("simplymcdb is disabled.") + val mm = + MiniMessage.miniMessage() + .deserialize( + "[SMCDB:admin] simplymcdb is disabled.
Run /smcdb check to check the status." + ) event.player.sendMessage(mm) } State.DISCONNECTED -> { - val mm = MiniMessage.miniMessage().deserialize("simplymcdb is enabled but disconnected.
Check the database connection and try /smcdb on.") + val mm = + MiniMessage.miniMessage() + .deserialize( + "[SMCDB:admin] simplymcdb is enabled but disconnected.
Run /smcdb check to check the status." + ) event.player.sendMessage(mm) } else -> { - val mm = MiniMessage.miniMessage().deserialize("simplymcdb is enabled.") + val mm = + MiniMessage.miniMessage() + .deserialize("[SMCDB:admin] simplymcdb is enabled.") event.player.sendMessage(mm) } } } - if (App.instance.enabled == State.ACTIVE) { - val player = event.player - // if player has already registered in the database, load the data - Database.instance?.let { - transaction(it) { - - } - } + + if (App.instance.enabled !== State.ACTIVE) return + if (!event.player.hasPlayedBefore()) return + + if (isRegistered(event.player.uniqueId)) { + fetch(event.player) + } else { + event.player.sendMessage( + MiniMessage.miniMessage() + .deserialize( + "[SMCDB] Welcome, ${event.player.name}." + + "SMCDB is active but you have already played before." + + "Run /smcdb register to register yourself." + ) + ) } } -} + + @EventHandler + fun onQuit(event: PlayerQuitEvent) { + if (App.instance.enabled !== State.ACTIVE) return + if (isRegistered(event.player.uniqueId)) update(event.player) + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/hareworks/simplymcdb/PlayerData.kt b/src/main/kotlin/net/hareworks/simplymcdb/PlayerData.kt deleted file mode 100644 index 367ec8b..0000000 --- a/src/main/kotlin/net/hareworks/simplymcdb/PlayerData.kt +++ /dev/null @@ -1,49 +0,0 @@ -package net.hareworks.simplymcdb.playerdata - -import net.hareworks.simplymcdb.App -import net.hareworks.simplymcdb.database.Database -import org.bukkit.entity.Player as BukkitPlayer -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.transactions.transaction - -public object Player : Table() { - val uuid = varchar("uuid", 36) - val name = varchar("name", 16) - val lastLogin = long("last_login") - val lastLogout = long("last_logout") - val playTime = long("play_time") - val firstLogin = long("first_login") - val lastIp = varchar("last_ip", 15) - - val data = text("data").nullable() - - override val primaryKey = PrimaryKey(uuid) -} - -public fun register(player: BukkitPlayer) { - App.instance.logger.info("Registering player data for ${player.name}") - Database.instance?.let { - transaction { - Player.insert { - it[uuid] = player.uniqueId.toString() - it[name] = player.name - it[lastLogin] = System.currentTimeMillis() - it[firstLogin] = System.currentTimeMillis() - it[lastIp] = player.address?.address?.hostAddress ?: "unknown" - } - } - } -} - -public fun push(player: BukkitPlayer) { - App.instance.logger.info("Creating player data for ${player.name}") - Database.instance?.let { - transaction { - Player.update({ Player.uuid eq player.uniqueId.toString() }) { - it[lastLogin] = System.currentTimeMillis() - it[lastIp] = player.address?.address?.hostAddress ?: "unknown" - - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/net/hareworks/simplymcdb/PlayerSerializer.kt b/src/main/kotlin/net/hareworks/simplymcdb/PlayerSerializer.kt index 09543ed..23c3ae8 100644 --- a/src/main/kotlin/net/hareworks/simplymcdb/PlayerSerializer.kt +++ b/src/main/kotlin/net/hareworks/simplymcdb/PlayerSerializer.kt @@ -1,31 +1,68 @@ package net.hareworks.simplymcdb +import de.tr7zw.nbtapi.NBT +import java.util.function.Function import org.bukkit.entity.Player as BukkitPlayer -import org.bukkit.inventory.ItemStack -class PlayerInventory( - val contents: Array, - val armor: Array, +data class PlayerData( + val health: Float, + val hunger: Int, + val exp: Float, + val effects: String, + val inv: String, + val enderchest: String ) object PlayerSerializer { fun serialize(player: BukkitPlayer): String { - val inv = player.inventory.storageContents - val armor = player.inventory.armorContents - val enderChest = player.enderChest + val result = + NBT.get( + player, + Function { nbt -> + var output = NBT.createNBTObject() - return "" // base64 encoded string + output.setFloat("Health", player.health.toFloat()) + output.setInteger("foodLevel", player.foodLevel) + output.setFloat("XpP", player.exp) + output.setInteger("SelectedItemSlot", player.inventory.heldItemSlot) + var active_effects = output.getCompoundList("active_effects") + nbt.getCompoundList("active_effects").forEach { + active_effects.addCompound(it) + } + var inventory = output.getCompoundList("Inventory") + nbt.getCompoundList("Inventory").forEach { inventory.addCompound(it) } + var enderchest = output.getCompoundList("EnderItems") + nbt.getCompoundList("EnderItems").forEach { enderchest.addCompound(it) } + + output.toString() + } + ) + return result } - fun deserialize(data: String): Map { - return mapOf() + fun deserialize(player: BukkitPlayer, data: String) { + NBT.modify( + player, + Function { nbt -> + val input = NBT.parseNBT(data) + + App.instance.logger.info("Deserializing player data: $data") + App.instance.logger.info("deserialized: ${input.toString()}") + + nbt.setFloat("Health", input.getFloat("Health")) + nbt.setInteger("foodLevel", input.getInteger("foodLevel")) + nbt.setFloat("XpP", input.getFloat("XpP")) + nbt.setInteger("SelectedItemSlot", input.getInteger("SelectedItemSlot")) + val active_effects = nbt.getCompoundList("active_effects") + active_effects.clear() + input.getCompoundList("active_effects").forEach { active_effects.addCompound(it) } + val inventory = nbt.getCompoundList("Inventory") + inventory.clear() + input.getCompoundList("Inventory").forEach { inventory.addCompound(it) } + val enderchest = nbt.getCompoundList("EnderItems") + enderchest.clear() + input.getCompoundList("EnderItems").forEach { enderchest.addCompound(it) } + } + ) } } - -fun ItemStacksToBase64(item: Array): String { - return "" -} - -fun fromBase64(data: String): ByteArray { - return byteArrayOf() -} \ No newline at end of file diff --git a/src/main/kotlin/net/hareworks/simplymcdb/Players.kt b/src/main/kotlin/net/hareworks/simplymcdb/Players.kt new file mode 100644 index 0000000..3dbb01b --- /dev/null +++ b/src/main/kotlin/net/hareworks/simplymcdb/Players.kt @@ -0,0 +1,70 @@ +package net.hareworks.simplymcdb + +import java.util.UUID +import net.hareworks.simplymcdb.database.Database +import org.bukkit.entity.Player as BukkitPlayer +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.transaction + +public object Players : Table() { + val id = integer("id").autoIncrement() + + val uuid = varchar("uuid", 36) + val name = varchar("name", 16) + val firstLogin = long("first_login") + val playTime = long("play_time").default(0) + val lastOnline = long("last_online").default(0) + val lastIp = varchar("last_ip", 15) + + val data = text("data").default("") + + override val primaryKey = PrimaryKey(uuid) +} + +public fun isRegistered(player: UUID): Boolean { + return transaction(Database.instance) { + val data = + Players.select(Players.uuid).where { Players.uuid eq player.toString() }.map { + it[Players.uuid] + } + data.isNotEmpty() + } +} + +public fun register(player: BukkitPlayer) { + transaction(Database.instance) { + Players.insert { + it[uuid] = player.uniqueId.toString() + it[name] = player.name + it[firstLogin] = System.currentTimeMillis() + it[lastOnline] = System.currentTimeMillis() + it[lastIp] = player.address?.address?.hostAddress ?: "unknown" + } + } +} + +public fun update(player: BukkitPlayer) { + val dat = PlayerSerializer.serialize(player) + transaction(Database.instance) { + Players.update({ Players.uuid eq player.uniqueId.toString() }) { + it[lastOnline] = System.currentTimeMillis() + it[lastIp] = player.address?.address?.hostAddress ?: "unknown" + + // player.sendMessage(dat) + it[data] = dat + } + } +} + +public fun fetch(player: BukkitPlayer) { + val dat = + transaction(Database.instance) { + Players.select(Players.uuid, Players.data) + .where { Players.uuid eq player.uniqueId.toString() } + .withDistinct() + .map { it[Players.data] } + .first() + } + // player.sendMessage(dat) + PlayerSerializer.deserialize(player, dat) +} diff --git a/src/main/kotlin/net/hareworks/simplymcdb/database/Database.kt b/src/main/kotlin/net/hareworks/simplymcdb/database/Database.kt index aa3acfc..4c154b9 100644 --- a/src/main/kotlin/net/hareworks/simplymcdb/database/Database.kt +++ b/src/main/kotlin/net/hareworks/simplymcdb/database/Database.kt @@ -1,8 +1,8 @@ package net.hareworks.simplymcdb.database import net.hareworks.simplymcdb.App -import net.hareworks.simplymcdb.config.config -import net.hareworks.simplymcdb.playerdata.Player +import net.hareworks.simplymcdb.Config +import net.hareworks.simplymcdb.Players import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.transactions.transaction @@ -10,7 +10,9 @@ import org.jetbrains.exposed.sql.transactions.transaction public object Database { public var instance: Database? = null private set + public fun connect() { + var config = Config.config val type = config.getString("database.type") val host = config.getString("database.host") val port = config.getInt("database.port") @@ -49,11 +51,6 @@ public object Database { } if (instance == null) return App.instance.logger.info("Database connected: $host:$port/$database") - if (!ping()) { - App.instance.logger.warning("Database ping failed") - } else { - App.instance.logger.info("Database ping successful") - } } public fun disconnect() { instance?.let { @@ -66,20 +63,23 @@ public object Database { } public fun ping(): Boolean { + val flag = (instance == null) + if (flag) connect() return try { - transaction { - addLogger(StdOutSqlLogger) - exec("SELECT 1") - } + transaction(instance) { exec("SELECT 1") } true } catch (e: Exception) { false + } finally { + if (flag) disconnect() } } public fun initialize() { - transaction { - SchemaUtils.create(Player) - } + transaction(instance) { SchemaUtils.create(Players) } + } + + public fun reset() { + transaction(instance) { SchemaUtils.drop(Players) } } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ca15183..64af33a 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,18 +1,24 @@ -enable: false database: - type: postgresql + type: postgresql # Supported: mysql, postgresql host: localhost port: 5432 + # You must have created a database and user. database: smcdb user: smcdb - password: SaN1m_wk2eh9 -playerdata: + password: password + +playerdata: # README for more details. health: true hunger: true experience: true - status: true + effects: true inventory: true enderchest: true # achievements: true # recipebook: true - gamemode: true \ No newline at end of file + gamemode: true + + +# Do not change this value manually, +# If the plugin settings are OK, enable it with the command. +enabled: false \ No newline at end of file