Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,10 @@ abstract class NMS {
abstract fun sendWindowsUpdateData(player: Player, windowId: Int = windowId(player), id: Int, value: Int)

abstract fun toNMSCopy(itemStack: ItemStack?): Any?

/**
* 发包菜单不会走 Bukkit openInventory,客户端右键没松开时会卡在使用状态。
* 所以打断举盾/拉弓等“正在使用物品”。
*/
abstract fun clearActiveItem(player: Player)
}
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,15 @@ class NMSImpl : NMS() {
}
}

override fun clearActiveItem(player: Player) {
runCatching { player.javaClass.getMethod("clearActiveItem").invoke(player); return }
val handle = runCatching { player.invokeMethod<Any>("getHandle") }.getOrNull() ?: return
// clearActiveItem: Spigot 1.16 及以下映射名,stopUsingItem: Mojang 映射名
arrayOf("clearActiveItem", "stopUsingItem").firstOrNull { name ->
runCatching { handle.invokeMethod<Any>(name); true }.getOrDefault(false)
}
}

override fun toNMSCopy(itemStack: ItemStack?): Any? {
if (itemStack.isAir()) return emptyItemStack
return if (isUnobfuscated) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,14 @@ open class WindowReceptacle(var type: WindowLayout, override var title: String =
override fun open(player: Player) {
viewer = player
player.setViewingReceptacle(this)
nmsProxy<NMS>().clearActiveItem(player)
initializationPackets()
onOpen(player, this)
}

override fun close(sendPacket: Boolean) {
if (viewer != null) {
nmsProxy<NMS>().clearActiveItem(viewer!!)
if (sendPacket) {
nmsProxy<NMS>().sendWindowsClose(viewer!!)
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
group=me.arasple.mc.trmenu
version=3.12.5
version=3.12.6
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ class DelData(handle: ActionHandle) : ActionBase(handle) {

contents.stringContent().parseContentSplited(placeholderPlayer, ";").forEach { it ->
val regex = Regex(it)
data.keys.filter { it.matches(regex) }.forEach { data.remove(it) }
data.keys.filter { it.matches(regex) }.forEach {
Metadata.setData(player.cast(), Metadata.DataType.DATA, it, null)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ class SetData(handle: ActionHandle) : ActionBase(handle) {
val key = split[0]
val value = split[1]

Metadata.getData(player)[key] = value
if (!Metadata.isUseLegacy) {
Metadata.saveData(player.cast(), key)
}
Metadata.setData(player.cast(), Metadata.DataType.DATA, key, value)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package trplugins.menu.module.internal.data

import java.util.concurrent.Executors
import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.TimeUnit

/** Serializes player data writes and reads so a pending write cannot overtake a deletion. */
internal class DataStorageQueue(private val onFailure: (Throwable) -> Unit) : AutoCloseable {

private val executor = Executors.newSingleThreadExecutor { task ->
Thread(task, "TrMenu-PlayerData").apply { isDaemon = true }
}

fun write(action: () -> Unit) {
val task = Runnable {
try {
action()
} catch (ex: Exception) {
onFailure(ex)
}
}
try {
executor.execute(task)
} catch (ex: RejectedExecutionException) {
// Queue already closed (plugin disabling), save directly
task.run()
}
}

/** Called from the asynchronous player-loading task, after all earlier writes. */
fun <T> read(action: () -> T): T = executor.submit<T> { action() }.get()

override fun close() {
executor.shutdown()
try {
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
onFailure(IllegalStateException("Timed out waiting for player data to be saved"))
}
} catch (ex: InterruptedException) {
Thread.currentThread().interrupt()
onFailure(ex)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ object Metadata {
internal val data = mutableMapOf<String, DataMap>()
internal val global = ConcurrentHashMap<String, Any?>()

private val storageQueue = DataStorageQueue { it.printStackTrace() }

@Config("data/globalData.yml")
lateinit var globalData: Configuration

Expand Down Expand Up @@ -91,11 +93,22 @@ object Metadata {
}

fun saveData(player: Player, key: String) {
submitAsync {
MetaDataDao.door.update(DataEntity.constructor(player, key, getData(player)[key]?.toString() ?: ""))
val uuid = player.uniqueId
val value = getData(player)[key]?.toString()
storageQueue.write {
if (value == null) {
MetaDataDao.door.delete(uuid, key)
} else {
MetaDataDao.door.update(DataEntity(uuid, key, value))
}
}
}

@Awake(LifeCycle.DISABLE)
fun closeStorage() {
storageQueue.close()
}

fun pushData(player: Player, dataMap: DataMap = getData(player)) {
if (isUseLegacy) {
getLocalePlayer(player)?.let {
Expand All @@ -108,8 +121,16 @@ object Metadata {
}
database?.push(player)
} else {
dataMap.data.forEach { (key, value) ->
MetaDataDao.door.update(DataEntity.constructor(player, key, value?.toString() ?: ""))
val uuid = player.uniqueId
val snapshot = dataMap.data.mapValues { it.value?.toString() }
storageQueue.write {
snapshot.forEach { (key, value) ->
if (value == null) {
MetaDataDao.door.delete(uuid, key)
} else {
MetaDataDao.door.update(DataEntity(uuid, key, value))
}
}
}
}
}
Expand All @@ -126,7 +147,8 @@ object Metadata {
section.getKeys(true).forEach { key -> map[key] = section[key] }
}
} else {
MetaDataDao.door.get(player.uniqueId).forEach {
val uuid = player.uniqueId
storageQueue.read { MetaDataDao.door.get(uuid) }.forEach {
map[it.key] = it.data
}
}
Expand Down Expand Up @@ -227,7 +249,11 @@ object Metadata {
fun setData(player: Player, dataType: DataType, dataName: String, value: Any?) {
when (dataType) {
DataType.DATA -> {
getData(player)[dataName] = value
if (value == null) {
getData(player).remove(dataName)
} else {
getData(player)[dataName] = value
}
if (!isUseLegacy) {
saveData(player, dataName)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ class MetaDataDao {
}.filter { it?.data?.isNotEmpty() == true }.mapNotNull { it }
}

fun delete(uuid: UUID, key: String) {
val containerx = container[table]
containerx.table.delete(containerx.dataSource) {
where {
"user" eq uuid.toString()
"key" eq key
}
}
}

fun get(uuid: UUID, key: String): DataEntity? {
return container[table].getOne<DataEntity?> {
"user" eq uuid.toString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ object ListenerQuit {
val player = e.player
val session = MenuSession.getSession(player)
session.shut()
submit(async = true) {
if (Metadata.isUseLegacy) {
submit(async = true) {
Metadata.pushData(player)
}
} else {
// Capture the snapshot before another session can modify this player's data.
Metadata.pushData(player)
}
Menu.menus.forEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,15 @@ class KetherData(val type: EditType, private val source: ParsedAction<*>, privat
return context.newFrame(source).run<String>().thenApply { key ->
when (type) {
DEL -> {
Metadata.getData(viewer).remove(key).also {
if (!Metadata.isUseLegacy) {
Metadata.saveData(viewer, key)
}
Metadata.getData(viewer)[key].also {
Metadata.setData(viewer, Metadata.DataType.DATA, key, null)
}
}

SET -> {
apply?.let { it1 ->
context.newFrame(it1).run<String>().thenApply { apply ->
Metadata.getData(viewer)[key] = apply.also {
if (!Metadata.isUseLegacy) {
Metadata.saveData(viewer, key)
}
}
Metadata.setData(viewer, Metadata.DataType.DATA, key, apply)
}
}
}
Expand Down
Loading