mirror of
https://github.com/GayPizzaSpecifications/foundation.git
synced 2025-08-03 21:41:32 +00:00
Basic Persistent Store using Xodus
This commit is contained in:
@ -85,6 +85,10 @@ subprojects {
|
|||||||
implementation("com.charleskorn.kaml:kaml:0.38.0")
|
implementation("com.charleskorn.kaml:kaml:0.38.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1")
|
||||||
|
|
||||||
|
// Persistence
|
||||||
|
implementation("org.jetbrains.xodus:xodus-openAPI:1.3.232")
|
||||||
|
implementation("org.jetbrains.xodus:xodus-entity-store:1.3.232")
|
||||||
|
|
||||||
// Paper API
|
// Paper API
|
||||||
compileOnly("io.papermc.paper:paper-api:1.18.1-R0.1-SNAPSHOT")
|
compileOnly("io.papermc.paper:paper-api:1.18.1-R0.1-SNAPSHOT")
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
package cloud.kubelet.foundation.core
|
package cloud.kubelet.foundation.core
|
||||||
|
|
||||||
import cloud.kubelet.foundation.core.command.BackupCommand
|
import cloud.kubelet.foundation.core.command.*
|
||||||
import cloud.kubelet.foundation.core.command.GamemodeCommand
|
import cloud.kubelet.foundation.core.persist.PersistentStore
|
||||||
import cloud.kubelet.foundation.core.command.LeaderboardCommand
|
import cloud.kubelet.foundation.core.persist.setAllProperties
|
||||||
import cloud.kubelet.foundation.core.command.UpdateCommand
|
import io.papermc.paper.event.player.AsyncChatEvent
|
||||||
import net.kyori.adventure.text.Component
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.TextComponent
|
||||||
import org.bukkit.GameMode
|
import org.bukkit.GameMode
|
||||||
import org.bukkit.command.CommandExecutor
|
import org.bukkit.command.CommandExecutor
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
import org.bukkit.event.Listener
|
import org.bukkit.event.Listener
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
class FoundationCorePlugin : JavaPlugin(), Listener {
|
class FoundationCorePlugin : JavaPlugin(), Listener {
|
||||||
|
internal val persistentStores = ConcurrentHashMap<String, PersistentStore>()
|
||||||
private lateinit var _pluginDataPath: Path
|
private lateinit var _pluginDataPath: Path
|
||||||
|
|
||||||
var pluginDataPath: Path
|
var pluginDataPath: Path
|
||||||
@ -29,6 +34,13 @@ class FoundationCorePlugin : JavaPlugin(), Listener {
|
|||||||
_pluginDataPath = value
|
_pluginDataPath = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a persistent store by name. Make sure the name is path-safe, descriptive and consistent across server runs.
|
||||||
|
*/
|
||||||
|
fun getPersistentStore(name: String) = persistentStores.getOrPut(name) { PersistentStore(this, name) }
|
||||||
|
|
||||||
|
private lateinit var chatLogStore: PersistentStore
|
||||||
|
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
pluginDataPath = dataFolder.toPath()
|
pluginDataPath = dataFolder.toPath()
|
||||||
val backupPath = pluginDataPath.resolve(BACKUPS_DIRECTORY)
|
val backupPath = pluginDataPath.resolve(BACKUPS_DIRECTORY)
|
||||||
@ -48,10 +60,12 @@ class FoundationCorePlugin : JavaPlugin(), Listener {
|
|||||||
registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE))
|
registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE))
|
||||||
registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR))
|
registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR))
|
||||||
registerCommandExecutor(listOf("leaderboard", "lb"), LeaderboardCommand())
|
registerCommandExecutor(listOf("leaderboard", "lb"), LeaderboardCommand())
|
||||||
|
registerCommandExecutor(listOf("pstorestats"), StoreStatsCommand(this))
|
||||||
|
|
||||||
val log = slF4JLogger
|
val log = slF4JLogger
|
||||||
log.info("Features:")
|
log.info("Features:")
|
||||||
Util.printFeatureStatus(log, "Backup", BACKUP_ENABLED)
|
Util.printFeatureStatus(log, "Backup", BACKUP_ENABLED)
|
||||||
|
chatLogStore = getPersistentStore("chat-logs")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerCommandExecutor(name: String, executor: CommandExecutor) {
|
private fun registerCommandExecutor(name: String, executor: CommandExecutor) {
|
||||||
@ -81,6 +95,26 @@ class FoundationCorePlugin : JavaPlugin(), Listener {
|
|||||||
server.sendMessage(component)
|
server.sendMessage(component)
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
private fun logOnChatMessage(e: AsyncChatEvent) {
|
||||||
|
val player = e.player
|
||||||
|
val message = e.message()
|
||||||
|
|
||||||
|
if (message !is TextComponent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val content = message.content()
|
||||||
|
chatLogStore.create("ChatMessageEvent") {
|
||||||
|
setAllProperties(
|
||||||
|
"timestamp" to Instant.now().toEpochMilli(),
|
||||||
|
"player.id" to player.identity().uuid().toString(),
|
||||||
|
"player.name" to player.name,
|
||||||
|
"message.content" to content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val BACKUPS_DIRECTORY = "backups"
|
private const val BACKUPS_DIRECTORY = "backups"
|
||||||
|
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package cloud.kubelet.foundation.core.command
|
||||||
|
|
||||||
|
import cloud.kubelet.foundation.core.FoundationCorePlugin
|
||||||
|
import org.bukkit.command.Command
|
||||||
|
import org.bukkit.command.CommandExecutor
|
||||||
|
import org.bukkit.command.CommandSender
|
||||||
|
|
||||||
|
class StoreStatsCommand(private val plugin: FoundationCorePlugin) : CommandExecutor {
|
||||||
|
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
||||||
|
plugin.persistentStores.forEach { (name, store) ->
|
||||||
|
store.transact { tx ->
|
||||||
|
val types = tx.entityTypes
|
||||||
|
val counts = types.associateWith { type -> tx.getAll(type).size() }.toSortedMap()
|
||||||
|
sender.sendMessage("Store $name ->", *counts.map { " ${it.key} -> ${it.value} entries" }.toTypedArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package cloud.kubelet.foundation.core.persist
|
||||||
|
|
||||||
|
import cloud.kubelet.foundation.core.FoundationCorePlugin
|
||||||
|
import jetbrains.exodus.entitystore.Entity
|
||||||
|
import jetbrains.exodus.entitystore.PersistentEntityStores
|
||||||
|
import jetbrains.exodus.entitystore.StoreTransaction
|
||||||
|
|
||||||
|
class PersistentStore(corePlugin: FoundationCorePlugin, fileStoreName: String) : AutoCloseable {
|
||||||
|
private val fileStorePath = corePlugin.pluginDataPath.resolve("persistence/${fileStoreName}")
|
||||||
|
internal val entityStore = PersistentEntityStores.newInstance(fileStorePath.toFile())
|
||||||
|
|
||||||
|
fun transact(block: (StoreTransaction) -> Unit) = entityStore.executeInTransaction(block)
|
||||||
|
|
||||||
|
fun create(entityTypeName: String, populate: Entity.() -> Unit) = transact { tx ->
|
||||||
|
val entity = tx.newEntity(entityTypeName)
|
||||||
|
populate(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> find(entityTypeName: String, propertyName: String, value: Comparable<T>) =
|
||||||
|
transact { tx -> tx.find(entityTypeName, propertyName, value) }
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
entityStore.close()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package cloud.kubelet.foundation.core.persist
|
||||||
|
|
||||||
|
import jetbrains.exodus.entitystore.Entity
|
||||||
|
|
||||||
|
fun <T : Comparable<*>> Entity.setAllProperties(vararg entries: Pair<String, T>) = entries.forEach { entry ->
|
||||||
|
setProperty(entry.first, entry.second)
|
||||||
|
}
|
@ -45,3 +45,7 @@ commands:
|
|||||||
aliases:
|
aliases:
|
||||||
- lb
|
- lb
|
||||||
permission: foundation.command.leaderboard
|
permission: foundation.command.leaderboard
|
||||||
|
pstorestats:
|
||||||
|
description: Persistent Store Stats
|
||||||
|
usage: /pstorestats
|
||||||
|
permission: foundation.command.pstorestats
|
||||||
|
Reference in New Issue
Block a user