Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
!.idea/icon.svg
.idea
*.iws
*.iml
Expand Down
143 changes: 143 additions & 0 deletions .idea/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dev.slne.surf.cloud.api.client.server

import dev.slne.surf.cloud.api.common.server.CloudServer
import dev.slne.surf.cloud.api.common.server.CloudServerManager
import dev.slne.surf.cloud.api.common.util.annotation.InternalApi

interface CloudClientServerManager : CloudServerManager {
fun currentServer(): CloudServer

@OptIn(InternalApi::class)
companion object :
CloudClientServerManager by CloudServerManager.instance as CloudClientServerManager
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package dev.slne.surf.cloud.api.client.paper.command.args

import dev.jorel.commandapi.CommandAPICommand
import dev.jorel.commandapi.CommandTree
import dev.jorel.commandapi.arguments.Argument
import dev.jorel.commandapi.arguments.ArgumentSuggestions
import dev.jorel.commandapi.arguments.CustomArgument
import dev.jorel.commandapi.arguments.StringArgument
import dev.slne.surf.cloud.api.common.server.CloudServer
import dev.slne.surf.cloud.api.common.server.CloudServerManager
import dev.slne.surf.cloud.api.common.util.annotation.InternalApi
import dev.slne.surf.surfapi.core.api.util.logger
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.future.future


@OptIn(InternalApi::class)
class CloudServerArgument(nodeName: String) : CustomArgument<CloudServer, String>(
StringArgument(nodeName),
{ info ->
val serverName = info.input
val server = CloudServerManager.getServerByNameUnsafe(serverName) ?: run {
throw CustomArgumentException.fromMessageBuilder(
MessageBuilder()
.append("Server '")
.appendArgInput()
.append("' not found")
)
}
server
}
) {
init {
replaceSuggestions(ArgumentSuggestions.stringCollectionAsync {
scope.future {
CloudServerManager.retrieveAllServers()
.filterIsInstance<CloudServer>()
.map { it.name }
}
})
}

private companion object {
private val log = logger()
private val scope =
CoroutineScope(Dispatchers.Default + CoroutineName("CloudServerSuggestionScope") + CoroutineExceptionHandler { coroutineContext, throwable ->
log.atWarning()
.withCause(throwable)
.log("Failed to suggest cloud servers")
})
}
}

inline fun CommandTree.cloudServerArgument(
nodeName: String,
optional: Boolean = false,
block: Argument<*>.() -> Unit = {}
): CommandTree = then(CloudServerArgument(nodeName).setOptional(optional).apply(block))

inline fun Argument<*>.cloudServerArgument(
nodeName: String,
optional: Boolean = false,
block: Argument<*>.() -> Unit = {}
): Argument<*> = then(CloudServerArgument(nodeName).setOptional(optional).apply(block))

inline fun CommandAPICommand.cloudServerArgument(
nodeName: String,
optional: Boolean = false,
block: Argument<*>.() -> Unit = {}
): CommandAPICommand = withArguments(CloudServerArgument(nodeName).setOptional(optional).apply(block))
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package dev.slne.surf.cloud.api.client.paper.command.args

import dev.jorel.commandapi.CommandAPICommand
import dev.jorel.commandapi.CommandTree
import dev.jorel.commandapi.arguments.Argument
import dev.jorel.commandapi.arguments.ArgumentSuggestions
import dev.jorel.commandapi.arguments.CustomArgument
import dev.jorel.commandapi.arguments.TextArgument
import dev.slne.surf.cloud.api.common.server.CloudServerManager
import dev.slne.surf.cloud.api.common.util.annotation.InternalApi
import dev.slne.surf.surfapi.core.api.util.logger
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.future.future

@OptIn(InternalApi::class)
class CloudServerGroupArgument(nodeName: String) : CustomArgument<String, String>(
TextArgument(nodeName),
{ info ->
val groupName = info.currentInput
val exists = CloudServerManager.existsServerGroup(groupName)
if (!exists) {
throw CustomArgumentException.fromMessageBuilder(
MessageBuilder()
.append("Server group '")
.appendArgInput()
.append("' not found or no servers in this group are online!")
)
}
groupName
}
) {
init {
replaceSuggestions(ArgumentSuggestions.stringCollectionAsync {
scope.future {
CloudServerManager.retrieveAllServers()
.filter { it.group.isNotEmpty() }
.map { it.group }
.distinct()
}
})
}

companion object {
private val log = logger()
private val scope =
CoroutineScope(Dispatchers.Default + CoroutineName("CloudServerGroupSuggestionScope") + CoroutineExceptionHandler { coroutineContext, throwable ->
log.atWarning()
.withCause(throwable)
.log("Failed to suggest cloud server groups")
})
}
}

inline fun CommandTree.cloudServerGroupArgument(
nodeName: String,
optional: Boolean = false,
block: Argument<*>.() -> Unit = {}
): CommandTree = then(CloudServerGroupArgument(nodeName).setOptional(optional).apply(block))

inline fun Argument<*>.cloudServerGroupArgument(
nodeName: String,
optional: Boolean = false,
block: Argument<*>.() -> Unit = {}
): Argument<*> = then(CloudServerGroupArgument(nodeName).setOptional(optional).apply(block))

inline fun CommandAPICommand.cloudServerGroupArgument(
nodeName: String,
optional: Boolean = false,
block: Argument<*>.() -> Unit = {}
): CommandAPICommand = withArguments(CloudServerGroupArgument(nodeName).setOptional(optional).apply(block))
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package dev.slne.surf.cloud.api.client.paper.command.args

import dev.jorel.commandapi.CommandAPICommand
import dev.jorel.commandapi.CommandTree
import dev.jorel.commandapi.arguments.Argument
import dev.jorel.commandapi.arguments.ArgumentSuggestions
import dev.jorel.commandapi.arguments.AsyncOfflinePlayerArgument
import dev.jorel.commandapi.arguments.CustomArgument
import dev.slne.surf.cloud.api.client.paper.player.toCloudOfflinePlayer
import dev.slne.surf.cloud.api.common.player.CloudPlayerManager
import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer
import dev.slne.surf.surfapi.core.api.messages.adventure.sendText
import dev.slne.surf.surfapi.core.api.util.logger
import kotlinx.coroutines.*
import kotlinx.coroutines.future.await
import kotlinx.coroutines.future.future
import org.bukkit.OfflinePlayer
import java.util.concurrent.CompletableFuture

class OfflineCloudPlayerArgument(nodeName: String) :
CustomArgument<Deferred<OfflineCloudPlayer?>, CompletableFuture<OfflinePlayer>>(
AsyncOfflinePlayerArgument(nodeName),
{ info ->
scope.async {
try {
val player = info.currentInput.await()
player.toCloudOfflinePlayer()
} catch (e: RuntimeException) {
val cause = e.cause
val rootCause = if (cause is RuntimeException) cause.cause else cause

info.sender.sendText {
error(
rootCause?.message
?: "Unknown error occurred while fetching offline player"
)
}
null
}
}
}
) {
init {
includeSuggestions(ArgumentSuggestions.stringCollectionAsync {
scope.future {
CloudPlayerManager.getOnlinePlayers().map { it.name }
}
})
}

companion object {
private val log = logger()
private val scope =
CoroutineScope(Dispatchers.IO + CoroutineName("OfflineCloudPlayerArgument") + CoroutineExceptionHandler { _, throwable ->
log.atWarning()
.withCause(throwable)
.log("An error occurred in OfflineCloudPlayerArgument")
})
}
}

inline fun CommandTree.offlineCloudPlayerArgument(
nodeName: String,
optional: Boolean = false,
block: Argument<*>.() -> Unit = {}
): CommandTree = then(OfflineCloudPlayerArgument(nodeName).setOptional(optional).apply(block))

inline fun Argument<*>.offlineCloudPlayerArgument(
nodeName: String,
optional: Boolean = false,
block: Argument<*>.() -> Unit = {}
): Argument<*> = then(OfflineCloudPlayerArgument(nodeName).setOptional(optional).apply(block))

inline fun CommandAPICommand.offlineCloudPlayerArgument(
nodeName: String,
optional: Boolean = false,
block: Argument<*>.() -> Unit = {}
): CommandAPICommand =
withArguments(OfflineCloudPlayerArgument(nodeName).setOptional(optional).apply(block))
Loading