From 322e23171c3a83d7cde92d0225429ed0db20ef28 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Sun, 28 Jun 2026 15:52:26 +0200 Subject: [PATCH 01/12] =?UTF-8?q?=E2=9C=A8=20feat(actionbar):=20add=20acti?= =?UTF-8?q?onbar=20service=20for=20sending=20messages=20to=20audience?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - implement sendActionbar function to display messages for a specified duration - create ActionbarService to manage actionbar jobs and cancellation - add StableActionbarTestCommand for testing actionbar functionality - update CoreInstance to cancel all actionbars on disable - bump version to 3.31.0 in gradle.properties --- gradle.properties | 2 +- .../slne/surf/api/core/server/CoreInstance.kt | 2 + .../api/core/actionbar/ActionbarService.kt | 66 ++++++++++++++ .../surf/api/core/actionbar/actionbar-util.kt | 27 ++++++ .../test/command/SurfApiTestCommand.java | 85 ++++++++++++------- .../subcommands/StableActionbarTestCommand.kt | 22 +++++ 6 files changed, 172 insertions(+), 32 deletions(-) create mode 100644 surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt create mode 100644 surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt create mode 100644 surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt diff --git a/gradle.properties b/gradle.properties index 45d9a9d4..afbbbe24 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled javaVersion=25 mcVersion=26.2 group=dev.slne.surf.api -version=3.30.0 +version=3.31.0 relocationPrefix=dev.slne.surf.api.libs snapshot=false diff --git a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/CoreInstance.kt b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/CoreInstance.kt index 9d02aa63..ff1ba7d7 100644 --- a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/CoreInstance.kt +++ b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/CoreInstance.kt @@ -1,5 +1,6 @@ package dev.slne.surf.api.core.server +import dev.slne.surf.api.core.actionbar.ActionbarService import dev.slne.surf.api.core.messages.Colors import dev.slne.surf.api.core.server.listener.CoreListenerManager import dev.slne.surf.api.core.server.util.PlayerSkinFetcher @@ -27,6 +28,7 @@ abstract class CoreInstance { @MustBeInvokedByOverriders open suspend fun onDisable() { CoreListenerManager.unregisterListeners() + ActionbarService.cancelAll() } private fun initObjects() { diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt new file mode 100644 index 00000000..baf29449 --- /dev/null +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt @@ -0,0 +1,66 @@ +package dev.slne.surf.api.core.actionbar + +import dev.slne.surf.api.core.messages.adventure.uuid +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import net.kyori.adventure.audience.Audience +import net.kyori.adventure.text.Component +import java.util.* +import java.util.concurrent.ConcurrentHashMap +import kotlin.time.Duration +import kotlin.time.TimeSource + +object ActionbarService { + private val actionbars = ConcurrentHashMap>() + + internal fun sendActionbar( + scope: CoroutineScope, + audience: Audience, + duration: Duration, + interval: Duration, + fadeOut: Boolean, + supplier: () -> Component + ): UUID { + val id = UUID.randomUUID() + val audienceUuid = audience.uuid() + + val job = scope.launch { + try { + val end = if (duration == Duration.INFINITE) { + null + } else { + TimeSource.Monotonic.markNow() + duration + } + + while (end == null || !end.hasPassedNow()) { + audience.sendActionBar(supplier()) + delay(interval) + } + + if (!fadeOut) { + audience.sendActionBar(Component.empty()) + } + } finally { + actionbars[audienceUuid]?.let { jobs -> + jobs.remove(id) + + if (jobs.isEmpty()) { + actionbars.remove(audienceUuid) + } + } + } + } + + actionbars.computeIfAbsent(audienceUuid) { ConcurrentHashMap() }[id] = job + return id + } + + fun cancelAll() { + actionbars.forEach { (_, jobs) -> + jobs.values.forEach { it.cancel() } + } + actionbars.clear() + } +} \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt new file mode 100644 index 00000000..9a56da8a --- /dev/null +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt @@ -0,0 +1,27 @@ +package dev.slne.surf.api.core.actionbar + +import dev.slne.surf.api.core.actionbar.ActionbarService.sendActionbar +import dev.slne.surf.api.core.messages.adventure.uuidOrNull +import dev.slne.surf.api.core.messages.builder.SurfComponentBuilder +import kotlinx.coroutines.CoroutineScope +import net.kyori.adventure.audience.Audience +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds + +/** + * Sends an actionbar to the audience for a specified duration. + * + * The audience must have an accessable UUID (see [uuidOrNull]). If the audience does not have a UUID, an error will be thrown. + * + * @param scope The CoroutineScope in which the actionbar will be sent. + * @param duration The duration for which the actionbar will be displayed. + * @param interval The interval at which the actionbar will be updated. Defaults to 1 second. + * @param text A lambda that builds the text component to be displayed in the actionbar. + */ +fun Audience.sendActionbar( + scope: CoroutineScope, + duration: Duration, + interval: Duration = 1.seconds, + fadeOut: Boolean = false, + text: SurfComponentBuilder.() -> Unit, +) = sendActionbar(scope, this, duration, interval, fadeOut) { SurfComponentBuilder(text) } \ 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 b454e4da..e94c43fe 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 @@ -1,40 +1,63 @@ package dev.slne.surf.api.paper.test.command; import dev.jorel.commandapi.CommandAPICommand; -import dev.slne.surf.api.paper.test.command.subcommands.*; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.*; +import dev.slne.surf.api.paper.test.command.subcommands.CommandExceptionTest; +import dev.slne.surf.api.paper.test.command.subcommands.MaxStacksizeTest; +import dev.slne.surf.api.paper.test.command.subcommands.PacketEntityTest; +import dev.slne.surf.api.paper.test.command.subcommands.PacketLoreTest; +import dev.slne.surf.api.paper.test.command.subcommands.PrefixConfigTest; +import dev.slne.surf.api.paper.test.command.subcommands.ReflectionTest; +import dev.slne.surf.api.paper.test.command.subcommands.ScoreboardTest; +import dev.slne.surf.api.paper.test.command.subcommands.SmoothTimeSkip; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.BlockPdcContainerTest; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.GlowingTest; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.InventoryTest; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.ModernSerializerTestConfigCommand; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.OfflineInventoryEditTest; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.PaginationTest; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.ShowItemCommand; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SignedMessageArgumentTest; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SortInvCommand; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.StableActionbarTestCommand; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SummonCommandTest; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SurfEventHandlerTest; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SuspendCommandExecutionTest; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SuspendRequirementTestCommand; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.ToastTest; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.VisualizerTest; public class SurfApiTestCommand extends CommandAPICommand { - public SurfApiTestCommand() { - super("surfapitest"); + public SurfApiTestCommand() { + super("surfapitest"); - withPermission("surfapitest.use"); + withPermission("surfapitest.use"); - withSubcommands( - new PacketLoreTest("packetlore"), - new ScoreboardTest("scoreboard"), - new SmoothTimeSkip("smoothtimeskip"), - new PacketEntityTest("packetentity"), - new ReflectionTest("reflection"), - new PrefixConfigTest("prefixconfig"), - new CommandExceptionTest("commandexception"), - new MaxStacksizeTest("maxstacksize"), - new VisualizerTest("visualizer"), - new GlowingTest("glowing"), - new PaginationTest("pagination"), - new InventoryTest("inventory"), - new ToastTest("toast"), - new SuspendCommandExecutionTest("suspendCommandExecution"), - new SummonCommandTest("summoncommand"), - new SurfEventHandlerTest("eventhandler"), - new ShowItemCommand("showitem"), - new SortInvCommand("sortInv"), - new SignedMessageArgumentTest("signedmessage"), - new BlockPdcContainerTest("blockpdc"), - new OfflineInventoryEditTest("editOfflineInventory"), - new ModernSerializerTestConfigCommand("modernSerializerTestConfig"), - new SuspendRequirementTestCommand("suspendRequirement") - ); - } + withSubcommands( + new PacketLoreTest("packetlore"), + new ScoreboardTest("scoreboard"), + new SmoothTimeSkip("smoothtimeskip"), + new PacketEntityTest("packetentity"), + new ReflectionTest("reflection"), + new PrefixConfigTest("prefixconfig"), + new CommandExceptionTest("commandexception"), + new MaxStacksizeTest("maxstacksize"), + new VisualizerTest("visualizer"), + new GlowingTest("glowing"), + new PaginationTest("pagination"), + new InventoryTest("inventory"), + new ToastTest("toast"), + new SuspendCommandExecutionTest("suspendCommandExecution"), + new SummonCommandTest("summoncommand"), + new SurfEventHandlerTest("eventhandler"), + new ShowItemCommand("showitem"), + new SortInvCommand("sortInv"), + new StableActionbarTestCommand("stableActionbarTest"), + new SignedMessageArgumentTest("signedmessage"), + new BlockPdcContainerTest("blockpdc"), + new OfflineInventoryEditTest("editOfflineInventory"), + 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/StableActionbarTestCommand.kt b/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt new file mode 100644 index 00000000..f1b3cfdb --- /dev/null +++ b/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt @@ -0,0 +1,22 @@ +package dev.slne.surf.surfapi.bukkit.test.command.subcommands + +import com.github.shynixn.mccoroutine.folia.scope +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.kotlindsl.playerExecutor +import dev.slne.surf.api.core.actionbar.sendActionbar +import dev.slne.surf.api.core.util.dateTimeFormatter +import dev.slne.surf.surfapi.bukkit.test.plugin +import java.time.LocalDateTime +import kotlin.time.Duration.Companion.seconds + +class StableActionbarTestCommand(name: String) : CommandAPICommand(name) { + init { + playerExecutor { sender, _ -> + sender.sendActionbar(plugin.scope, 5.seconds) { + appendInfoPrefix() + info("Actionbar Test: ") + variableValue(LocalDateTime.now().format(dateTimeFormatter)) + } + } + } +} \ No newline at end of file From 06442554a8f553a40b3d34893567591589785592 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Sun, 28 Jun 2026 16:00:57 +0200 Subject: [PATCH 02/12] =?UTF-8?q?=E2=9C=A8=20feat(actionbar):=20add=20opti?= =?UTF-8?q?onal=20callbacks=20for=20actionbar=20display=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - introduce onCount callback for each actionbar sent - add onFinish callback for when actionbar display ends - enhance sendActionbar function to support new parameters --- .../api/core/actionbar/ActionbarService.kt | 8 +++- .../surf/api/core/actionbar/actionbar-util.kt | 43 ++++++++++++++++++- .../subcommands/StableActionbarTestCommand.kt | 5 +-- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt index baf29449..99cf00c8 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt @@ -21,7 +21,9 @@ object ActionbarService { duration: Duration, interval: Duration, fadeOut: Boolean, - supplier: () -> Component + supplier: () -> Component, + onCount: (() -> Unit)? = null, + onFinish: (() -> Unit)? = null ): UUID { val id = UUID.randomUUID() val audienceUuid = audience.uuid() @@ -36,15 +38,19 @@ object ActionbarService { while (end == null || !end.hasPassedNow()) { audience.sendActionBar(supplier()) + onCount?.invoke() delay(interval) } + onFinish?.invoke() + if (!fadeOut) { audience.sendActionBar(Component.empty()) } } finally { actionbars[audienceUuid]?.let { jobs -> jobs.remove(id) + onFinish?.invoke() if (jobs.isEmpty()) { actionbars.remove(audienceUuid) diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt index 9a56da8a..9122926e 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt @@ -1,6 +1,5 @@ package dev.slne.surf.api.core.actionbar -import dev.slne.surf.api.core.actionbar.ActionbarService.sendActionbar import dev.slne.surf.api.core.messages.adventure.uuidOrNull import dev.slne.surf.api.core.messages.builder.SurfComponentBuilder import kotlinx.coroutines.CoroutineScope @@ -17,6 +16,8 @@ import kotlin.time.Duration.Companion.seconds * @param duration The duration for which the actionbar will be displayed. * @param interval The interval at which the actionbar will be updated. Defaults to 1 second. * @param text A lambda that builds the text component to be displayed in the actionbar. + * @param onCount An optional callback that is invoked each time the actionbar is sent. + * @param onFinish An optional callback that is invoked when the actionbar display is finished. */ fun Audience.sendActionbar( scope: CoroutineScope, @@ -24,4 +25,42 @@ fun Audience.sendActionbar( interval: Duration = 1.seconds, fadeOut: Boolean = false, text: SurfComponentBuilder.() -> Unit, -) = sendActionbar(scope, this, duration, interval, fadeOut) { SurfComponentBuilder(text) } \ No newline at end of file + onCount: (() -> Unit)? = null, + onFinish: (() -> Unit)? = null +) = ActionbarService.sendActionbar( + scope, + this, + duration, + interval, + fadeOut, + { SurfComponentBuilder(text) }, + onCount, + onFinish +) + +/** + * Sends an actionbar to the audience for a specified duration. + * + * The audience must have an accessable UUID (see [uuidOrNull]). If the audience does not have a UUID, an error will be thrown. + * + * @param scope The CoroutineScope in which the actionbar will be sent. + * @param duration The duration for which the actionbar will be displayed. + * @param interval The interval at which the actionbar will be updated. Defaults to 1 second. + * @param text A lambda that builds the text component to be displayed in the actionbar. + */ +fun Audience.sendActionbar( + scope: CoroutineScope, + duration: Duration, + interval: Duration = 1.seconds, + fadeOut: Boolean = false, + text: SurfComponentBuilder.() -> Unit +) = ActionbarService.sendActionbar( + scope, + this, + duration, + interval, + fadeOut, + { SurfComponentBuilder(text) }, + null, + null +) \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt b/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt index f1b3cfdb..38de69a0 100644 --- a/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt +++ b/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt @@ -4,7 +4,6 @@ import com.github.shynixn.mccoroutine.folia.scope import dev.jorel.commandapi.CommandAPICommand import dev.jorel.commandapi.kotlindsl.playerExecutor import dev.slne.surf.api.core.actionbar.sendActionbar -import dev.slne.surf.api.core.util.dateTimeFormatter import dev.slne.surf.surfapi.bukkit.test.plugin import java.time.LocalDateTime import kotlin.time.Duration.Companion.seconds @@ -14,8 +13,8 @@ class StableActionbarTestCommand(name: String) : CommandAPICommand(name) { playerExecutor { sender, _ -> sender.sendActionbar(plugin.scope, 5.seconds) { appendInfoPrefix() - info("Actionbar Test: ") - variableValue(LocalDateTime.now().format(dateTimeFormatter)) + info("Aktuelle Sekunde: ") + variableValue(LocalDateTime.now().second) } } } From 23aa4cb27967c110777c180096e36ab0eefa42cd Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Sun, 28 Jun 2026 16:02:42 +0200 Subject: [PATCH 03/12] =?UTF-8?q?=E2=9C=A8=20feat(actionbar):=20add=20canc?= =?UTF-8?q?el=20method=20for=20actionbar=20jobs=20management?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - implement cancel method to allow cancellation of specific actionbar jobs by jobId - enhance cancelAll method to remove jobs from actionbars --- .../dev/slne/surf/api/core/actionbar/ActionbarService.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt index 99cf00c8..6d4c381f 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt @@ -63,6 +63,13 @@ object ActionbarService { return id } + fun cancel(jobId: UUID) { + actionbars.forEach { (_, jobs) -> + jobs[jobId]?.cancel() + jobs.remove(jobId) + } + } + fun cancelAll() { actionbars.forEach { (_, jobs) -> jobs.values.forEach { it.cancel() } From cc7dc215f8d4917b20559215b41eb16d81b934ce Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:07:19 +0200 Subject: [PATCH 04/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt index 9122926e..bd39cd8c 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt @@ -41,11 +41,12 @@ fun Audience.sendActionbar( /** * Sends an actionbar to the audience for a specified duration. * - * The audience must have an accessable UUID (see [uuidOrNull]). If the audience does not have a UUID, an error will be thrown. + * The audience must have an accessible UUID (see [uuidOrNull]). If the audience does not have a UUID, an error will be thrown. * * @param scope The CoroutineScope in which the actionbar will be sent. * @param duration The duration for which the actionbar will be displayed. * @param interval The interval at which the actionbar will be updated. Defaults to 1 second. + * @param fadeOut When true, lets the actionbar disappear naturally after the last update; when false, clears it immediately when finished. * @param text A lambda that builds the text component to be displayed in the actionbar. */ fun Audience.sendActionbar( From eacf36ef79bb9fba6ba8cf45a266aa9da70ef9c8 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:07:42 +0200 Subject: [PATCH 05/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt index bd39cd8c..77b1152c 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt @@ -10,11 +10,12 @@ import kotlin.time.Duration.Companion.seconds /** * Sends an actionbar to the audience for a specified duration. * - * The audience must have an accessable UUID (see [uuidOrNull]). If the audience does not have a UUID, an error will be thrown. + * The audience must have an accessible UUID (see [uuidOrNull]). If the audience does not have a UUID, an error will be thrown. * * @param scope The CoroutineScope in which the actionbar will be sent. * @param duration The duration for which the actionbar will be displayed. * @param interval The interval at which the actionbar will be updated. Defaults to 1 second. + * @param fadeOut When true, lets the actionbar disappear naturally after the last update; when false, clears it immediately when finished. * @param text A lambda that builds the text component to be displayed in the actionbar. * @param onCount An optional callback that is invoked each time the actionbar is sent. * @param onFinish An optional callback that is invoked when the actionbar display is finished. From 6f71311317d5062f668fe75ef9f1aa1debd77f7d Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Sun, 28 Jun 2026 16:11:14 +0200 Subject: [PATCH 06/12] =?UTF-8?q?```=20=E2=99=BB=EF=B8=8F=20refactor(actio?= =?UTF-8?q?nbar):=20remove=20redundant=20onFinish=20invocation=20in=20acti?= =?UTF-8?q?onbar=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - eliminate unnecessary onFinish invocation before sending empty action bar - streamline actionbar job management logic ``` --- .../kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt index 6d4c381f..0355b777 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt @@ -42,8 +42,6 @@ object ActionbarService { delay(interval) } - onFinish?.invoke() - if (!fadeOut) { audience.sendActionBar(Component.empty()) } From aee824443a7e662fde489c33fd1d149e562530d2 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Sun, 28 Jun 2026 16:28:55 +0200 Subject: [PATCH 07/12] Update ABI reference --- surf-api-core/surf-api-core/api/surf-api-core.api | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/surf-api-core/surf-api-core/api/surf-api-core.api b/surf-api-core/surf-api-core/api/surf-api-core.api index afdb77bd..25f53000 100644 --- a/surf-api-core/surf-api-core/api/surf-api-core.api +++ b/surf-api-core/surf-api-core/api/surf-api-core.api @@ -11,6 +11,19 @@ public final class dev/slne/surf/api/core/SurfApiCore$Companion : dev/slne/surf/ public fun sendPlayerToServer (Ljava/util/UUID;Ljava/lang/String;)V } +public final class dev/slne/surf/api/core/actionbar/ActionbarService { + public static final field INSTANCE Ldev/slne/surf/api/core/actionbar/ActionbarService; + public final fun cancel (Ljava/util/UUID;)V + public final fun cancelAll ()V +} + +public final class dev/slne/surf/api/core/actionbar/Actionbar_utilKt { + public static final fun sendActionbar-A1NDRPo (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZLkotlin/jvm/functions/Function1;)Ljava/util/UUID; + public static synthetic fun sendActionbar-A1NDRPo$default (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/util/UUID; + public static final fun sendActionbar-yR0oWTA (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)Ljava/util/UUID; + public static synthetic fun sendActionbar-yR0oWTA$default (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Ljava/util/UUID; +} + public final class dev/slne/surf/api/core/algorithms/ConvexHull2D { public static final field INSTANCE Ldev/slne/surf/api/core/algorithms/ConvexHull2D; public final fun ccw (Lorg/spongepowered/math/vector/Vectord;Lorg/spongepowered/math/vector/Vectord;Lorg/spongepowered/math/vector/Vectord;)Z From 92d8994da409cf1884380206ee133087a076e54a Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:41:23 +0200 Subject: [PATCH 08/12] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(actionbar):?= =?UTF-8?q?=20move=20action=20bar=20handling=20behind=20service=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * replace ActionbarService with service-loaded ActionBarService interface and implementation * add ActionBarFinishReason for completed, auto-cancelled, cancelled, and failed jobs * return coroutine Job from sendActionBar and support afterTick and finish callbacks * auto-cancel player action bars when the target player is no longer online * add platform player checks for Paper and Velocity audiences --- .../slne/surf/api/core/server/CoreInstance.kt | 2 - .../api/core/server/impl/SurfApiCoreImpl.kt | 7 + .../impl/actionbar/ActionBarServiceImpl.kt | 132 +++++++++++++++ .../core/actionbar/ActionBarFinishReason.kt | 26 +++ .../api/core/actionbar/ActionBarService.kt | 155 ++++++++++++++++++ .../api/core/actionbar/ActionbarService.kt | 77 --------- .../surf/api/core/actionbar/actionbar-util.kt | 68 -------- .../slne/surf/api/core/util/coroutine-util.kt | 2 +- .../test/command/SurfApiTestCommand.java | 86 ++++------ .../subcommands/StableActionbarTestCommand.kt | 4 +- .../api/paper/server/impl/SurfApiPaperImpl.kt | 5 + .../server/impl/SurfApiVelocityImpl.kt | 9 +- 12 files changed, 366 insertions(+), 207 deletions(-) create mode 100644 surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/actionbar/ActionBarServiceImpl.kt create mode 100644 surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionBarFinishReason.kt create mode 100644 surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionBarService.kt delete mode 100644 surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt delete mode 100644 surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt diff --git a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/CoreInstance.kt b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/CoreInstance.kt index ff1ba7d7..9d02aa63 100644 --- a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/CoreInstance.kt +++ b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/CoreInstance.kt @@ -1,6 +1,5 @@ package dev.slne.surf.api.core.server -import dev.slne.surf.api.core.actionbar.ActionbarService import dev.slne.surf.api.core.messages.Colors import dev.slne.surf.api.core.server.listener.CoreListenerManager import dev.slne.surf.api.core.server.util.PlayerSkinFetcher @@ -28,7 +27,6 @@ abstract class CoreInstance { @MustBeInvokedByOverriders open suspend fun onDisable() { CoreListenerManager.unregisterListeners() - ActionbarService.cancelAll() } private fun initObjects() { diff --git a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/SurfApiCoreImpl.kt b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/SurfApiCoreImpl.kt index 668cf9b6..467c1017 100644 --- a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/SurfApiCoreImpl.kt +++ b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/SurfApiCoreImpl.kt @@ -1,6 +1,7 @@ package dev.slne.surf.api.core.server.impl import dev.slne.surf.api.core.SurfApiCore +import net.kyori.adventure.audience.Audience import org.apache.commons.lang3.builder.ToStringBuilder /** @@ -9,7 +10,13 @@ import org.apache.commons.lang3.builder.ToStringBuilder */ abstract class SurfApiCoreImpl protected constructor() : SurfApiCore { + abstract fun isPlayer(audience: Audience): Boolean + override fun toString(): String { return ToStringBuilder.reflectionToString(this) } + + companion object { + fun get() = SurfApiCore.instance as SurfApiCoreImpl + } } diff --git a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/actionbar/ActionBarServiceImpl.kt b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/actionbar/ActionBarServiceImpl.kt new file mode 100644 index 00000000..247fb508 --- /dev/null +++ b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/actionbar/ActionBarServiceImpl.kt @@ -0,0 +1,132 @@ +package dev.slne.surf.api.core.server.impl.actionbar + +import com.google.auto.service.AutoService +import dev.slne.surf.api.core.SurfApiCore +import dev.slne.surf.api.core.actionbar.ActionBarFinishReason +import dev.slne.surf.api.core.actionbar.ActionBarService +import dev.slne.surf.api.core.messages.adventure.nameOrNull +import dev.slne.surf.api.core.messages.adventure.uuidOrNull +import dev.slne.surf.api.core.server.impl.SurfApiCoreImpl +import dev.slne.surf.api.core.util.logger +import kotlinx.coroutines.* +import net.kyori.adventure.audience.Audience +import net.kyori.adventure.text.Component +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference +import kotlin.time.Duration +import kotlin.time.TimeSource + +@AutoService(ActionBarService::class) +class ActionBarServiceImpl : ActionBarService { + + companion object { + private val log = logger() + } + + override fun sendActionBar( + scope: CoroutineScope, + audience: Audience, + duration: Duration, + interval: Duration, + fadeOut: Boolean, + autoCancelPlayer: Boolean, + computation: () -> Component, + afterTick: (() -> Unit)?, + onFinish: ((ActionBarFinishReason) -> Unit)? + ): Job { + require(duration.isPositive()) { "duration must be positive" } + require(interval.isPositive()) { "interval must be positive" } + + val uuid = audience.uuidOrNull() + val audienceName = audience.nameOrNull() + ?: uuid?.toString() + ?: "#Unknown" + + val autoCancelUuid = if ( + autoCancelPlayer && + uuid != null && + SurfApiCoreImpl.get().isPlayer(audience) + ) { + uuid + } else { + null + } + + fun shouldAutoCancel(): Boolean { + return autoCancelUuid != null && SurfApiCore.getPlayer(autoCancelUuid) == null + } + + val finishReason = AtomicReference(ActionBarFinishReason.CANCELLED) + + val job = scope.launch { + val end = TimeSource.Monotonic.markNow() + duration + + while (true) { + ensureActive() + + if (shouldAutoCancel()) { + finishReason.set(ActionBarFinishReason.AUTO_CANCELLED) + break + } + + if (end.hasPassedNow()) { + finishReason.set(ActionBarFinishReason.COMPLETED) + break + } + + val tickStart = TimeSource.Monotonic.markNow() + val component = try { + computation() + } catch (t: Throwable) { + log.atWarning() + .withCause(t) + .atMostEvery(900, TimeUnit.MILLISECONDS) + .log("Failed to compute actionbar for $audienceName") + null + } + + if (component != null) { + audience.sendActionBar(component) + } + + try { + afterTick?.invoke() + } catch (t: Throwable) { + log.atWarning() + .withCause(t) + .atMostEvery(900, TimeUnit.MILLISECONDS) + .log("Failed to invoke afterTick for $audienceName") + } + + val remainingDelay = interval - tickStart.elapsedNow() + if (remainingDelay.isPositive()) { + delay(remainingDelay) + } + } + + ensureActive() + + if (!fadeOut) { + audience.sendActionBar(Component.empty()) + } + } + + job.invokeOnCompletion { throwable -> + val reason = when (throwable) { + null -> finishReason.get() + is CancellationException -> ActionBarFinishReason.CANCELLED + else -> ActionBarFinishReason.FAILED + } + + try { + onFinish?.invoke(reason) + } catch (throwable: Throwable) { + log.atWarning() + .withCause(throwable) + .log("Failed to invoke onFinish for $audienceName") + } + } + + return job + } +} \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionBarFinishReason.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionBarFinishReason.kt new file mode 100644 index 00000000..f7340dcd --- /dev/null +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionBarFinishReason.kt @@ -0,0 +1,26 @@ +package dev.slne.surf.api.core.actionbar + +/** + * Describes why a repeating action bar task finished. + */ +enum class ActionBarFinishReason { + /** + * The configured duration elapsed successfully. + */ + COMPLETED, + + /** + * The task stopped because the target player is no longer online. + */ + AUTO_CANCELLED, + + /** + * The returned job was cancelled before completion. + */ + CANCELLED, + + /** + * The task failed with an unexpected exception. + */ + FAILED +} \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionBarService.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionBarService.kt new file mode 100644 index 00000000..39e9aeb1 --- /dev/null +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionBarService.kt @@ -0,0 +1,155 @@ +package dev.slne.surf.api.core.actionbar + +import dev.slne.surf.api.core.messages.builder.SurfComponentBuilder +import dev.slne.surf.api.core.util.requiredService +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import net.kyori.adventure.audience.Audience +import net.kyori.adventure.text.Component +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds + +interface ActionBarService { + + /** + * Sends an action bar to the given [audience] repeatedly. + * + * The [computation] callback is invoked once per tick to build the component that should be + * sent for the current update. If [computation] throws, the exception is logged and the current + * tick is skipped. + * + * The returned [Job] can be cancelled to stop the action bar early. When the task finishes, + * [onFinish] is invoked with an [ActionBarFinishReason] describing why it ended. + * + * If [fadeOut] is `false`, an empty action bar is sent after the task completes normally or is + * auto-cancelled. If [fadeOut] is `true`, the last sent action bar is allowed to disappear + * naturally on the client. + * + * If [autoCancelPlayer] is `true` and the [audience] is a player, the task automatically stops + * once that player is no longer online. + * + * @param scope The coroutine scope used to launch the repeating action bar task. + * @param audience The audience that should receive the action bar. + * @param duration The total duration for which the action bar should be updated. + * @param interval The delay between two action bar updates. + * @param fadeOut Whether the last action bar should fade out naturally instead of being cleared. + * @param autoCancelPlayer Whether to stop automatically when a player audience goes offline. + * @param computation Computes the component to send for the current tick. + * @param afterTick Optional callback invoked after each tick, regardless of whether a component was sent. + * @param onFinish Optional callback invoked when the action bar task finishes. + * + * @return The launched action bar job. + * + * @throws IllegalArgumentException If [duration] or [interval] is not positive. + */ + fun sendActionBar( + scope: CoroutineScope, + audience: Audience, + duration: Duration, + interval: Duration = 1.seconds, + fadeOut: Boolean = false, + autoCancelPlayer: Boolean = true, + computation: () -> Component, + afterTick: (() -> Unit)? = null, + onFinish: ((ActionBarFinishReason) -> Unit)? = null + ): Job + + companion object : ActionBarService by INSTANCE { + val instance get() = INSTANCE + } +} + +private val INSTANCE = requiredService() + +/** + * Sends a repeatedly updated action bar to this [Audience]. + * + * This overload supports [afterTick] and [onFinish] callbacks. Because [textComputation] is not + * the last parameter, callers usually need to pass it as a named argument when using callbacks. + * + * Example: + * ```kotlin + * player.sendActionBar( + * scope = scope, + * duration = 5.seconds, + * textComputation = { + * info("Loading...") + * }, + * onFinish = { reason -> + * // handle finish reason + * } + * ) + * ``` + * + * @param scope The coroutine scope used to launch the repeating action bar task. + * @param duration The total duration for which the action bar should be updated. + * @param interval The delay between two action bar updates. + * @param fadeOut Whether the last action bar should fade out naturally instead of being cleared. + * @param autoCancelPlayer Whether to stop automatically when this audience is an offline player. + * @param textComputation Builds the action bar component for the current tick. + * @param afterTick Optional callback invoked after each tick. + * @param onFinish Optional callback invoked when the action bar task finishes. + * + * @return The launched action bar job. + */ +fun Audience.sendActionBar( + scope: CoroutineScope, + duration: Duration, + interval: Duration = 1.seconds, + fadeOut: Boolean = false, + autoCancelPlayer: Boolean = true, + textComputation: SurfComponentBuilder.() -> Unit, + afterTick: (() -> Unit)? = null, + onFinish: ((ActionBarFinishReason) -> Unit)? = null +) = ActionBarService.instance.sendActionBar( + scope, + this, + duration, + interval, + fadeOut, + autoCancelPlayer, + { SurfComponentBuilder(textComputation) }, + afterTick, + onFinish +) + +/** + * Sends a repeatedly updated action bar to this [Audience]. + * + * This overload keeps [textComputation] as the trailing lambda, making simple action bars concise: + * + * ```kotlin + * player.sendActionBar(scope, 5.seconds) { + * info("Loading...") + * } + * ``` + * + * Use the overload with [afterTick] and [onFinish] parameters when callbacks are required. + * + * @param scope The coroutine scope used to launch the repeating action bar task. + * @param duration The total duration for which the action bar should be updated. + * @param interval The delay between two action bar updates. + * @param fadeOut Whether the last action bar should fade out naturally instead of being cleared. + * @param autoCancelPlayer Whether to stop automatically when this audience is an offline player. + * @param textComputation Builds the action bar component for the current tick. + * + * @return The launched action bar job. + */ +fun Audience.sendActionBar( + scope: CoroutineScope, + duration: Duration, + interval: Duration = 1.seconds, + fadeOut: Boolean = false, + autoCancelPlayer: Boolean = true, + textComputation: SurfComponentBuilder.() -> Unit +) = ActionBarService.instance.sendActionBar( + scope, + this, + duration, + interval, + fadeOut, + autoCancelPlayer, + { SurfComponentBuilder(textComputation) }, + null, + null +) \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt deleted file mode 100644 index 0355b777..00000000 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/ActionbarService.kt +++ /dev/null @@ -1,77 +0,0 @@ -package dev.slne.surf.api.core.actionbar - -import dev.slne.surf.api.core.messages.adventure.uuid -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import net.kyori.adventure.audience.Audience -import net.kyori.adventure.text.Component -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import kotlin.time.Duration -import kotlin.time.TimeSource - -object ActionbarService { - private val actionbars = ConcurrentHashMap>() - - internal fun sendActionbar( - scope: CoroutineScope, - audience: Audience, - duration: Duration, - interval: Duration, - fadeOut: Boolean, - supplier: () -> Component, - onCount: (() -> Unit)? = null, - onFinish: (() -> Unit)? = null - ): UUID { - val id = UUID.randomUUID() - val audienceUuid = audience.uuid() - - val job = scope.launch { - try { - val end = if (duration == Duration.INFINITE) { - null - } else { - TimeSource.Monotonic.markNow() + duration - } - - while (end == null || !end.hasPassedNow()) { - audience.sendActionBar(supplier()) - onCount?.invoke() - delay(interval) - } - - if (!fadeOut) { - audience.sendActionBar(Component.empty()) - } - } finally { - actionbars[audienceUuid]?.let { jobs -> - jobs.remove(id) - onFinish?.invoke() - - if (jobs.isEmpty()) { - actionbars.remove(audienceUuid) - } - } - } - } - - actionbars.computeIfAbsent(audienceUuid) { ConcurrentHashMap() }[id] = job - return id - } - - fun cancel(jobId: UUID) { - actionbars.forEach { (_, jobs) -> - jobs[jobId]?.cancel() - jobs.remove(jobId) - } - } - - fun cancelAll() { - actionbars.forEach { (_, jobs) -> - jobs.values.forEach { it.cancel() } - } - actionbars.clear() - } -} \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt deleted file mode 100644 index 77b1152c..00000000 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/actionbar/actionbar-util.kt +++ /dev/null @@ -1,68 +0,0 @@ -package dev.slne.surf.api.core.actionbar - -import dev.slne.surf.api.core.messages.adventure.uuidOrNull -import dev.slne.surf.api.core.messages.builder.SurfComponentBuilder -import kotlinx.coroutines.CoroutineScope -import net.kyori.adventure.audience.Audience -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds - -/** - * Sends an actionbar to the audience for a specified duration. - * - * The audience must have an accessible UUID (see [uuidOrNull]). If the audience does not have a UUID, an error will be thrown. - * - * @param scope The CoroutineScope in which the actionbar will be sent. - * @param duration The duration for which the actionbar will be displayed. - * @param interval The interval at which the actionbar will be updated. Defaults to 1 second. - * @param fadeOut When true, lets the actionbar disappear naturally after the last update; when false, clears it immediately when finished. - * @param text A lambda that builds the text component to be displayed in the actionbar. - * @param onCount An optional callback that is invoked each time the actionbar is sent. - * @param onFinish An optional callback that is invoked when the actionbar display is finished. - */ -fun Audience.sendActionbar( - scope: CoroutineScope, - duration: Duration, - interval: Duration = 1.seconds, - fadeOut: Boolean = false, - text: SurfComponentBuilder.() -> Unit, - onCount: (() -> Unit)? = null, - onFinish: (() -> Unit)? = null -) = ActionbarService.sendActionbar( - scope, - this, - duration, - interval, - fadeOut, - { SurfComponentBuilder(text) }, - onCount, - onFinish -) - -/** - * Sends an actionbar to the audience for a specified duration. - * - * The audience must have an accessible UUID (see [uuidOrNull]). If the audience does not have a UUID, an error will be thrown. - * - * @param scope The CoroutineScope in which the actionbar will be sent. - * @param duration The duration for which the actionbar will be displayed. - * @param interval The interval at which the actionbar will be updated. Defaults to 1 second. - * @param fadeOut When true, lets the actionbar disappear naturally after the last update; when false, clears it immediately when finished. - * @param text A lambda that builds the text component to be displayed in the actionbar. - */ -fun Audience.sendActionbar( - scope: CoroutineScope, - duration: Duration, - interval: Duration = 1.seconds, - fadeOut: Boolean = false, - text: SurfComponentBuilder.() -> Unit -) = ActionbarService.sendActionbar( - scope, - this, - duration, - interval, - fadeOut, - { SurfComponentBuilder(text) }, - null, - null -) \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/coroutine-util.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/coroutine-util.kt index 66aaa4ef..9c8668c6 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/coroutine-util.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/coroutine-util.kt @@ -307,7 +307,7 @@ fun CoroutineScope.runUntil( fun CoroutineScope.runUntil( delay: Duration, initialDelay: Duration = Duration.ZERO, - catchExceptions: Boolean, + catchExceptions: Boolean = true, predicate: suspend () -> Boolean, taskName: String? = null, block: suspend CoroutineScope.() -> Unit, 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 e94c43fe..62e78c22 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 @@ -1,63 +1,41 @@ package dev.slne.surf.api.paper.test.command; import dev.jorel.commandapi.CommandAPICommand; -import dev.slne.surf.api.paper.test.command.subcommands.CommandExceptionTest; -import dev.slne.surf.api.paper.test.command.subcommands.MaxStacksizeTest; -import dev.slne.surf.api.paper.test.command.subcommands.PacketEntityTest; -import dev.slne.surf.api.paper.test.command.subcommands.PacketLoreTest; -import dev.slne.surf.api.paper.test.command.subcommands.PrefixConfigTest; -import dev.slne.surf.api.paper.test.command.subcommands.ReflectionTest; -import dev.slne.surf.api.paper.test.command.subcommands.ScoreboardTest; -import dev.slne.surf.api.paper.test.command.subcommands.SmoothTimeSkip; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.BlockPdcContainerTest; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.GlowingTest; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.InventoryTest; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.ModernSerializerTestConfigCommand; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.OfflineInventoryEditTest; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.PaginationTest; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.ShowItemCommand; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SignedMessageArgumentTest; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SortInvCommand; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.StableActionbarTestCommand; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SummonCommandTest; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SurfEventHandlerTest; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SuspendCommandExecutionTest; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.SuspendRequirementTestCommand; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.ToastTest; -import dev.slne.surf.surfapi.bukkit.test.command.subcommands.VisualizerTest; +import dev.slne.surf.api.paper.test.command.subcommands.*; +import dev.slne.surf.surfapi.bukkit.test.command.subcommands.*; public class SurfApiTestCommand extends CommandAPICommand { - public SurfApiTestCommand() { - super("surfapitest"); + public SurfApiTestCommand() { + super("surfapitest"); - withPermission("surfapitest.use"); + withPermission("surfapitest.use"); - withSubcommands( - new PacketLoreTest("packetlore"), - new ScoreboardTest("scoreboard"), - new SmoothTimeSkip("smoothtimeskip"), - new PacketEntityTest("packetentity"), - new ReflectionTest("reflection"), - new PrefixConfigTest("prefixconfig"), - new CommandExceptionTest("commandexception"), - new MaxStacksizeTest("maxstacksize"), - new VisualizerTest("visualizer"), - new GlowingTest("glowing"), - new PaginationTest("pagination"), - new InventoryTest("inventory"), - new ToastTest("toast"), - new SuspendCommandExecutionTest("suspendCommandExecution"), - new SummonCommandTest("summoncommand"), - new SurfEventHandlerTest("eventhandler"), - new ShowItemCommand("showitem"), - new SortInvCommand("sortInv"), - new StableActionbarTestCommand("stableActionbarTest"), - new SignedMessageArgumentTest("signedmessage"), - new BlockPdcContainerTest("blockpdc"), - new OfflineInventoryEditTest("editOfflineInventory"), - new ModernSerializerTestConfigCommand("modernSerializerTestConfig"), - new SuspendRequirementTestCommand("suspendRequirement") - ); - } + withSubcommands( + new PacketLoreTest("packetlore"), + new ScoreboardTest("scoreboard"), + new SmoothTimeSkip("smoothtimeskip"), + new PacketEntityTest("packetentity"), + new ReflectionTest("reflection"), + new PrefixConfigTest("prefixconfig"), + new CommandExceptionTest("commandexception"), + new MaxStacksizeTest("maxstacksize"), + new VisualizerTest("visualizer"), + new GlowingTest("glowing"), + new PaginationTest("pagination"), + new InventoryTest("inventory"), + new ToastTest("toast"), + new SuspendCommandExecutionTest("suspendCommandExecution"), + new SummonCommandTest("summoncommand"), + new SurfEventHandlerTest("eventhandler"), + new ShowItemCommand("showitem"), + new SortInvCommand("sortInv"), + new StableActionbarTestCommand("stableActionbarTest"), + new SignedMessageArgumentTest("signedmessage"), + new BlockPdcContainerTest("blockpdc"), + new OfflineInventoryEditTest("editOfflineInventory"), + 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/StableActionbarTestCommand.kt b/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt index 38de69a0..28e28d59 100644 --- a/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt +++ b/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt @@ -3,7 +3,7 @@ package dev.slne.surf.surfapi.bukkit.test.command.subcommands import com.github.shynixn.mccoroutine.folia.scope import dev.jorel.commandapi.CommandAPICommand import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.slne.surf.api.core.actionbar.sendActionbar +import dev.slne.surf.api.core.actionbar.sendActionBar import dev.slne.surf.surfapi.bukkit.test.plugin import java.time.LocalDateTime import kotlin.time.Duration.Companion.seconds @@ -11,7 +11,7 @@ import kotlin.time.Duration.Companion.seconds class StableActionbarTestCommand(name: String) : CommandAPICommand(name) { init { playerExecutor { sender, _ -> - sender.sendActionbar(plugin.scope, 5.seconds) { + sender.sendActionBar(plugin.scope, 5.seconds) { appendInfoPrefix() info("Aktuelle Sekunde: ") variableValue(LocalDateTime.now().second) diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/impl/SurfApiPaperImpl.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/impl/SurfApiPaperImpl.kt index e9fd6304..12c1f77b 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/impl/SurfApiPaperImpl.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/impl/SurfApiPaperImpl.kt @@ -14,8 +14,10 @@ import dev.slne.surf.api.paper.time.SkipOperations import dev.slne.surf.api.paper.time.TimeSkipResult import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope +import net.kyori.adventure.audience.Audience import org.bukkit.Bukkit import org.bukkit.World +import org.bukkit.entity.Player import java.util.* @AutoService(SurfApiCore::class) @@ -45,6 +47,9 @@ class SurfApiPaperImpl : SurfApiCoreImpl(), SurfApiPaper { } override fun getPlayer(playerUuid: UUID) = Bukkit.getPlayer(playerUuid) + override fun isPlayer(audience: Audience): Boolean { + return audience is Player + } val dataFolder get() = plugin.dataPath diff --git a/surf-api-velocity/surf-api-velocity-server/src/main/kotlin/dev/slne/surf/api/velocity/server/impl/SurfApiVelocityImpl.kt b/surf-api-velocity/surf-api-velocity-server/src/main/kotlin/dev/slne/surf/api/velocity/server/impl/SurfApiVelocityImpl.kt index 0393d900..90545f33 100644 --- a/surf-api-velocity/surf-api-velocity-server/src/main/kotlin/dev/slne/surf/api/velocity/server/impl/SurfApiVelocityImpl.kt +++ b/surf-api-velocity/surf-api-velocity-server/src/main/kotlin/dev/slne/surf/api/velocity/server/impl/SurfApiVelocityImpl.kt @@ -7,10 +7,12 @@ import dev.slne.surf.api.core.server.impl.SurfApiCoreImpl import dev.slne.surf.api.core.util.checkInstantiationByServiceLoader import dev.slne.surf.api.velocity.SurfApiVelocity import dev.slne.surf.api.velocity.server.plugin +import net.kyori.adventure.audience.Audience import java.util.* @AutoService(SurfApiCore::class) class SurfApiVelocityImpl : SurfApiCoreImpl(), SurfApiVelocity { + init { checkInstantiationByServiceLoader() } @@ -18,13 +20,14 @@ class SurfApiVelocityImpl : SurfApiCoreImpl(), SurfApiVelocity { override val executorService get() = plugin.executorService override fun sendPlayerToServer(playerUuid: UUID, server: String) { - val proxy = plugin.server proxy.getPlayer(playerUuid).ifPresent { player -> proxy.getServer(server).ifPresent { server -> player.createConnectionRequest(server) } } } - override fun getPlayer(playerUuid: UUID): Player = - plugin.server.getPlayer(playerUuid).orElse(null) + override fun getPlayer(playerUuid: UUID): Player? = plugin.server.getPlayer(playerUuid).orElse(null) + override fun isPlayer(audience: Audience): Boolean { + return audience is Player + } } From 8d2661c7879db4a03d27ef34e91e6c0ddf230e30 Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:45:17 +0200 Subject: [PATCH 09/12] =?UTF-8?q?=F0=9F=94=A7=20chore(deps):=20update=20pa?= =?UTF-8?q?cketevents=20and=20packetevents-plugin=20to=202.13.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update packetevents from 2.12.2 to 2.13.0 - update packetevents-plugin from 2.12.2 to 2.13.0 --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 43d69746..a13283a4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,8 +9,8 @@ kotlinxCoroutines = "1.11.0" kotlinx-serialization = "1.11.0" # Packet Events -packetevents = "2.12.2" -packetevents-plugin = "2.12.2" +packetevents = "2.13.0" +packetevents-plugin = "2.13.0" # Command API commandapi = "11.2.0" From 77ed0622f17771b500c268d37b914a468ab39273 Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:48:04 +0200 Subject: [PATCH 10/12] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(actionbar):?= =?UTF-8?q?=20change=20playerExecutor=20to=20anyExecutor=20in=20StableActi?= =?UTF-8?q?onbarTestCommand?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update playerExecutor to anyExecutor for improved command handling --- .../test/command/subcommands/StableActionbarTestCommand.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt b/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt index 28e28d59..570df6da 100644 --- a/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt +++ b/surf-api-paper/surf-api-paper-plugin-test/src/main/kotlin/dev/slne/surf/surfapi/bukkit/test/command/subcommands/StableActionbarTestCommand.kt @@ -2,7 +2,7 @@ package dev.slne.surf.surfapi.bukkit.test.command.subcommands import com.github.shynixn.mccoroutine.folia.scope import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.playerExecutor +import dev.jorel.commandapi.kotlindsl.anyExecutor import dev.slne.surf.api.core.actionbar.sendActionBar import dev.slne.surf.surfapi.bukkit.test.plugin import java.time.LocalDateTime @@ -10,7 +10,7 @@ import kotlin.time.Duration.Companion.seconds class StableActionbarTestCommand(name: String) : CommandAPICommand(name) { init { - playerExecutor { sender, _ -> + anyExecutor { sender, _ -> sender.sendActionBar(plugin.scope, 5.seconds) { appendInfoPrefix() info("Aktuelle Sekunde: ") From 2c4b50b2647b2cc1c30cd6e17f08ad3e575f6302 Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:51:40 +0200 Subject: [PATCH 11/12] =?UTF-8?q?=E2=9C=A8=20feat(api):=20add=20isPlayer?= =?UTF-8?q?=20method=20to=20SurfStandaloneApiImpl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - implement isPlayer method to always return false for Audience --- .../slne/surf/api/standalone/impl/SurfStandaloneApiImpl.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/surf-api-standalone/src/main/kotlin/dev/slne/surf/api/standalone/impl/SurfStandaloneApiImpl.kt b/surf-api-standalone/src/main/kotlin/dev/slne/surf/api/standalone/impl/SurfStandaloneApiImpl.kt index 04202bf2..c2d6a567 100644 --- a/surf-api-standalone/src/main/kotlin/dev/slne/surf/api/standalone/impl/SurfStandaloneApiImpl.kt +++ b/surf-api-standalone/src/main/kotlin/dev/slne/surf/api/standalone/impl/SurfStandaloneApiImpl.kt @@ -4,6 +4,7 @@ import com.google.auto.service.AutoService import dev.slne.surf.api.core.SurfApiCore import dev.slne.surf.api.core.server.impl.SurfApiCoreImpl import dev.slne.surf.api.core.util.checkInstantiationByServiceLoader +import net.kyori.adventure.audience.Audience import java.nio.file.Path import java.util.* @@ -21,5 +22,9 @@ class SurfStandaloneApiImpl : SurfApiCoreImpl() { throw UnsupportedOperationException("getPlayer is not supported in standalone mode") } + override fun isPlayer(audience: Audience): Boolean { + return false + } + val dataFolder: Path get() = Path.of("api-data") } From c101c735416706923a90518e79d4fa54b8f6bc8c Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:52:18 +0200 Subject: [PATCH 12/12] =?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-core/api/surf-api-core.api | 37 ++++++++++++++----- .../api/surf-api-standalone.api | 1 + 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/surf-api-core/surf-api-core/api/surf-api-core.api b/surf-api-core/surf-api-core/api/surf-api-core.api index 25f53000..ea2c8297 100644 --- a/surf-api-core/surf-api-core/api/surf-api-core.api +++ b/surf-api-core/surf-api-core/api/surf-api-core.api @@ -11,17 +11,36 @@ public final class dev/slne/surf/api/core/SurfApiCore$Companion : dev/slne/surf/ public fun sendPlayerToServer (Ljava/util/UUID;Ljava/lang/String;)V } -public final class dev/slne/surf/api/core/actionbar/ActionbarService { - public static final field INSTANCE Ldev/slne/surf/api/core/actionbar/ActionbarService; - public final fun cancel (Ljava/util/UUID;)V - public final fun cancelAll ()V +public final class dev/slne/surf/api/core/actionbar/ActionBarFinishReason : java/lang/Enum { + public static final field AUTO_CANCELLED Ldev/slne/surf/api/core/actionbar/ActionBarFinishReason; + public static final field CANCELLED Ldev/slne/surf/api/core/actionbar/ActionBarFinishReason; + public static final field COMPLETED Ldev/slne/surf/api/core/actionbar/ActionBarFinishReason; + public static final field FAILED Ldev/slne/surf/api/core/actionbar/ActionBarFinishReason; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Ldev/slne/surf/api/core/actionbar/ActionBarFinishReason; + public static fun values ()[Ldev/slne/surf/api/core/actionbar/ActionBarFinishReason; +} + +public abstract interface class dev/slne/surf/api/core/actionbar/ActionBarService { + public static final field Companion Ldev/slne/surf/api/core/actionbar/ActionBarService$Companion; + public abstract fun sendActionBar-FragotA (Lkotlinx/coroutines/CoroutineScope;Lnet/kyori/adventure/audience/Audience;JJZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/Job; + public static synthetic fun sendActionBar-FragotA$default (Ldev/slne/surf/api/core/actionbar/ActionBarService;Lkotlinx/coroutines/CoroutineScope;Lnet/kyori/adventure/audience/Audience;JJZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/Job; +} + +public final class dev/slne/surf/api/core/actionbar/ActionBarService$Companion : dev/slne/surf/api/core/actionbar/ActionBarService { + public final fun getInstance ()Ldev/slne/surf/api/core/actionbar/ActionBarService; + public fun sendActionBar-FragotA (Lkotlinx/coroutines/CoroutineScope;Lnet/kyori/adventure/audience/Audience;JJZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/Job; +} + +public final class dev/slne/surf/api/core/actionbar/ActionBarService$DefaultImpls { + public static synthetic fun sendActionBar-FragotA$default (Ldev/slne/surf/api/core/actionbar/ActionBarService;Lkotlinx/coroutines/CoroutineScope;Lnet/kyori/adventure/audience/Audience;JJZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/Job; } -public final class dev/slne/surf/api/core/actionbar/Actionbar_utilKt { - public static final fun sendActionbar-A1NDRPo (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZLkotlin/jvm/functions/Function1;)Ljava/util/UUID; - public static synthetic fun sendActionbar-A1NDRPo$default (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/util/UUID; - public static final fun sendActionbar-yR0oWTA (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)Ljava/util/UUID; - public static synthetic fun sendActionbar-yR0oWTA$default (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Ljava/util/UUID; +public final class dev/slne/surf/api/core/actionbar/ActionBarServiceKt { + public static final fun sendActionBar-DIOjvaQ (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZZLkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/Job; + public static synthetic fun sendActionBar-DIOjvaQ$default (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/Job; + public static final fun sendActionBar-FragotA (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/Job; + public static synthetic fun sendActionBar-FragotA$default (Lnet/kyori/adventure/audience/Audience;Lkotlinx/coroutines/CoroutineScope;JJZZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/Job; } public final class dev/slne/surf/api/core/algorithms/ConvexHull2D { diff --git a/surf-api-standalone/api/surf-api-standalone.api b/surf-api-standalone/api/surf-api-standalone.api index f62b67d3..cecb21d8 100644 --- a/surf-api-standalone/api/surf-api-standalone.api +++ b/surf-api-standalone/api/surf-api-standalone.api @@ -13,6 +13,7 @@ public final class dev/slne/surf/api/standalone/impl/SurfStandaloneApiImpl : dev public fun ()V public final fun getDataFolder ()Ljava/nio/file/Path; public fun getPlayer (Ljava/util/UUID;)Ljava/lang/Object; + public fun isPlayer (Lnet/kyori/adventure/audience/Audience;)Z public fun sendPlayerToServer (Ljava/util/UUID;Ljava/lang/String;)V }