From b3bdc95f639673c0d35cfc545b84bb1c63040851 Mon Sep 17 00:00:00 2001 From: Hare Date: Sun, 7 Dec 2025 07:14:46 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9C=80=E9=81=A9=E5=8C=96=E3=81=A8Kom?= =?UTF-8?q?mand=E3=82=A2=E3=83=83=E3=83=97=E3=83=87=E3=83=BC=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kommand-lib | 2 +- .../ghostdisplays/GhostDisplaysPlugin.kt | 4 +-- .../ghostdisplays/api/DisplayController.kt | 6 +++++ .../api/audience/AudiencePredicate.kt | 8 ++++++ .../api/audience/AudiencePredicates.kt | 9 ++++--- .../controller/BaseDisplayController.kt | 25 +++++++++++++++++++ .../internal/controller/DisplayRegistry.kt | 10 +++++--- 7 files changed, 54 insertions(+), 10 deletions(-) diff --git a/kommand-lib b/kommand-lib index e0613cd..3835c9b 160000 --- a/kommand-lib +++ b/kommand-lib @@ -1 +1 @@ -Subproject commit e0613cd05278222b0a2ac1c79a2d4ef317c9cea1 +Subproject commit 3835c9b9e2d488681b39f4cfd827e3e3371a1e1b diff --git a/src/main/kotlin/net/hareworks/ghostdisplays/GhostDisplaysPlugin.kt b/src/main/kotlin/net/hareworks/ghostdisplays/GhostDisplaysPlugin.kt index 3a716cc..5e87363 100644 --- a/src/main/kotlin/net/hareworks/ghostdisplays/GhostDisplaysPlugin.kt +++ b/src/main/kotlin/net/hareworks/ghostdisplays/GhostDisplaysPlugin.kt @@ -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() { diff --git a/src/main/kotlin/net/hareworks/ghostdisplays/api/DisplayController.kt b/src/main/kotlin/net/hareworks/ghostdisplays/api/DisplayController.kt index 76ef2ce..dfa7bbd 100644 --- a/src/main/kotlin/net/hareworks/ghostdisplays/api/DisplayController.kt +++ b/src/main/kotlin/net/hareworks/ghostdisplays/api/DisplayController.kt @@ -44,6 +44,12 @@ interface DisplayController { fun refreshAudience(target: Player? = null) + /** + * 定期的な更新チェックが必要かどうかを返します。 + * trueの場合、サーバーのtick毎(または定期タスク)にrefreshAudienceが呼び出されます。 + */ + fun needsPeriodicUpdate(): Boolean = false + fun destroy() fun onClick(priority: ClickPriority = ClickPriority.NORMAL, handler: DisplayClickHandler): HandlerRegistration diff --git a/src/main/kotlin/net/hareworks/ghostdisplays/api/audience/AudiencePredicate.kt b/src/main/kotlin/net/hareworks/ghostdisplays/api/audience/AudiencePredicate.kt index 2c29368..623aa80 100644 --- a/src/main/kotlin/net/hareworks/ghostdisplays/api/audience/AudiencePredicate.kt +++ b/src/main/kotlin/net/hareworks/ghostdisplays/api/audience/AudiencePredicate.kt @@ -7,4 +7,12 @@ import org.bukkit.entity.Player */ fun interface AudiencePredicate { fun test(player: Player): Boolean + + /** + * 表示対象判定が動的(時間経過や移動で変化する)かどうかを返します。 + * trueの場合、定期的な再評価の対象となります。 + */ + fun isDynamic(): Boolean { + return false + } } diff --git a/src/main/kotlin/net/hareworks/ghostdisplays/api/audience/AudiencePredicates.kt b/src/main/kotlin/net/hareworks/ghostdisplays/api/audience/AudiencePredicates.kt index f910645..9376d7c 100644 --- a/src/main/kotlin/net/hareworks/ghostdisplays/api/audience/AudiencePredicates.kt +++ b/src/main/kotlin/net/hareworks/ghostdisplays/api/audience/AudiencePredicates.kt @@ -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 } } } diff --git a/src/main/kotlin/net/hareworks/ghostdisplays/internal/controller/BaseDisplayController.kt b/src/main/kotlin/net/hareworks/ghostdisplays/internal/controller/BaseDisplayController.kt index 9e8d3f5..6e22dc7 100644 --- a/src/main/kotlin/net/hareworks/ghostdisplays/internal/controller/BaseDisplayController.kt +++ b/src/main/kotlin/net/hareworks/ghostdisplays/internal/controller/BaseDisplayController.kt @@ -32,10 +32,16 @@ internal class BaseDisplayController( private val viewerCounts = ConcurrentHashMap() private val handlers = CopyOnWriteArrayList() + // Audience Management // Audience Management private var baseVisibility: Boolean = false private val audienceRules = CopyOnWriteArrayList() private val autoVisiblePlayers = ConcurrentHashMap.newKeySet() + + // 最適化: 定期更新が必要なルールがあるか + private var hasDynamicRules: Boolean = false + + override fun needsPeriodicUpdate(): Boolean = hasDynamicRules override fun show(player: Player) { runSync { @@ -110,12 +116,31 @@ internal class BaseDisplayController( 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() diff --git a/src/main/kotlin/net/hareworks/ghostdisplays/internal/controller/DisplayRegistry.kt b/src/main/kotlin/net/hareworks/ghostdisplays/internal/controller/DisplayRegistry.kt index b031b7c..348da8c 100644 --- a/src/main/kotlin/net/hareworks/ghostdisplays/internal/controller/DisplayRegistry.kt +++ b/src/main/kotlin/net/hareworks/ghostdisplays/internal/controller/DisplayRegistry.kt @@ -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) } }