Skip to content

Commit fc64862

Browse files
committed
fix(core): 修复菜单节点缓存和异步线程安全问题
- 在 MenuSession 中添加节点缓存机制,避免菜单关闭后无法读取节点值 - 修改 HookPlaceholderAPI 中的 node 类型解析逻辑,使用新的缓存方法 - 在 RegisterCommands 中添加主线程检查,确保命令加载在主线程执行 - 更新在线玩家获取方式,使用 onlinePlayers 替代 Bukkit.getOnlinePlayers() - 修复潜在的 ConcurrentModificationException 问题
1 parent 1f4460d commit fc64862

6 files changed

Lines changed: 34 additions & 8 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
.gradle
22
.idea
3-
build
3+
build
4+
.serena
5+
AGENTS.md

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
group=me.arasple.mc.trmenu
2-
version=3.9.21
2+
version=3.9.23

plugin/src/main/kotlin/trplugins/menu/module/display/Menu.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class Menu(
9292

9393
if (menuOpenEvent.isCancelled) return
9494
session.menu = this
95+
session.cacheNodes()
9596
block(session)
9697

9798
if (!Metadata.byBukkit(viewer, "FORCE_OPEN") && !settings.openEvent.eval(adaptPlayer(session.viewer))) {

plugin/src/main/kotlin/trplugins/menu/module/display/MenuSession.kt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ class MenuSession(
8383
// 临时任务(切换页码时允许删除)
8484
private val temporaries = mutableSetOf<PlatformExecutor.PlatformTask>()
8585

86+
// 缓存菜单节点,避免菜单关闭后无法读取
87+
private var nodeCache: Map<String, Any?> = emptyMap()
88+
8689
var locale: String = kotlin.run {
8790
if (langPlayer.isNotBlank()) {
8891
// Avoid ConcurrentModificationException
@@ -131,9 +134,7 @@ class MenuSession(
131134
val funced = FunctionParser.parse(placeholderPlayer, string) { type, value ->
132135
when (type) {
133136
"node", "nodes", "n" -> parseNode(value) { key ->
134-
val config = menu?.conf ?: return@parseNode null
135-
val foundKey = config.getKeys(true).find { it.equals(key, ignoreCase = true) } ?: return@parseNode null
136-
config[foundKey]
137+
getNodeValue(key)
137138
}
138139
"lang" -> parseNode(value) { key ->
139140
menu?.getLocaleValue(locale, key)
@@ -152,6 +153,21 @@ class MenuSession(
152153
return string.map { parse(it) }
153154
}
154155

156+
fun cacheNodes() {
157+
val config = menu?.conf
158+
nodeCache = config?.getKeys(true)?.associateBy({ it.lowercase() }) { config[it] } ?: emptyMap()
159+
}
160+
161+
fun getNodeValue(key: String): Any? {
162+
val normalizedKey = key.lowercase()
163+
if (nodeCache.containsKey(normalizedKey)) {
164+
return nodeCache[normalizedKey]
165+
}
166+
val config = menu?.conf ?: return null
167+
val foundKey = config.getKeys(true).find { it.equals(key, ignoreCase = true) } ?: return null
168+
return config[foundKey]
169+
}
170+
155171
private fun parseNode(node: String, valueSupplier: (String) -> Any?): String? {
156172
var key = node
157173
val arguments: Array<String>? = if (key.contains('_')) {

plugin/src/main/kotlin/trplugins/menu/module/internal/hook/ext/HookPlaceholderAPI.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ object HookPlaceholderAPI : PlaceholderExpansion {
3939
"meta" -> Metadata.getMeta(player)[key]
4040
"data" -> Metadata.getData(player)[key]
4141
"globaldata" -> runCatching { Metadata.globalData[value()].toString() }.getOrElse { "null" }
42-
"node" -> session.menu?.conf?.let { it[it.ignoreCase(value())].toString() }.toString()
42+
"node" -> session.getNodeValue(value())?.toString() ?: "null"
4343
"menu" -> menu(session, params)
4444
"locale" -> session.locale
4545
"js" -> if (enabledParseJavaScript) if (params.size > 1) JavaScriptAgent.eval(session, params[1]).asString() else "" else "UNABLE_PARSE"
@@ -75,4 +75,4 @@ object HookPlaceholderAPI : PlaceholderExpansion {
7575
}
7676
}
7777

78-
}
78+
}

plugin/src/main/kotlin/trplugins/menu/module/internal/service/RegisterCommands.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import taboolib.common.platform.command.command
77
import taboolib.common.platform.function.adaptPlayer
88
import taboolib.common.platform.function.submit
99
import taboolib.common.platform.function.unregisterCommand
10+
import taboolib.platform.util.onlinePlayers
1011
import trplugins.menu.TrMenu
1112
import trplugins.menu.TrMenu.actionHandle
1213
import trplugins.menu.api.reaction.Reactions
@@ -26,6 +27,12 @@ object RegisterCommands {
2627
}
2728

2829
fun load() {
30+
if (!Bukkit.isPrimaryThread()) {
31+
submit {
32+
load()
33+
}
34+
return
35+
}
2936
val unregisterList = registered.toList()
3037
unregisterList.forEach { unregisterCommand(it) }
3138
registered.clear()
@@ -78,7 +85,7 @@ object RegisterCommands {
7885
// 延迟同步命令到所有在线玩家,避免与 Paper 异步命令发送线程冲突
7986
// Paper 的 sendAsync 会在异步线程遍历命令树,直接调用 updateCommands 可能触发 ConcurrentModificationException
8087
submit(delay = 1) {
81-
val players = Bukkit.getOnlinePlayers()
88+
val players = onlinePlayers
8289
if (players.isEmpty()) {
8390
return@submit
8491
}

0 commit comments

Comments
 (0)