数据库添加缓存 优化性能

This commit is contained in:
xbaimiao
2024-04-08 10:57:45 +08:00
parent 652193fcf6
commit c6de9c32c4
15 changed files with 559 additions and 72 deletions
@@ -6,9 +6,14 @@ import com.github.xbaimiao.shoppro.core.database.SQLiteDatabase
import com.github.xbaimiao.shoppro.core.item.ItemLoaderManager
import com.github.xbaimiao.shoppro.core.shop.ShopManager
import com.github.xbaimiao.shoppro.core.vault.DiyCurrency
import com.github.xbaimiao.shoppro.core.vault.VaultImpl
import com.xbaimiao.ktor.KtorPluginsBukkit
import com.xbaimiao.ktor.KtorStat
import org.bukkit.Bukkit
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent
import taboolib.common.platform.Plugin
import taboolib.common.platform.event.SubscribeEvent
import taboolib.common.platform.function.info
import taboolib.module.configuration.Config
import taboolib.module.configuration.Configuration
@@ -33,7 +38,13 @@ object ShopPro : Plugin(), KtorStat {
}
ShopManager.load()
DiyCurrency.load()
VaultImpl.startTask()
database = if (config.getBoolean("mysql.enable")) MysqlDatabase(config) else SQLiteDatabase()
Bukkit.getOnlinePlayers().forEach {
database.loadPlayerData(it)
}
}
fun reload() {
@@ -41,7 +52,31 @@ object ShopPro : Plugin(), KtorStat {
ShopManager.shops.clear()
ShopManager.load()
DiyCurrency.load()
Bukkit.getOnlinePlayers().forEach {
database.releasePlayerData(it)
}
database = if (config.getBoolean("mysql.enable")) MysqlDatabase(config) else SQLiteDatabase()
Bukkit.getOnlinePlayers().forEach {
database.loadPlayerData(it)
}
}
}
override fun onDisable() {
VaultImpl.flush()
}
@SubscribeEvent
fun join(event: PlayerJoinEvent) {
val player = event.player
database.loadPlayerData(player)
}
@SubscribeEvent
fun quit(event: PlayerQuitEvent) {
val player = event.player
database.releasePlayerData(player)
}
}
@@ -29,4 +29,8 @@ interface Database {
fun reset()
}
fun loadPlayerData(player: Player)
fun releasePlayerData(player: Player)
}
@@ -3,6 +3,7 @@ package com.github.xbaimiao.shoppro.core.database
import com.github.xbaimiao.shoppro.core.item.Item
import com.github.xbaimiao.shoppro.core.item.impl.ItemsAdderShopItem
import org.bukkit.entity.Player
import taboolib.common.platform.function.submitAsync
import taboolib.module.database.Host
import taboolib.module.database.Table
import javax.sql.DataSource
@@ -25,6 +26,8 @@ abstract class SqlDatabase : Database {
// ServerTable 无 User
val playerLine = "user"
private val playerAlreadyDataCache = HashMap<String, HashMap<String, LimitData>>()
override fun reset() {
serverTable.workspace(dataSource) {
executeUpdate("DELETE FROM $serverTableName;").run()
@@ -50,8 +53,20 @@ abstract class SqlDatabase : Database {
} ?: LimitData.ofNull()
}
private fun Item.toCacheKey(): String {
val item = this
return "${item.key}-${item.material}-${if (item is ItemsAdderShopItem) item.custom else 0}"
}
override fun getPlayerAlreadyData(player: Player, item: Item): LimitData {
return playerTable.workspace(dataSource) {
val cache = playerAlreadyDataCache[player.name] ?: error("玩家 ${player.name} 数据缓存未加载")
val cacheKey = item.toCacheKey()
if (cache.containsKey(cacheKey)) {
return cache[cacheKey]!!
}
val databaseLimitData = playerTable.workspace(dataSource) {
select {
where {
playerLine eq player.uniqueId.toString()
@@ -65,6 +80,57 @@ abstract class SqlDatabase : Database {
}.firstOrNull {
LimitData.formString(this.getString(dataLine))
} ?: LimitData.ofNull()
cache[cacheKey] = databaseLimitData
return databaseLimitData
}
override fun setPlayerAlreadyData(player: Player, item: Item, amount: LimitData) {
val cache = playerAlreadyDataCache[player.name] ?: error("玩家 ${player.name} 数据缓存未加载")
val cacheKey = item.toCacheKey()
cache[cacheKey] = amount
submitAsync {
if (playerTable.workspace(dataSource) {
select {
where {
playerLine eq player.uniqueId.toString()
itemKeyLine eq item.key.toString()
itemMaterialLine eq item.material.toString()
if (item is ItemsAdderShopItem) {
itemCustomLine eq item.custom
}
}
}
}.find()) {
playerTable.workspace(dataSource) {
update {
where {
playerLine eq player.uniqueId.toString()
itemKeyLine eq item.key.toString()
itemMaterialLine eq item.material.toString()
if (item is ItemsAdderShopItem) {
itemCustomLine eq item.custom
}
}
set(dataLine, amount.toString())
}
}.run()
} else {
val custom = if (item is ItemsAdderShopItem) item.custom else 0
playerTable.workspace(dataSource) {
insert {
value(
item.key.toString(),
item.material.toString(),
custom,
amount.toString(),
player.uniqueId.toString()
)
}
}.run()
}
}
}
override fun addAmount(item: Item, player: Player, amount: LimitData) {
@@ -106,46 +172,12 @@ abstract class SqlDatabase : Database {
}
}
override fun setPlayerAlreadyData(player: Player, item: Item, amount: LimitData) {
if (playerTable.workspace(dataSource) {
select {
where {
playerLine eq player.uniqueId.toString()
itemKeyLine eq item.key.toString()
itemMaterialLine eq item.material.toString()
if (item is ItemsAdderShopItem) {
itemCustomLine eq item.custom
}
}
}
}.find()) {
playerTable.workspace(dataSource) {
update {
where {
playerLine eq player.uniqueId.toString()
itemKeyLine eq item.key.toString()
itemMaterialLine eq item.material.toString()
if (item is ItemsAdderShopItem) {
itemCustomLine eq item.custom
}
}
set(dataLine, amount.toString())
}
}.run()
} else {
val custom = if (item is ItemsAdderShopItem) item.custom else 0
playerTable.workspace(dataSource) {
insert {
value(
item.key.toString(),
item.material.toString(),
custom,
amount.toString(),
player.uniqueId.toString()
)
}
}.run()
}
override fun loadPlayerData(player: Player) {
playerAlreadyDataCache[player.name] = HashMap()
}
}
override fun releasePlayerData(player: Player) {
playerAlreadyDataCache.remove(player.name)
}
}
@@ -29,7 +29,7 @@ interface KetherCondition {
if (conditionIcon == null || conditionName == null) {
return ItemStack(Material.AIR)
}
return taboolib.platform.util.buildItem(conditionIcon!!) {
return com.github.xbaimiao.shoppro.util.buildItem(conditionIcon!!) {
name = conditionName!!.colored().replacePapi(player)
conditionLore.forEach {
lore += it.colored().replacePapi(player)
@@ -37,4 +37,4 @@ interface KetherCondition {
}
}
}
}
@@ -9,9 +9,7 @@ import org.bukkit.configuration.ConfigurationSection
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import taboolib.library.xseries.XMaterial
import taboolib.library.xseries.XSkull
import taboolib.library.xseries.parseToMaterial
import taboolib.platform.util.ItemBuilder
import java.util.*
class HeadShopItem(
@@ -29,8 +27,8 @@ class HeadShopItem(
}
override fun vanillaItem(player: Player): ItemStack {
return taboolib.platform.util.buildItem(material) {
skullTexture = XSkull.SkullTexture(head, UUID.randomUUID())
return com.github.xbaimiao.shoppro.util.buildItem(material) {
skullTexture = com.github.xbaimiao.shoppro.util.ItemBuilder.SkullTexture(head, UUID.randomUUID())
}
}
@@ -39,10 +37,10 @@ class HeadShopItem(
}
override fun buildItem(player: Player): ItemStack {
return taboolib.platform.util.buildItem(material) {
return com.github.xbaimiao.shoppro.util.buildItem(material) {
this.name = this@HeadShopItem.name
this.lore.addAll(this@HeadShopItem.lore)
skullTexture = XSkull.SkullTexture(head, UUID.randomUUID())
skullTexture = com.github.xbaimiao.shoppro.util.ItemBuilder.SkullTexture(head, UUID.randomUUID())
}
}
@@ -9,7 +9,6 @@ import org.bukkit.configuration.ConfigurationSection
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import taboolib.library.xseries.XMaterial
import taboolib.library.xseries.XSkull
import taboolib.module.chat.colored
import taboolib.platform.util.modifyLore
import java.util.*
@@ -49,12 +48,12 @@ class ItemImpl(
}
override fun buildItem(player: Player): ItemStack {
return taboolib.platform.util.buildItem(material) {
return com.github.xbaimiao.shoppro.util.buildItem(material) {
this.name = this@ItemImpl.name
this.lore.addAll(this@ItemImpl.lore)
custom?.let { customModelData = it }
head?.let {
skullTexture = XSkull.SkullTexture(it, UUID.randomUUID())
skullTexture = com.github.xbaimiao.shoppro.util.ItemBuilder.SkullTexture(it, UUID.randomUUID())
}
}
}
@@ -15,7 +15,7 @@ class ItemsAdderShopItem(
itemSetting: ItemSetting
) : ShopItem(itemSetting) {
final override val material: Material
override val material: Material
val custom: Int
@@ -34,13 +34,13 @@ class ItemsAdderShopItem(
}
override fun vanillaItem(player: Player): ItemStack {
return taboolib.platform.util.buildItem(material) {
return com.github.xbaimiao.shoppro.util.buildItem(material) {
this.customModelData = custom
}
}
override fun buildItem(player: Player): ItemStack {
return taboolib.platform.util.buildItem(material) {
return com.github.xbaimiao.shoppro.util.buildItem(material) {
this.name = this@ItemsAdderShopItem.name
this.lore.addAll(this@ItemsAdderShopItem.lore)
this.customModelData = custom
@@ -67,4 +67,4 @@ class ItemsAdderShopItem(
}
}
}
@@ -3,7 +3,6 @@ package com.github.xbaimiao.shoppro.core.item.impl
import com.github.xbaimiao.shoppro.core.item.Item
import com.github.xbaimiao.shoppro.core.item.ItemLoader
import com.github.xbaimiao.shoppro.core.shop.Shop
import ink.ptms.zaphkiel.Zaphkiel
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.configuration.ConfigurationSection
@@ -19,14 +18,14 @@ import pers.neige.neigeitems.manager.ItemManager
class NeigeItem(
private val neigeItem: ItemGenerator,
itemSetting: ItemSetting,
private val neigeId :String
private val neigeId: String
) : VanillaShopItem(itemSetting) {
override val material: Material
get() = neigeItem.staticItemStack.type
override fun vanillaItem(player: Player): ItemStack {
return ItemManager.getItemStack(neigeId,player)!!
return ItemManager.getItemStack(neigeId, player)!!
}
override fun equal(itemStack: ItemStack): Boolean {
@@ -42,8 +41,8 @@ class NeigeItem(
error("NeigeItems Plugin not found")
}
val neigeId = section.getString("material")!!.substring(prefix.length + 1)
val item = ItemManager.getItem(neigeId)?: error("NeigeItems $neigeId not found")
return NeigeItem(item, section.toItemSetting(char, shop),neigeId)
val item = ItemManager.getItem(neigeId) ?: error("NeigeItems $neigeId not found")
return NeigeItem(item, section.toItemSetting(char, shop), neigeId)
}
override fun parseToMaterial(section: ConfigurationSection): Material {
@@ -52,4 +51,4 @@ class NeigeItem(
}
}
}
@@ -12,7 +12,7 @@ import taboolib.platform.util.hasLore
open class VanillaShopItem(itemSetting: ItemSetting) : ShopItem(itemSetting) {
override fun vanillaItem(player: Player): ItemStack {
return taboolib.platform.util.buildItem(material)
return com.github.xbaimiao.shoppro.util.buildItem(material)
}
override fun equal(itemStack: ItemStack): Boolean {
@@ -20,7 +20,7 @@ open class VanillaShopItem(itemSetting: ItemSetting) : ShopItem(itemSetting) {
}
override fun buildItem(player: Player): ItemStack {
return taboolib.platform.util.buildItem(material) {
return com.github.xbaimiao.shoppro.util.buildItem(material) {
this.name = this@VanillaShopItem.name
this.lore.addAll(this@VanillaShopItem.lore)
}
@@ -35,4 +35,4 @@ open class VanillaShopItem(itemSetting: ItemSetting) : ShopItem(itemSetting) {
}
}
}
}
@@ -18,6 +18,7 @@ import taboolib.module.chat.colored
import taboolib.module.ui.ClickType
import taboolib.module.ui.openMenu
import taboolib.module.ui.type.Basic
import taboolib.module.ui.type.Chest
import taboolib.platform.util.giveItem
import taboolib.platform.util.hasItem
import taboolib.platform.util.sendLang
@@ -2,16 +2,59 @@ package com.github.xbaimiao.shoppro.core.vault
import com.github.xbaimiao.shoppro.util.Util.format
import org.bukkit.entity.Player
import org.bukkit.event.player.PlayerQuitEvent
import taboolib.common.platform.event.EventPriority
import taboolib.common.platform.event.SubscribeEvent
import taboolib.common.platform.function.submit
import taboolib.platform.compat.VaultService
import java.util.concurrent.ConcurrentLinkedQueue
object VaultImpl : Currency {
private val queue = ConcurrentLinkedQueue<ValueQueue>()
private data class ValueQueue(val player: Player, val money: Double)
fun startTask() {
submit(period = 20) {
flush()
}
}
@SubscribeEvent(priority = EventPriority.LOWEST)
fun quit(event: PlayerQuitEvent) {
val iterator = queue.iterator()
var amount = 0.0
while (iterator.hasNext()) {
val queue = iterator.next()
if (queue.player.uniqueId == event.player.uniqueId) {
amount += queue.money
iterator.remove()
}
}
if (amount > 0) {
realGiveMoney(event.player, amount)
}
}
fun flush() {
queue.groupBy { it.player }.forEach { (player, valueQueues) ->
val amount = valueQueues.sumOf { it.money }
realGiveMoney(player, amount)
}
queue.clear()
}
private fun realGiveMoney(player: Player, double: Double) {
VaultService.economy!!.depositPlayer(player, double.format())
}
override fun hasMoney(player: Player, double: Double): Boolean {
return getMoney(player) >= double.format()
}
override fun giveMoney(player: Player, double: Double) {
VaultService.economy!!.depositPlayer(player, double.format())
queue.offer(ValueQueue(player, double))
}
override fun takeMoney(player: Player, double: Double): Boolean {
@@ -26,5 +69,4 @@ object VaultImpl : Currency {
return VaultService.economy!!.getBalance(player).format()
}
}
}
@@ -0,0 +1,363 @@
package com.github.xbaimiao.shoppro.util
import com.mojang.authlib.GameProfile
import com.mojang.authlib.properties.Property
import org.bukkit.ChatColor
import org.bukkit.Color
import org.bukkit.Material
import org.bukkit.block.banner.Pattern
import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.EntityType
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.*
import org.bukkit.potion.PotionData
import org.bukkit.potion.PotionEffect
import taboolib.library.reflex.Reflex.Companion.getProperty
import taboolib.library.reflex.Reflex.Companion.invokeMethod
import taboolib.library.reflex.Reflex.Companion.setProperty
import taboolib.library.xseries.XMaterial
import taboolib.module.chat.colored
import taboolib.platform.util.isAir
import java.util.*
/**
* 通过现有物品构建新的物品
*
* @param itemStack 原始物品
* @param builder 构建器
* @return 新的物品
* @throws IllegalArgumentException 如果物品为空气
*/
fun buildItem(itemStack: ItemStack, builder: ItemBuilder.() -> Unit = {}): ItemStack {
if (itemStack.isAir()) {
error("air")
}
return ItemBuilder(itemStack).also(builder).build()
}
/**
* 通过 [Material] 材质构建新的物品
*
* @param material 材质
* @param builder 构建器
* @return 新的物品
* @throws IllegalArgumentException 如果材质为空气
*/
fun buildItem(material: Material, builder: ItemBuilder.() -> Unit = {}): ItemStack {
// 在低版本, 并没有 Material.isAir() 方法
if (material == Material.AIR) {
error("air")
}
return ItemBuilder(material).also(builder).build()
}
/**
* 通过 [XMaterial] 材质构建新的物品
*
* @param material 材质
* @param builder 构建器
* @return 新的物品
* @throws IllegalArgumentException 如果材质为空气
*/
fun buildItem(material: XMaterial, builder: ItemBuilder.() -> Unit = {}): ItemStack {
// 在低版本, 并没有 Material.isAir() 方法
if (material == XMaterial.AIR) {
error("air")
}
return ItemBuilder(material.parseMaterial() ?: error("material.parseMaterial() is null")).also(builder).build()
}
open class ItemBuilder {
class SkullTexture(val textures: String, val uuid: UUID? = UUID.randomUUID())
/**
* 物品材质
*/
var material: Material
/**
* 数量
*/
var amount = 1
/**
* 附加值(损伤值)
*/
var damage = 0
/**
* 展示名称
*/
var name: String? = null
/**
* 描述
*/
val lore = ArrayList<String>()
/**
* 标签
*/
val flags = ArrayList<ItemFlag>()
/**
* 附魔
*/
val enchants = HashMap<Enchantment, Int>()
/**
* 旗帜花纹
*/
val patterns = ArrayList<Pattern>()
/**
* 颜色
*/
var color: Color? = null
/**
* 药水效果
*/
val potions = ArrayList<PotionEffect>()
/**
* 基础药水效果
*/
var potionData: PotionData? = null
/**
* 生物类型
*/
var spawnType: EntityType? = null
/**
* 头颅信息
*/
var skullOwner: String? = null
/**
* 头颅材质信息
*/
var skullTexture: SkullTexture? = null
/**
* 无法破坏
*/
var isUnbreakable = false
/**
* CustomModelData
*/
var customModelData = -1
/**
* 原始数据
* 尝试修复自定义 nbt 失效的问题
*/
var originMeta: ItemMeta? = null
/**
* 当构建完成时,做出最后修改
*/
var finishing: (ItemStack) -> Unit = {}
/**
* 使其发光
*/
fun shiny() {
flags += ItemFlag.HIDE_ENCHANTS
enchants[Enchantment.LURE] = 1
}
/**
* 隐藏所有附加信息(赋予所有 ItemFlag)
*/
fun hideAll() {
flags.addAll(ItemFlag.values())
}
open fun getSkinValue(skull: ItemMeta): SkullTexture? {
var profile: GameProfile? = null
try {
profile = skull.getProperty("profile")
} catch (ignored: Exception) {
}
if (profile != null && !profile.properties["textures"].isEmpty()) {
for (property in profile.properties["textures"]) {
if (property.value.isNotEmpty()) {
return SkullTexture(property.value, profile.id)
}
}
}
return null
}
/**
* 上色
*/
fun colored() {
if (name != null) {
name = try {
name!!.colored()
} catch (ex: NoClassDefFoundError) {
ChatColor.translateAlternateColorCodes('&', name!!)
}
}
if (lore.isNotEmpty()) {
val newLore = try {
lore.colored()
} catch (ex: NoClassDefFoundError) {
lore.map { ChatColor.translateAlternateColorCodes('&', it) }
}
lore.clear()
lore += newLore
}
}
/**
* 构建物品
*/
open fun build(): ItemStack {
val itemStack = ItemStack(material)
itemStack.amount = amount
if (damage != 0) {
itemStack.durability = damage.toShort()
}
val itemMeta = originMeta ?: itemStack.itemMeta ?: return itemStack
itemMeta.setDisplayName(name)
itemMeta.lore = lore
kotlin.runCatching {
itemMeta.addItemFlags(*flags.map { org.bukkit.inventory.ItemFlag.valueOf(it.name) }.toTypedArray())
}
if (itemMeta is EnchantmentStorageMeta) {
enchants.forEach { (e, lvl) -> itemMeta.addStoredEnchant(e, lvl, true) }
} else {
enchants.forEach { (e, lvl) -> itemMeta.addEnchant(e, lvl, true) }
}
when (itemMeta) {
is LeatherArmorMeta -> {
itemMeta.setColor(color)
}
is PotionMeta -> {
itemMeta.color = color
potions.forEach { itemMeta.addCustomEffect(it, true) }
if (potionData != null) {
itemMeta.basePotionData = potionData!!
}
}
is SkullMeta -> {
if (skullOwner != null) {
itemMeta.owner = skullOwner
}
if (skullTexture != null) {
itemMeta.setProperty("profile", GameProfile(skullTexture!!.uuid, null).also {
it.properties.put("textures", Property("textures", skullTexture!!.textures))
})
}
}
}
try {
itemMeta.isUnbreakable = isUnbreakable
} catch (ex: NoSuchMethodError) {
try {
itemMeta.invokeMethod<Any>("spigot")!!.invokeMethod<Any>("setUnbreakable", isUnbreakable)
} catch (ignored: NoSuchMethodException) {
}
}
try {
if (spawnType != null && itemMeta is SpawnEggMeta) {
itemMeta.spawnedType = spawnType
}
} catch (ignored: NoClassDefFoundError) {
}
try {
if (patterns.isNotEmpty() && itemMeta is BannerMeta) {
patterns.forEach { itemMeta.addPattern(it) }
}
} catch (ignored: NoClassDefFoundError) {
}
try {
if (customModelData != -1) {
itemMeta.invokeMethod<Void>("setCustomModelData", customModelData)
}
} catch (ignored: NoSuchMethodException) {
}
itemStack.itemMeta = itemMeta
finishing(itemStack)
return itemStack
}
constructor(material: Material) {
this.material = material
}
/**
* 通过现有物品构建新的物品
* 读取基本信息
*/
constructor(item: ItemStack) {
material = item.type
amount = item.amount
damage = item.durability.toInt()
// 如果物品没有 ItemMeta 则不进行后续操作
val itemMeta = item.itemMeta ?: return
originMeta = itemMeta
name = itemMeta.displayName
lore += itemMeta.lore ?: emptyList()
flags += kotlin.runCatching { itemMeta.itemFlags.map { ItemFlag.valueOf(it.name) } }.getOrElse { emptyList() }
enchants += if (itemMeta is EnchantmentStorageMeta) {
itemMeta.storedEnchants
} else {
itemMeta.enchants
}
when (itemMeta) {
is LeatherArmorMeta -> {
color = itemMeta.color
}
is PotionMeta -> {
color = itemMeta.color
potions += itemMeta.customEffects
potionData = itemMeta.basePotionData
}
is SkullMeta -> {
if (itemMeta.owner != null) {
skullOwner = itemMeta.owner
}
skullTexture = getSkinValue(itemMeta)
}
}
try {
customModelData = itemMeta.getProperty<Int>("customModelData") ?: -1
} catch (ignored: NoSuchFieldException) {
}
try {
isUnbreakable = itemMeta.isUnbreakable
} catch (ex: NoSuchMethodError) {
try {
isUnbreakable = itemMeta.invokeMethod<Any>("spigot")!!.invokeMethod<Boolean>("isUnbreakable") ?: false
} catch (ignored: NoSuchMethodException) {
}
}
try {
if (itemMeta is SpawnEggMeta && itemMeta.spawnedType != null) {
spawnType = itemMeta.spawnedType
}
} catch (ignored: NoClassDefFoundError) {
} catch (ignored: UnsupportedOperationException) {
}
try {
if (itemMeta is BannerMeta && itemMeta.patterns.isNotEmpty()) {
patterns += itemMeta.patterns
}
} catch (ignored: NoClassDefFoundError) {
}
}
}
@@ -0,0 +1,11 @@
package com.github.xbaimiao.shoppro.util;
public enum ItemFlag {
HIDE_ENCHANTS,
HIDE_ATTRIBUTES,
HIDE_UNBREAKABLE,
HIDE_DESTROYS,
HIDE_PLACED_ON,
HIDE_POTION_EFFECTS,
HIDE_DYE
}