diff --git a/gradle.properties b/gradle.properties index 45d9a9d44..afbbbe249 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/gradle/libs.versions.toml b/gradle/libs.versions.toml index 43d697468..a13283a4f 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" 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 668cf9b66..467c10172 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 000000000..247fb508f --- /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/api/surf-api-core.api b/surf-api-core/surf-api-core/api/surf-api-core.api index afdb77bd1..ea2c8297f 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,38 @@ 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/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/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 { 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 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 000000000..f7340dcda --- /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 000000000..39e9aeb1a --- /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/util/coroutine-util.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/coroutine-util.kt index 66aaa4ef0..9c8668c6b 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 b454e4da8..62e78c227 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 @@ -30,6 +30,7 @@ public SurfApiTestCommand() { new SurfEventHandlerTest("eventhandler"), new ShowItemCommand("showitem"), new SortInvCommand("sortInv"), + new StableActionbarTestCommand("stableActionbarTest"), new SignedMessageArgumentTest("signedmessage"), new BlockPdcContainerTest("blockpdc"), new OfflineInventoryEditTest("editOfflineInventory"), 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 000000000..570df6dae --- /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,21 @@ +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.anyExecutor +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 + +class StableActionbarTestCommand(name: String) : CommandAPICommand(name) { + init { + anyExecutor { sender, _ -> + sender.sendActionBar(plugin.scope, 5.seconds) { + appendInfoPrefix() + info("Aktuelle Sekunde: ") + variableValue(LocalDateTime.now().second) + } + } + } +} \ 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/impl/SurfApiPaperImpl.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/impl/SurfApiPaperImpl.kt index e9fd63046..12c1f77b8 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-standalone/api/surf-api-standalone.api b/surf-api-standalone/api/surf-api-standalone.api index f62b67d3f..cecb21d85 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 } 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 04202bf22..c2d6a5677 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") } 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 0393d9002..90545f339 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 + } }