From 640a0a3e1c42cb2ad6f9634859abac278a174465 Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 11:45:22 +0200 Subject: [PATCH 01/13] =?UTF-8?q?=E2=9C=A8=20feat(command):=20implement=20?= =?UTF-8?q?suspend=20command=20requirements=20and=20packet=20blocking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add CommandSendPacketBlockerListener for blocking commands to specific players - create SuspendRequirementService for managing command execution requirements - integrate new listener into existing event handling for command execution - update NmsProvider to support new command blocking functionality --- .../CommandSendPacketBlockerListener.kt | 8 + .../surf/api/paper/nms/common/NmsProvider.kt | 3 + .../nms/v1_21_11/V1_21_11NmsProvider.kt | 6 + ..._11CommandSendPacketBlockerListenerImpl.kt | 23 +++ .../server/nms/v26_1/V26_1NmsProvider.kt | 6 + ...6_1CommandSendPacketBlockerListenerImpl.kt | 24 +++ .../command/SuspendRequirementServiceImpl.kt | 155 ++++++++++++++++++ .../paper/server/listener/ListenerManager.kt | 3 + .../paper/server/packet/PacketApiLoader.kt | 3 + .../requirement/SuspendCommandRequirements.kt | 18 ++ .../requirement/SuspendRequirementService.kt | 23 +++ 11 files changed, 272 insertions(+) create mode 100644 surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt create mode 100644 surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt create mode 100644 surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt create mode 100644 surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt create mode 100644 surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt create mode 100644 surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendRequirementService.kt diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt new file mode 100644 index 00000000..0d09dd19 --- /dev/null +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt @@ -0,0 +1,8 @@ +package dev.slne.surf.api.paper.nms.common + +import dev.slne.surf.api.paper.nms.NmsUseWithCaution +import dev.slne.surf.api.paper.packet.listener.listener.PacketListener +import java.util.* + +@OptIn(NmsUseWithCaution::class) +abstract class CommandSendPacketBlockerListener(protected val blockedPlayer: Set) : PacketListener \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/NmsProvider.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/NmsProvider.kt index e38e41e6..0a8d7133 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/NmsProvider.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/NmsProvider.kt @@ -18,6 +18,7 @@ import dev.slne.surf.api.shared.internal.nms.NmsVersion import org.bukkit.plugin.java.JavaPlugin import java.io.File import java.net.URI +import java.util.* import java.util.jar.JarFile /** @@ -84,6 +85,8 @@ interface NmsProvider { fun createChannelInjector(): AbstractChannelInjector<*> fun createPacketListenerApi(): InternalPacketListenerApiBridge + fun createCommandSendPacketBlockerListener(blockedPlayer: Set): CommandSendPacketBlockerListener + /** * Creates version-specific packet listeners (e.g. lore handler, glowing handler). * diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/V1_21_11NmsProvider.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/V1_21_11NmsProvider.kt index 25b02856..7610ecb7 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/V1_21_11NmsProvider.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/V1_21_11NmsProvider.kt @@ -22,6 +22,7 @@ import dev.slne.surf.api.paper.server.nms.v1_21_11.bridges.packets.player.V1_21_ import dev.slne.surf.api.paper.server.nms.v1_21_11.glow.V1_21_11GlowingLifecycleHandler import dev.slne.surf.api.paper.server.nms.v1_21_11.glow.V1_21_11SurfGlowingApiImpl import dev.slne.surf.api.paper.server.nms.v1_21_11.packet.listener.V1_21_11ChannelInjector +import dev.slne.surf.api.paper.server.nms.v1_21_11.packet.listener.V1_21_11CommandSendPacketBlockerListenerImpl import dev.slne.surf.api.paper.server.nms.v1_21_11.packet.listener.V1_21_11GlowingPacketListener import dev.slne.surf.api.paper.server.nms.v1_21_11.packet.lore.V1_21_11PacketLoreListener import dev.slne.surf.api.paper.server.nms.v1_21_11.packet.lore.V1_21_11PacketLoreRegistry @@ -30,6 +31,7 @@ import dev.slne.surf.api.paper.server.nms.v1_21_11.region.V1_21_11TickThreadGuar import dev.slne.surf.api.shared.internal.nms.NmsProviderMarker import dev.slne.surf.api.shared.internal.nms.NmsVersion import org.bukkit.plugin.java.JavaPlugin +import java.util.* @Suppress("ClassName") @OptIn(NmsUseWithCaution::class) @@ -89,6 +91,10 @@ class V1_21_11NmsProvider(override val plugin: JavaPlugin) : NmsProvider { override fun createChannelInjector(): AbstractChannelInjector<*> = V1_21_11ChannelInjector override fun createPacketListenerApi(): InternalPacketListenerApiBridge = V1_21_11PacketListenerApiImpl() + override fun createCommandSendPacketBlockerListener(blockedPlayer: Set): CommandSendPacketBlockerListener { + return V1_21_11CommandSendPacketBlockerListenerImpl(blockedPlayer) + } + override fun createPacketListeners(): List = listOf( V1_21_11PacketLoreListener, V1_21_11GlowingPacketListener, diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt new file mode 100644 index 00000000..7211f1c9 --- /dev/null +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt @@ -0,0 +1,23 @@ +package dev.slne.surf.api.paper.server.nms.v1_21_11.packet.listener + +import dev.slne.surf.api.paper.nms.NmsUseWithCaution +import dev.slne.surf.api.paper.nms.common.CommandSendPacketBlockerListener +import dev.slne.surf.api.paper.packet.listener.listener.annotation.ClientboundListener +import net.minecraft.network.protocol.game.ClientboundCommandsPacket +import net.minecraft.server.level.ServerPlayer +import java.util.* + +@OptIn(NmsUseWithCaution::class) +@Suppress("ClassName") +class V1_21_11CommandSendPacketBlockerListenerImpl(blockedPlayer: Set) : + CommandSendPacketBlockerListener(blockedPlayer) { + + @ClientboundListener + fun onClientboundCommandsPacket( + packet: ClientboundCommandsPacket, + player: ServerPlayer + ): ClientboundCommandsPacket? { + if (blockedPlayer.contains(player.uuid)) return null + return packet + } +} \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/V26_1NmsProvider.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/V26_1NmsProvider.kt index 77480d57..9199c7a9 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/V26_1NmsProvider.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/V26_1NmsProvider.kt @@ -22,6 +22,7 @@ import dev.slne.surf.api.paper.server.nms.v26_1.bridges.packets.player.V26_1Surf import dev.slne.surf.api.paper.server.nms.v26_1.glow.V26_1GlowingLifecycleHandler import dev.slne.surf.api.paper.server.nms.v26_1.glow.V26_1SurfGlowingApiImpl import dev.slne.surf.api.paper.server.nms.v26_1.packet.listener.V26_1ChannelInjector +import dev.slne.surf.api.paper.server.nms.v26_1.packet.listener.V26_1CommandSendPacketBlockerListenerImpl import dev.slne.surf.api.paper.server.nms.v26_1.packet.listener.V26_1GlowingPacketListener import dev.slne.surf.api.paper.server.nms.v26_1.packet.lore.V26_1PacketLoreListener import dev.slne.surf.api.paper.server.nms.v26_1.packet.lore.V26_1PacketLoreRegistry @@ -30,6 +31,7 @@ import dev.slne.surf.api.paper.server.nms.v26_1.region.V26_1TickThreadGuard import dev.slne.surf.api.shared.internal.nms.NmsProviderMarker import dev.slne.surf.api.shared.internal.nms.NmsVersion import org.bukkit.plugin.java.JavaPlugin +import java.util.* @Suppress("ClassName") @OptIn(NmsUseWithCaution::class) @@ -68,6 +70,10 @@ class V26_1NmsProvider(override val plugin: JavaPlugin) : NmsProvider { override fun createChannelInjector(): AbstractChannelInjector<*> = V26_1ChannelInjector override fun createPacketListenerApi(): InternalPacketListenerApiBridge = V26_1PacketListenerApiImpl() + override fun createCommandSendPacketBlockerListener(blockedPlayer: Set): CommandSendPacketBlockerListener { + return V26_1CommandSendPacketBlockerListenerImpl(blockedPlayer) + } + override fun createPacketListeners(): List = listOf( V26_1PacketLoreListener, V26_1GlowingPacketListener, diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt new file mode 100644 index 00000000..0d4b0a1a --- /dev/null +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt @@ -0,0 +1,24 @@ +package dev.slne.surf.api.paper.server.nms.v26_1.packet.listener + +import dev.slne.surf.api.paper.nms.NmsUseWithCaution +import dev.slne.surf.api.paper.nms.common.CommandSendPacketBlockerListener +import dev.slne.surf.api.paper.packet.listener.listener.annotation.ClientboundListener +import net.minecraft.network.protocol.game.ClientboundCommandsPacket +import net.minecraft.server.level.ServerPlayer +import java.util.* + + +@Suppress("ClassName") +@OptIn(NmsUseWithCaution::class) +class V26_1CommandSendPacketBlockerListenerImpl(blockedPlayer: Set) : + CommandSendPacketBlockerListener(blockedPlayer) { + + @ClientboundListener + fun onClientboundCommandsPacket( + packet: ClientboundCommandsPacket, + player: ServerPlayer + ): ClientboundCommandsPacket? { + if (blockedPlayer.contains(player.uuid)) return null + return packet + } +} \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt new file mode 100644 index 00000000..e9bc9aa1 --- /dev/null +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt @@ -0,0 +1,155 @@ +package dev.slne.surf.api.paper.server.command + +import com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent +import com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent +import com.github.benmanes.caffeine.cache.Caffeine +import com.github.shynixn.mccoroutine.folia.launch +import com.google.auto.service.AutoService +import dev.jorel.commandapi.ExecutableCommand +import dev.slne.surf.api.core.util.checkInstantiationByServiceLoader +import dev.slne.surf.api.core.util.logger +import dev.slne.surf.api.paper.command.executors.CoroutineScopeProvider +import dev.slne.surf.api.paper.command.requirement.SuspendRequirementService +import dev.slne.surf.api.paper.nms.NmsUseWithCaution +import dev.slne.surf.api.paper.nms.common.CommandSendPacketBlockerListener +import dev.slne.surf.api.paper.nms.common.NmsProvider +import dev.slne.surf.api.paper.server.plugin +import io.papermc.paper.command.brigadier.CommandSourceStack +import kotlinx.coroutines.* +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import java.util.* +import java.util.concurrent.ConcurrentHashMap +import kotlin.coroutines.cancellation.CancellationException + +@AutoService(SuspendRequirementService::class) +class SuspendRequirementServiceImpl : SuspendRequirementService { + init { + checkInstantiationByServiceLoader() + } + + companion object { + private val log = logger() + + fun get() = SuspendRequirementService.instance as SuspendRequirementServiceImpl + } + + private val requirements = Caffeine.newBuilder() + .build() + + private val blockedCommandPackets = ConcurrentHashMap.newKeySet() + private val ready = ConcurrentHashMap.newKeySet() + + private val listener = EventListener() + + @OptIn(NmsUseWithCaution::class) + fun createCommandSendPacketBlockerListener(): CommandSendPacketBlockerListener { + return NmsProvider.current.createCommandSendPacketBlockerListener(blockedCommandPackets) + } + + fun getEventListener(): Listener { + return listener + } + + override fun > ExecutableCommand.withSuspendRequirement( + scope: CoroutineScopeProvider, + requirement: suspend CoroutineScope.(Player) -> Boolean, + allowIfNonPlayer: Boolean + ) { + val id = UUID.randomUUID() + register(id, scope, requirement) + withRequirement { + if (it !is Player) { + allowIfNonPlayer + } else { + testCached(id, it) + } + } + } + + private fun register( + id: UUID, + scope: CoroutineScopeProvider, + requirement: suspend CoroutineScope.(Player) -> Boolean + ) { + requirements.put(id, Requirement(scope, requirement)) + } + + private fun testCached(id: UUID, sender: Player): Boolean { + val requirement = requirements.getIfPresent(id) ?: return false + return requirement.testCached(sender) + } + + suspend fun refreshForSender(sender: Player) { + coroutineScope { + for (requirement in requirements.asMap().values) { + launch { + requirement.refreshForSender(sender) + } + } + } + + ready.add(sender.uniqueId) + sender.updateCommands() + } + + fun triggerRefreshForSender(sender: Player) { + plugin.launch { + refreshForSender(sender) + } + } + + inner class EventListener : Listener { + + @EventHandler + @Suppress("UnstableApiUsage") + fun onAsyncPlayerSendCommand(event: AsyncPlayerSendCommandsEvent) { + if (requirements.asMap().isEmpty()) return + + if (event.isAsynchronous || !event.hasFiredAsync()) { + val uuid = event.player.uniqueId + + if (ready.remove(uuid)) { + blockedCommandPackets.remove(uuid) + triggerRefreshForSender(event.player) + } else { + blockedCommandPackets.add(uuid) + } + } + } + + @EventHandler + fun onConnectionClosed(event: PlayerConnectionCloseEvent) { + blockedCommandPackets.remove(event.playerUniqueId) + ready.remove(event.playerUniqueId) + } + } + + data class Requirement( + val scope: CoroutineScopeProvider, + val requirement: suspend CoroutineScope.(Player) -> Boolean + ) { + private val cached = Caffeine.newBuilder() + .weakKeys() + .build() + + fun testCached(sender: Player): Boolean { + return cached.getIfPresent(sender.uniqueId) ?: false + } + + suspend fun refreshForSender(sender: Player) { + withContext(scope().coroutineContext.minusKey(Job)) { + try { + cached.put(sender.uniqueId, requirement(sender)) + } catch (e: Throwable) { + if (e is CancellationException) throw e + log.atWarning() + .withCause(e) + .log("Failed to check requirement for sender $sender! Not updating cache.") + } + } + } + } +} \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/listener/ListenerManager.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/listener/ListenerManager.kt index 1680262f..fadc9f6d 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/listener/ListenerManager.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/listener/ListenerManager.kt @@ -1,6 +1,7 @@ package dev.slne.surf.api.paper.server.listener import dev.slne.surf.api.paper.event.register +import dev.slne.surf.api.paper.server.command.SuspendRequirementServiceImpl import dev.slne.surf.api.paper.server.impl.glow.GlowingListener import dev.slne.surf.api.paper.server.impl.pdc.block.BlockDataListener import dev.slne.surf.api.paper.server.impl.visualizer.visualizer.VisualizerListener @@ -17,6 +18,8 @@ object ListenerManager { GlowingListener.register() BlockDataListener.register() + + SuspendRequirementServiceImpl.get().getEventListener().register() } /** diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt index 85569bfd..977470f3 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt @@ -9,6 +9,7 @@ import dev.slne.surf.api.paper.nms.common.AbstractChannelInjector import dev.slne.surf.api.paper.nms.common.NmsProvider import dev.slne.surf.api.paper.packet.listener.SurfPaperPacketListenerApi import dev.slne.surf.api.paper.packet.listener.listener.PacketListener +import dev.slne.surf.api.paper.server.command.SuspendRequirementServiceImpl import dev.slne.surf.api.paper.server.packet.lore.PluginDisablePacketLoreListener import dev.slne.surf.api.paper.server.plugin import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder @@ -35,6 +36,8 @@ object PacketApiLoader { SurfPaperPacketListenerApi.registerListeners(listener) } + SurfPaperPacketListenerApi.registerListeners(SuspendRequirementServiceImpl.get().createCommandSendPacketBlockerListener()) + AbstractChannelInjector.instance.register() PluginDisablePacketLoreListener.register() } diff --git a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt new file mode 100644 index 00000000..15bb1425 --- /dev/null +++ b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt @@ -0,0 +1,18 @@ +package dev.slne.surf.api.paper.command.requirement + +import dev.jorel.commandapi.ExecutableCommand +import dev.slne.surf.api.paper.command.executors.CoroutineScopeProvider +import dev.slne.surf.api.paper.command.executors.extractCallingPluginScopeOrThrow +import kotlinx.coroutines.CoroutineScope +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +fun > ExecutableCommand.withSuspendPlayerRequirement( + scope: CoroutineScopeProvider = extractCallingPluginScopeOrThrow(), + requirement: suspend CoroutineScope.(Player) -> Boolean, + allowIfNonPlayer: Boolean = false +) { + SuspendRequirementService.instance.apply { + withSuspendRequirement(scope, requirement, allowIfNonPlayer) + } +} \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendRequirementService.kt b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendRequirementService.kt new file mode 100644 index 00000000..212c6479 --- /dev/null +++ b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendRequirementService.kt @@ -0,0 +1,23 @@ +package dev.slne.surf.api.paper.command.requirement + +import dev.jorel.commandapi.ExecutableCommand +import dev.slne.surf.api.core.util.requiredService +import dev.slne.surf.api.paper.command.executors.CoroutineScopeProvider +import dev.slne.surf.api.shared.api.util.InternalSurfApi +import kotlinx.coroutines.CoroutineScope +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +@InternalSurfApi +interface SuspendRequirementService { + + fun > ExecutableCommand.withSuspendRequirement( + scope: CoroutineScopeProvider, + requirement: suspend CoroutineScope.(Player) -> Boolean, + allowIfNonPlayer: Boolean + ) + + companion object { + val instance = requiredService() + } +} \ No newline at end of file From 430c1047c38a904b07d2ad4322da4b94af08ff58 Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 11:46:16 +0200 Subject: [PATCH 02/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(service):?= =?UTF-8?q?=20optimize=20command=20packet=20handling=20in=20suspend=20requ?= =?UTF-8?q?irement=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ensure triggerRefreshForSender is called consistently for both adding and removing UUIDs --- .../api/paper/server/command/SuspendRequirementServiceImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt index e9bc9aa1..d6c46ca3 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt @@ -113,9 +113,9 @@ class SuspendRequirementServiceImpl : SuspendRequirementService { if (ready.remove(uuid)) { blockedCommandPackets.remove(uuid) - triggerRefreshForSender(event.player) } else { blockedCommandPackets.add(uuid) + triggerRefreshForSender(event.player) } } } From 2b4e48c8b231d2325a12d32e7d00af59fb26b4fb Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 11:46:43 +0200 Subject: [PATCH 03/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(service):?= =?UTF-8?q?=20optimize=20blocked=20command=20packet=20handling=20in=20susp?= =?UTF-8?q?end=20requirement=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - streamline logic for adding and removing blocked command packets - trigger refresh for sender only when a new packet is added --- .../paper/server/command/SuspendRequirementServiceImpl.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt index d6c46ca3..5b62c85b 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt @@ -114,8 +114,9 @@ class SuspendRequirementServiceImpl : SuspendRequirementService { if (ready.remove(uuid)) { blockedCommandPackets.remove(uuid) } else { - blockedCommandPackets.add(uuid) - triggerRefreshForSender(event.player) + if (blockedCommandPackets.add(uuid)) { + triggerRefreshForSender(event.player) + } } } } From e5da4476018f63735320ad56078a35fb59322230 Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 12:04:58 +0200 Subject: [PATCH 04/13] =?UTF-8?q?=E2=9C=A8=20feat(command):=20add=20suspen?= =?UTF-8?q?d=20requirement=20test=20command=20and=20enhance=20packet=20han?= =?UTF-8?q?dling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - implement SuspendRequirementTestCommand for testing suspend command requirements - modify CommandSendPacketBlockerListener to manage received command packets - update SuspendRequirementServiceImpl to provide command send packet blocker listener --- .../CommandSendPacketBlockerListener.kt | 9 +++- ..._11CommandSendPacketBlockerListenerImpl.kt | 34 +++++++++++++- ...6_1CommandSendPacketBlockerListenerImpl.kt | 34 +++++++++++++- .../test/command/SurfApiTestCommand.java | 3 +- .../SuspendRequirementTestCommand.kt | 45 +++++++++++++++++++ .../command/SuspendRequirementServiceImpl.kt | 8 +++- .../paper/server/packet/PacketApiLoader.kt | 2 +- .../requirement/SuspendCommandRequirements.kt | 4 +- 8 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/SuspendRequirementTestCommand.kt diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt index 0d09dd19..62d2cde3 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt @@ -3,6 +3,13 @@ package dev.slne.surf.api.paper.nms.common import dev.slne.surf.api.paper.nms.NmsUseWithCaution import dev.slne.surf.api.paper.packet.listener.listener.PacketListener import java.util.* +import java.util.concurrent.ConcurrentHashMap @OptIn(NmsUseWithCaution::class) -abstract class CommandSendPacketBlockerListener(protected val blockedPlayer: Set) : PacketListener \ No newline at end of file +abstract class CommandSendPacketBlockerListener(protected val blockedPlayer: Set) : PacketListener { + protected val receivedCommandPacket: MutableSet = ConcurrentHashMap.newKeySet() + + fun removeReceivedCommandPacket(uuid: UUID) { + receivedCommandPacket.remove(uuid) + } +} \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt index 7211f1c9..6ca1f9fa 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt @@ -1,9 +1,15 @@ package dev.slne.surf.api.paper.server.nms.v1_21_11.packet.listener +import com.mojang.brigadier.CommandDispatcher +import com.mojang.brigadier.builder.LiteralArgumentBuilder +import com.mojang.brigadier.tree.ArgumentCommandNode +import com.mojang.brigadier.tree.CommandNode import dev.slne.surf.api.paper.nms.NmsUseWithCaution import dev.slne.surf.api.paper.nms.common.CommandSendPacketBlockerListener import dev.slne.surf.api.paper.packet.listener.listener.annotation.ClientboundListener +import net.minecraft.commands.CommandSourceStack import net.minecraft.network.protocol.game.ClientboundCommandsPacket +import net.minecraft.resources.Identifier import net.minecraft.server.level.ServerPlayer import java.util.* @@ -12,12 +18,38 @@ import java.util.* class V1_21_11CommandSendPacketBlockerListenerImpl(blockedPlayer: Set) : CommandSendPacketBlockerListener(blockedPlayer) { + private val loadingCommandsDispatcher = CommandDispatcher() + private val commandNodeInspector = object : ClientboundCommandsPacket.NodeInspector { + override fun suggestionId(p0: ArgumentCommandNode): Identifier? { + return null + } + + override fun isExecutable(p0: CommandNode): Boolean { + return false + } + + override fun isRestricted(p0: CommandNode): Boolean { + return false + } + } + + init { + loadingCommandsDispatcher.register(LiteralArgumentBuilder.literal("commands-are-loading")) + } + @ClientboundListener fun onClientboundCommandsPacket( packet: ClientboundCommandsPacket, player: ServerPlayer ): ClientboundCommandsPacket? { - if (blockedPlayer.contains(player.uuid)) return null + if (blockedPlayer.contains(player.uuid)) { + return if (receivedCommandPacket.add(player.uuid)) { + ClientboundCommandsPacket(loadingCommandsDispatcher.root, commandNodeInspector) + } else { + null + } + } + return packet } } \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt index 0d4b0a1a..7b898039 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt @@ -1,9 +1,15 @@ package dev.slne.surf.api.paper.server.nms.v26_1.packet.listener +import com.mojang.brigadier.CommandDispatcher +import com.mojang.brigadier.builder.LiteralArgumentBuilder +import com.mojang.brigadier.tree.ArgumentCommandNode +import com.mojang.brigadier.tree.CommandNode import dev.slne.surf.api.paper.nms.NmsUseWithCaution import dev.slne.surf.api.paper.nms.common.CommandSendPacketBlockerListener import dev.slne.surf.api.paper.packet.listener.listener.annotation.ClientboundListener +import net.minecraft.commands.CommandSourceStack import net.minecraft.network.protocol.game.ClientboundCommandsPacket +import net.minecraft.resources.Identifier import net.minecraft.server.level.ServerPlayer import java.util.* @@ -13,12 +19,38 @@ import java.util.* class V26_1CommandSendPacketBlockerListenerImpl(blockedPlayer: Set) : CommandSendPacketBlockerListener(blockedPlayer) { + private val loadingCommandsDispatcher = CommandDispatcher() + private val commandNodeInspector = object : ClientboundCommandsPacket.NodeInspector { + override fun suggestionId(p0: ArgumentCommandNode): Identifier? { + return null + } + + override fun isExecutable(p0: CommandNode): Boolean { + return false + } + + override fun isRestricted(p0: CommandNode): Boolean { + return false + } + } + + init { + loadingCommandsDispatcher.register(LiteralArgumentBuilder.literal("commands-are-loading")) + } + @ClientboundListener fun onClientboundCommandsPacket( packet: ClientboundCommandsPacket, player: ServerPlayer ): ClientboundCommandsPacket? { - if (blockedPlayer.contains(player.uuid)) return null + if (blockedPlayer.contains(player.uuid)) { + return if (receivedCommandPacket.add(player.uuid)) { + ClientboundCommandsPacket(loadingCommandsDispatcher.root, commandNodeInspector) + } else { + null + } + } + return packet } } \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-plugin-test/src/main/java/dev/slne/surf/api/paper/test/command/SurfApiTestCommand.java b/surf-api-paper/surf-api-paper-plugin-test/src/main/java/dev/slne/surf/api/paper/test/command/SurfApiTestCommand.java index c4dcc920..b454e4da 100644 --- a/surf-api-paper/surf-api-paper-plugin-test/src/main/java/dev/slne/surf/api/paper/test/command/SurfApiTestCommand.java +++ b/surf-api-paper/surf-api-paper-plugin-test/src/main/java/dev/slne/surf/api/paper/test/command/SurfApiTestCommand.java @@ -33,7 +33,8 @@ public SurfApiTestCommand() { new SignedMessageArgumentTest("signedmessage"), new BlockPdcContainerTest("blockpdc"), new OfflineInventoryEditTest("editOfflineInventory"), - new ModernSerializerTestConfigCommand("modernSerializerTestConfig") + new ModernSerializerTestConfigCommand("modernSerializerTestConfig"), + new SuspendRequirementTestCommand("suspendRequirement") ); } } diff --git a/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/SuspendRequirementTestCommand.kt b/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/SuspendRequirementTestCommand.kt new file mode 100644 index 00000000..babe8c6c --- /dev/null +++ b/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/SuspendRequirementTestCommand.kt @@ -0,0 +1,45 @@ +package dev.slne.surf.surfapi.bukkit.test.command.subcommands + +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.kotlindsl.* +import dev.slne.surf.api.paper.command.requirement.withSuspendPlayerRequirement +import kotlinx.coroutines.delay +import java.util.* +import java.util.concurrent.ConcurrentHashMap +import kotlin.time.Duration.Companion.seconds + +class SuspendRequirementTestCommand(name: String) : CommandAPICommand(name) { + private val shown = ConcurrentHashMap.newKeySet() + + init { + subcommand("updateCommands") { + playerExecutor { player, _ -> + player.updateCommands() + } + } + + subcommand("showCommand") { + booleanArgument("show") + + playerExecutor { player, arguments -> + val show: Boolean by arguments + if (show) { + shown.add(player.uniqueId) + } else { + shown.remove(player.uniqueId) + } + } + } + + subcommand("conditionalCommand") { + withSuspendPlayerRequirement { player -> + delay(10.seconds) + player.uniqueId in shown + } + + anyExecutor { sender, _ -> + sender.sendMessage("This command is only available to players who have shown it") + } + } + } +} \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt index 5b62c85b..f8024adf 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt @@ -45,8 +45,11 @@ class SuspendRequirementServiceImpl : SuspendRequirementService { private val listener = EventListener() @OptIn(NmsUseWithCaution::class) - fun createCommandSendPacketBlockerListener(): CommandSendPacketBlockerListener { - return NmsProvider.current.createCommandSendPacketBlockerListener(blockedCommandPackets) + private val commandSendPacketBlockerListener = + NmsProvider.current.createCommandSendPacketBlockerListener(blockedCommandPackets) + + fun getCommandSendPacketBlockerListener(): CommandSendPacketBlockerListener { + return commandSendPacketBlockerListener } fun getEventListener(): Listener { @@ -125,6 +128,7 @@ class SuspendRequirementServiceImpl : SuspendRequirementService { fun onConnectionClosed(event: PlayerConnectionCloseEvent) { blockedCommandPackets.remove(event.playerUniqueId) ready.remove(event.playerUniqueId) + commandSendPacketBlockerListener.removeReceivedCommandPacket(event.playerUniqueId) } } diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt index 977470f3..48a75f8f 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt @@ -36,7 +36,7 @@ object PacketApiLoader { SurfPaperPacketListenerApi.registerListeners(listener) } - SurfPaperPacketListenerApi.registerListeners(SuspendRequirementServiceImpl.get().createCommandSendPacketBlockerListener()) + SurfPaperPacketListenerApi.registerListeners(SuspendRequirementServiceImpl.get().getCommandSendPacketBlockerListener()) AbstractChannelInjector.instance.register() PluginDisablePacketLoreListener.register() diff --git a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt index 15bb1425..25374c98 100644 --- a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt +++ b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt @@ -9,8 +9,8 @@ import org.bukkit.entity.Player fun > ExecutableCommand.withSuspendPlayerRequirement( scope: CoroutineScopeProvider = extractCallingPluginScopeOrThrow(), - requirement: suspend CoroutineScope.(Player) -> Boolean, - allowIfNonPlayer: Boolean = false + allowIfNonPlayer: Boolean = false, + requirement: suspend CoroutineScope.(Player) -> Boolean ) { SuspendRequirementService.instance.apply { withSuspendRequirement(scope, requirement, allowIfNonPlayer) From 69860e8327ade50a648a7cf4cb974626f1ef8d63 Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 12:08:57 +0200 Subject: [PATCH 05/13] =?UTF-8?q?=F0=9F=94=A7=20chore:=20update=20version?= =?UTF-8?q?=20to=203.16.0=20in=20gradle.properties?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index f92e3683..deeda5d4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled javaVersion=25 mcVersion=26.1.2 group=dev.slne.surf.api -version=3.15.0 +version=3.16.0 relocationPrefix=dev.slne.surf.api.libs snapshot=false From 334f12a59cb177e35de5e0f6b4267411b915a67d Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 12:15:14 +0200 Subject: [PATCH 06/13] =?UTF-8?q?=E2=9C=A8=20feat(command):=20add=20suspen?= =?UTF-8?q?dable=20player-only=20requirement=20for=20commands?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - implement withSuspendPlayerRequirement function to refresh command visibility - cache requirement results for improved performance during command execution --- .../requirement/SuspendCommandRequirements.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt index 25374c98..6bae2043 100644 --- a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt +++ b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt @@ -7,6 +7,29 @@ import kotlinx.coroutines.CoroutineScope import org.bukkit.command.CommandSender import org.bukkit.entity.Player +/** + * Adds a suspendable player-only requirement to this command. + * + * The suspendable [requirement] is refreshed asynchronously when the command tree is sent + * to a player. Its result is cached and used by the underlying CommandAPI requirement. + * + * This means the cached result is still checked when the command is executed, but the + * suspendable requirement itself is not refreshed at execution time. The command may + * therefore be executed based on the last cached value until the command tree is sent + * again and the cache is updated. + * + * The cached result can be refreshed manually by calling [Player.updateCommands], which + * causes the command tree to be resent to the player. + * + * Because of that, this should mainly be used for command visibility or other visual + * command-list behavior. Any condition that must be enforced with up-to-date data during + * execution should also be checked explicitly inside the command executor. + * + * @param scope the coroutine scope provider used to refresh the requirement + * @param allowIfNonPlayer whether non-player command senders should be allowed + * @param requirement the suspendable predicate used to refresh the cached requirement + * result for a player + */ fun > ExecutableCommand.withSuspendPlayerRequirement( scope: CoroutineScopeProvider = extractCallingPluginScopeOrThrow(), allowIfNonPlayer: Boolean = false, From 62983f35f2afc876d03ae649a2de92e4b2fae871 Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 12:15:46 +0200 Subject: [PATCH 07/13] =?UTF-8?q?=F0=9F=94=A7=20chore(abi):=20update=20api?= =?UTF-8?q?=20dump?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../surf-api-paper/api/surf-api-paper.api | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/surf-api-paper/surf-api-paper/api/surf-api-paper.api b/surf-api-paper/surf-api-paper/api/surf-api-paper.api index dafefb6e..49296999 100644 --- a/surf-api-paper/surf-api-paper/api/surf-api-paper.api +++ b/surf-api-paper/surf-api-paper/api/surf-api-paper.api @@ -227,6 +227,20 @@ public final class dev/slne/surf/api/paper/command/executors/SuspendCommandExecu public static final fun sendSyntaxMessageOrRethrow (Lorg/bukkit/command/CommandSender;Ljava/lang/String;Ljava/lang/Throwable;)V } +public final class dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirementsKt { + public static final fun withSuspendPlayerRequirement (Ldev/jorel/commandapi/ExecutableCommand;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function3;)V + public static synthetic fun withSuspendPlayerRequirement$default (Ldev/jorel/commandapi/ExecutableCommand;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function3;ILjava/lang/Object;)V +} + +public abstract interface class dev/slne/surf/api/paper/command/requirement/SuspendRequirementService { + public static final field Companion Ldev/slne/surf/api/paper/command/requirement/SuspendRequirementService$Companion; + public abstract fun withSuspendRequirement (Ldev/jorel/commandapi/ExecutableCommand;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;Z)V +} + +public final class dev/slne/surf/api/paper/command/requirement/SuspendRequirementService$Companion { + public final fun getInstance ()Ldev/slne/surf/api/paper/command/requirement/SuspendRequirementService; +} + public final class dev/slne/surf/api/paper/command/util/AsyncPlayerProfileArgumentUtilsKt { public static final fun awaitAsyncPlayerProfile (Ldev/jorel/commandapi/executors/CommandArguments;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun awaitAsyncPlayerProfileOptional (Ldev/jorel/commandapi/executors/CommandArguments;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; From 0a8ee260793351d7709b69c62ee135f4ba64cebf Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 12:18:29 +0200 Subject: [PATCH 08/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(service):?= =?UTF-8?q?=20enhance=20command=20packet=20handling=20with=20invalidation?= =?UTF-8?q?=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add invalidate method to clear cached requirements for players - invoke invalidate on player removal from blocked command packets --- .../paper/server/command/SuspendRequirementServiceImpl.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt index f8024adf..94333485 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt @@ -129,6 +129,7 @@ class SuspendRequirementServiceImpl : SuspendRequirementService { blockedCommandPackets.remove(event.playerUniqueId) ready.remove(event.playerUniqueId) commandSendPacketBlockerListener.removeReceivedCommandPacket(event.playerUniqueId) + requirements.asMap().values.forEach { it.invalidate(event.playerUniqueId) } } } @@ -137,13 +138,16 @@ class SuspendRequirementServiceImpl : SuspendRequirementService { val requirement: suspend CoroutineScope.(Player) -> Boolean ) { private val cached = Caffeine.newBuilder() - .weakKeys() .build() fun testCached(sender: Player): Boolean { return cached.getIfPresent(sender.uniqueId) ?: false } + fun invalidate(uuid: UUID) { + cached.invalidate(uuid) + } + suspend fun refreshForSender(sender: Player) { withContext(scope().coroutineContext.minusKey(Job)) { try { From b467da55e0fe63677f720ad38394910eaeff6bab Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 12:22:13 +0200 Subject: [PATCH 09/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(command):?= =?UTF-8?q?=20rename=20blockedPlayer=20to=20blockedPlayers=20for=20consist?= =?UTF-8?q?ency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update variable name in CommandSendPacketBlockerListener and its implementations - ensure consistent naming across related classes and methods --- .../paper/nms/common/CommandSendPacketBlockerListener.kt | 4 ++-- .../dev/slne/surf/api/paper/nms/common/NmsProvider.kt | 2 +- .../api/paper/server/nms/v1_21_11/V1_21_11NmsProvider.kt | 4 ++-- .../V1_21_11CommandSendPacketBlockerListenerImpl.kt | 6 +++--- .../surf/api/paper/server/nms/v26_1/V26_1NmsProvider.kt | 4 ++-- .../listener/V26_1CommandSendPacketBlockerListenerImpl.kt | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt index 62d2cde3..1d4c99d6 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt @@ -6,8 +6,8 @@ import java.util.* import java.util.concurrent.ConcurrentHashMap @OptIn(NmsUseWithCaution::class) -abstract class CommandSendPacketBlockerListener(protected val blockedPlayer: Set) : PacketListener { - protected val receivedCommandPacket: MutableSet = ConcurrentHashMap.newKeySet() +abstract class CommandSendPacketBlockerListener(protected val blockedPlayers: Set) : PacketListener { + protected val receivedCommandPacket: MutableSet = ConcurrentHashMap.newKeySet() fun removeReceivedCommandPacket(uuid: UUID) { receivedCommandPacket.remove(uuid) diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/NmsProvider.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/NmsProvider.kt index 0a8d7133..639dbd8f 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/NmsProvider.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/NmsProvider.kt @@ -85,7 +85,7 @@ interface NmsProvider { fun createChannelInjector(): AbstractChannelInjector<*> fun createPacketListenerApi(): InternalPacketListenerApiBridge - fun createCommandSendPacketBlockerListener(blockedPlayer: Set): CommandSendPacketBlockerListener + fun createCommandSendPacketBlockerListener(blockedPlayers: Set): CommandSendPacketBlockerListener /** * Creates version-specific packet listeners (e.g. lore handler, glowing handler). diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/V1_21_11NmsProvider.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/V1_21_11NmsProvider.kt index 7610ecb7..d3ce9753 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/V1_21_11NmsProvider.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/V1_21_11NmsProvider.kt @@ -91,8 +91,8 @@ class V1_21_11NmsProvider(override val plugin: JavaPlugin) : NmsProvider { override fun createChannelInjector(): AbstractChannelInjector<*> = V1_21_11ChannelInjector override fun createPacketListenerApi(): InternalPacketListenerApiBridge = V1_21_11PacketListenerApiImpl() - override fun createCommandSendPacketBlockerListener(blockedPlayer: Set): CommandSendPacketBlockerListener { - return V1_21_11CommandSendPacketBlockerListenerImpl(blockedPlayer) + override fun createCommandSendPacketBlockerListener(blockedPlayers: Set): CommandSendPacketBlockerListener { + return V1_21_11CommandSendPacketBlockerListenerImpl(blockedPlayers) } override fun createPacketListeners(): List = listOf( diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt index 6ca1f9fa..8de61e64 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt @@ -15,8 +15,8 @@ import java.util.* @OptIn(NmsUseWithCaution::class) @Suppress("ClassName") -class V1_21_11CommandSendPacketBlockerListenerImpl(blockedPlayer: Set) : - CommandSendPacketBlockerListener(blockedPlayer) { +class V1_21_11CommandSendPacketBlockerListenerImpl(blockedPlayers: Set) : + CommandSendPacketBlockerListener(blockedPlayers) { private val loadingCommandsDispatcher = CommandDispatcher() private val commandNodeInspector = object : ClientboundCommandsPacket.NodeInspector { @@ -42,7 +42,7 @@ class V1_21_11CommandSendPacketBlockerListenerImpl(blockedPlayer: Set) : packet: ClientboundCommandsPacket, player: ServerPlayer ): ClientboundCommandsPacket? { - if (blockedPlayer.contains(player.uuid)) { + if (blockedPlayers.contains(player.uuid)) { return if (receivedCommandPacket.add(player.uuid)) { ClientboundCommandsPacket(loadingCommandsDispatcher.root, commandNodeInspector) } else { diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/V26_1NmsProvider.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/V26_1NmsProvider.kt index 9199c7a9..357e99dd 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/V26_1NmsProvider.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/V26_1NmsProvider.kt @@ -70,8 +70,8 @@ class V26_1NmsProvider(override val plugin: JavaPlugin) : NmsProvider { override fun createChannelInjector(): AbstractChannelInjector<*> = V26_1ChannelInjector override fun createPacketListenerApi(): InternalPacketListenerApiBridge = V26_1PacketListenerApiImpl() - override fun createCommandSendPacketBlockerListener(blockedPlayer: Set): CommandSendPacketBlockerListener { - return V26_1CommandSendPacketBlockerListenerImpl(blockedPlayer) + override fun createCommandSendPacketBlockerListener(blockedPlayers: Set): CommandSendPacketBlockerListener { + return V26_1CommandSendPacketBlockerListenerImpl(blockedPlayers) } override fun createPacketListeners(): List = listOf( diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt index 7b898039..904e0199 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt @@ -16,8 +16,8 @@ import java.util.* @Suppress("ClassName") @OptIn(NmsUseWithCaution::class) -class V26_1CommandSendPacketBlockerListenerImpl(blockedPlayer: Set) : - CommandSendPacketBlockerListener(blockedPlayer) { +class V26_1CommandSendPacketBlockerListenerImpl(blockedPlayers: Set) : + CommandSendPacketBlockerListener(blockedPlayers) { private val loadingCommandsDispatcher = CommandDispatcher() private val commandNodeInspector = object : ClientboundCommandsPacket.NodeInspector { @@ -43,7 +43,7 @@ class V26_1CommandSendPacketBlockerListenerImpl(blockedPlayer: Set) : packet: ClientboundCommandsPacket, player: ServerPlayer ): ClientboundCommandsPacket? { - if (blockedPlayer.contains(player.uuid)) { + if (blockedPlayers.contains(player.uuid)) { return if (receivedCommandPacket.add(player.uuid)) { ClientboundCommandsPacket(loadingCommandsDispatcher.root, commandNodeInspector) } else { From f1cf3921865223acc285462b426f0c45c70799ac Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 12:23:09 +0200 Subject: [PATCH 10/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(command):?= =?UTF-8?q?=20rename=20receivedCommandPacket=20to=20receivedFirstCommandPa?= =?UTF-8?q?cket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update variable name for clarity in CommandSendPacketBlockerListener - adjust method names accordingly in SuspendRequirementServiceImpl - ensure consistency in command packet handling across implementations --- .../paper/nms/common/CommandSendPacketBlockerListener.kt | 6 +++--- .../V1_21_11CommandSendPacketBlockerListenerImpl.kt | 2 +- .../listener/V26_1CommandSendPacketBlockerListenerImpl.kt | 2 +- .../paper/server/command/SuspendRequirementServiceImpl.kt | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt index 1d4c99d6..c1bab758 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-common/src/main/kotlin/dev/slne/surf/api/paper/nms/common/CommandSendPacketBlockerListener.kt @@ -7,9 +7,9 @@ import java.util.concurrent.ConcurrentHashMap @OptIn(NmsUseWithCaution::class) abstract class CommandSendPacketBlockerListener(protected val blockedPlayers: Set) : PacketListener { - protected val receivedCommandPacket: MutableSet = ConcurrentHashMap.newKeySet() + protected val receivedFirstCommandPacket: MutableSet = ConcurrentHashMap.newKeySet() - fun removeReceivedCommandPacket(uuid: UUID) { - receivedCommandPacket.remove(uuid) + fun removeReceivedFirstCommandPacket(uuid: UUID) { + receivedFirstCommandPacket.remove(uuid) } } \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt index 8de61e64..04f0587e 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/packet/listener/V1_21_11CommandSendPacketBlockerListenerImpl.kt @@ -43,7 +43,7 @@ class V1_21_11CommandSendPacketBlockerListenerImpl(blockedPlayers: Set) : player: ServerPlayer ): ClientboundCommandsPacket? { if (blockedPlayers.contains(player.uuid)) { - return if (receivedCommandPacket.add(player.uuid)) { + return if (receivedFirstCommandPacket.add(player.uuid)) { ClientboundCommandsPacket(loadingCommandsDispatcher.root, commandNodeInspector) } else { null diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt index 904e0199..085b5105 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/packet/listener/V26_1CommandSendPacketBlockerListenerImpl.kt @@ -44,7 +44,7 @@ class V26_1CommandSendPacketBlockerListenerImpl(blockedPlayers: Set) : player: ServerPlayer ): ClientboundCommandsPacket? { if (blockedPlayers.contains(player.uuid)) { - return if (receivedCommandPacket.add(player.uuid)) { + return if (receivedFirstCommandPacket.add(player.uuid)) { ClientboundCommandsPacket(loadingCommandsDispatcher.root, commandNodeInspector) } else { null diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt index 94333485..92a12af6 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt @@ -128,7 +128,7 @@ class SuspendRequirementServiceImpl : SuspendRequirementService { fun onConnectionClosed(event: PlayerConnectionCloseEvent) { blockedCommandPackets.remove(event.playerUniqueId) ready.remove(event.playerUniqueId) - commandSendPacketBlockerListener.removeReceivedCommandPacket(event.playerUniqueId) + commandSendPacketBlockerListener.removeReceivedFirstCommandPacket(event.playerUniqueId) requirements.asMap().values.forEach { it.invalidate(event.playerUniqueId) } } } From 0e316259efbaf06d5dcac10245e4c32b6694b5b1 Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 12:29:18 +0200 Subject: [PATCH 11/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(command):?= =?UTF-8?q?=20clarify=20non-player=20command=20sender=20requirement=20desc?= =?UTF-8?q?ription?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update parameter description for allowIfNonPlayer to specify it bypasses player-only requirement --- .../api/paper/command/requirement/SuspendCommandRequirements.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt index 6bae2043..0fc4d55d 100644 --- a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt +++ b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/command/requirement/SuspendCommandRequirements.kt @@ -26,7 +26,7 @@ import org.bukkit.entity.Player * execution should also be checked explicitly inside the command executor. * * @param scope the coroutine scope provider used to refresh the requirement - * @param allowIfNonPlayer whether non-player command senders should be allowed + * @param allowIfNonPlayer whether non-player command senders should bypass this player-only requirement * @param requirement the suspendable predicate used to refresh the cached requirement * result for a player */ From a2e2a4e79142281fcd2c174019f28974f8d354d3 Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 14:24:26 +0200 Subject: [PATCH 12/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(service):?= =?UTF-8?q?=20unregister=20command=20send=20packet=20blocker=20listener?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - remove registration of command send packet blocker listener in PacketApiLoader --- .../dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt index 48a75f8f..bd8ac416 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/packet/PacketApiLoader.kt @@ -52,6 +52,8 @@ object PacketApiLoader { } versionPacketListeners = emptyList() + SurfPaperPacketListenerApi.unregisterListeners(SuspendRequirementServiceImpl.get().getCommandSendPacketBlockerListener()) + PluginDisablePacketLoreListener.unregister() AbstractChannelInjector.instance.unregister() provider.shutdown() From f81d61cb8e721b1dd9cc0ca0c9cf7635d59301d1 Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Wed, 27 May 2026 14:28:18 +0200 Subject: [PATCH 13/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(service):?= =?UTF-8?q?=20improve=20requirement=20caching=20for=20connected=20players?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - check player connection status before caching requirement - store requirement result in cache only if player is connected --- .../paper/server/command/SuspendRequirementServiceImpl.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt index 92a12af6..8a54c366 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/command/SuspendRequirementServiceImpl.kt @@ -151,7 +151,10 @@ class SuspendRequirementServiceImpl : SuspendRequirementService { suspend fun refreshForSender(sender: Player) { withContext(scope().coroutineContext.minusKey(Job)) { try { - cached.put(sender.uniqueId, requirement(sender)) + val isRequirementMet = requirement(sender) + if (sender.isConnected) { // check if this player instance is still connected + cached.put(sender.uniqueId, isRequirementMet) + } } catch (e: Throwable) { if (e is CancellationException) throw e log.atWarning()