feat: 最適化とKommandアップデート

This commit is contained in:
2025-12-07 07:14:46 +09:00
parent 4343d01d11
commit b3bdc95f63
7 changed files with 54 additions and 10 deletions
@@ -41,8 +41,8 @@ class GhostDisplaysPlugin : JavaPlugin() {
logger.info("GhostDisplays ready: ${displayRegistry.controllerCount()} controllers active.")
server.scheduler.runTaskTimer(this, Runnable {
displayRegistry.tick()
}, 10L, 10L)
displayRegistry.updateAudiences()
}, 20L, 20L)
}
override fun onDisable() {
@@ -44,6 +44,12 @@ interface DisplayController<T : Display> {
fun refreshAudience(target: Player? = null)
/**
* 定期的な更新チェックが必要かどうかを返します。
* trueの場合、サーバーのtick毎(または定期タスク)にrefreshAudienceが呼び出されます。
*/
fun needsPeriodicUpdate(): Boolean = false
fun destroy()
fun onClick(priority: ClickPriority = ClickPriority.NORMAL, handler: DisplayClickHandler): HandlerRegistration
@@ -7,4 +7,12 @@ import org.bukkit.entity.Player
*/
fun interface AudiencePredicate {
fun test(player: Player): Boolean
/**
* 表示対象判定が動的(時間経過や移動で変化する)かどうかを返します。
* trueの場合、定期的な再評価の対象となります。
*/
fun isDynamic(): Boolean {
return false
}
}
@@ -28,12 +28,15 @@ object AudiencePredicates {
}
fun near(location: Location, radius: Double): AudiencePredicate {
val radiusSq = radius * radius
val worldName = location.world?.name
require(worldName != null) { "Location must have a world" }
return AudiencePredicate { player ->
player.world.name == worldName && player.location.distanceSquared(location) <= radiusSq
return object : AudiencePredicate {
override fun test(player: Player): Boolean {
return player.world.name == worldName && player.location.distanceSquared(location) <= radiusSq
}
override fun isDynamic(): Boolean = true
}
}
}
@@ -32,11 +32,17 @@ internal class BaseDisplayController<T : Display>(
private val viewerCounts = ConcurrentHashMap<UUID, Int>()
private val handlers = CopyOnWriteArrayList<HandlerEntry>()
// Audience Management
// Audience Management
private var baseVisibility: Boolean = false
private val audienceRules = CopyOnWriteArrayList<RuleEntry>()
private val autoVisiblePlayers = ConcurrentHashMap.newKeySet<UUID>()
// 最適化: 定期更新が必要なルールがあるか
private var hasDynamicRules: Boolean = false
override fun needsPeriodicUpdate(): Boolean = hasDynamicRules
override fun show(player: Player) {
runSync {
val uuid = player.uniqueId
@@ -110,13 +116,32 @@ internal class BaseDisplayController<T : Display>(
override fun addAudienceRule(predicate: AudiencePredicate, action: AudienceAction): HandlerRegistration {
val entry = RuleEntry(predicate, action)
audienceRules.add(entry)
updateDynamicStatus()
refreshAudience()
return HandlerRegistration {
audienceRules.remove(entry)
updateDynamicStatus()
refreshAudience()
}
}
private fun updateDynamicStatus() {
// 簡易判定: クラス名に "Near" が含まれる、または特定の実装であることを期待する
// ただし現状はラムダなので名前判定は不安定。
// AudiencePredicate 側にマーカーインターフェースをつけるのが正しいが、
// 今回は「ユーザー定義のPredicate」も含めて、動的かどうかわからない。
// -> AudiencePredicates.near() が返すオブジェクトに特徴を持たせる。
hasDynamicRules = audienceRules.any { isDynamicPredicate(it.predicate) }
}
private fun isDynamicPredicate(predicate: AudiencePredicate): Boolean {
// AudiencePredicates.near が返すクラスの実装詳細に依存するか、
// AudiencePredicate にプロパティを追加する。
// ここでは一旦、toString() 等で識別するか?いや、安全ではない。
// API変更: AudiencePredicate に default isDynamic() を追加する。
return predicate.isDynamic()
}
override fun clearAudienceRules() {
audienceRules.clear()
refreshAudience()
@@ -82,16 +82,18 @@ internal class DisplayRegistry : Listener {
controllers.forEach { it.refreshAudience(player) }
}
fun tick() {
fun updateAudiences() {
val players = org.bukkit.Bukkit.getOnlinePlayers()
if (players.isEmpty()) return
val controllers = controllersSnapshot()
if (controllers.isEmpty()) return
// 最適化: 動的な評価が必要なコントローラーだけ抽出できれば良いが、
// 現状はシンプルに全走査する。
// 動的な評価が必要なコントローラーだけ抽出して評価する
val activeControllers = controllers.filter { it.needsPeriodicUpdate() }
if (activeControllers.isEmpty()) return
players.forEach { player ->
controllers.forEach { controller ->
activeControllers.forEach { controller ->
controller.refreshAudience(player)
}
}