Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.

Commit 7acc5ff

Browse files
committed
feat: add command arguments for CloudServer, CloudServerGroup, OfflineCloudPlayer, and OnlineCloudPlayer
1 parent 8c103be commit 7acc5ff

File tree

8 files changed

+299
-0
lines changed

8 files changed

+299
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package dev.slne.surf.cloud.api.client.velocity
2+
3+
import com.velocitypowered.api.proxy.Player
4+
import dev.slne.surf.cloud.api.common.util.annotation.InternalApi
5+
import dev.slne.surf.surfapi.core.api.util.requiredService
6+
import kotlinx.coroutines.CoroutineScope
7+
import kotlinx.coroutines.Job
8+
9+
@InternalApi
10+
interface InternalCloudVelocityBridge {
11+
fun getPlayer(name: String): Player?
12+
13+
fun launch(block: suspend CoroutineScope.() -> Unit): Job
14+
15+
companion object {
16+
val instance = requiredService<InternalCloudVelocityBridge>()
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package dev.slne.surf.cloud.api.client.velocity.command.args
2+
3+
import com.mojang.brigadier.arguments.StringArgumentType
4+
import com.mojang.brigadier.context.CommandContext
5+
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
6+
import com.velocitypowered.api.command.VelocityBrigadierMessage
7+
import dev.jorel.commandapi.CommandAPICommand
8+
import dev.jorel.commandapi.CommandTree
9+
import dev.jorel.commandapi.arguments.Argument
10+
import dev.jorel.commandapi.arguments.ArgumentSuggestions
11+
import dev.jorel.commandapi.arguments.CommandAPIArgumentType
12+
import dev.jorel.commandapi.executors.CommandArguments
13+
import dev.slne.surf.cloud.api.common.server.CloudServer
14+
import dev.slne.surf.cloud.api.common.server.CloudServerManager
15+
import dev.slne.surf.surfapi.core.api.messages.adventure.text
16+
17+
class CloudServerArgument(nodeName: String) :
18+
Argument<CloudServer>(nodeName, StringArgumentType.string()) {
19+
override fun getPrimitiveType(): Class<CloudServer> = CloudServer::class.java
20+
override fun getArgumentType(): CommandAPIArgumentType = CommandAPIArgumentType.PRIMITIVE_TEXT
21+
22+
init {
23+
replaceSuggestions(ArgumentSuggestions.stringCollection {
24+
CloudServerManager.retrieveAllServers()
25+
.filterIsInstance<CloudServer>()
26+
.map { it.name }
27+
})
28+
}
29+
30+
override fun <Source : Any> parseArgument(
31+
cmdCtx: CommandContext<Source>,
32+
key: String,
33+
previousArgs: CommandArguments
34+
): CloudServer {
35+
val serverName = StringArgumentType.getString(cmdCtx, key)
36+
val server = CloudServerManager.retrieveServerByName(serverName) as? CloudServer
37+
38+
if (server == null) {
39+
throw SimpleCommandExceptionType(VelocityBrigadierMessage.tooltip(text("Server '$serverName' not found"))).create()
40+
}
41+
42+
return server
43+
}
44+
}
45+
46+
inline fun CommandTree.cloudServerArgument(
47+
nodeName: String,
48+
optional: Boolean = false,
49+
block: Argument<*>.() -> Unit = {}
50+
): CommandTree = then(CloudServerArgument(nodeName).setOptional(optional).apply(block))
51+
52+
inline fun Argument<*>.cloudServerArgument(
53+
nodeName: String,
54+
optional: Boolean = false,
55+
block: Argument<*>.() -> Unit = {}
56+
): Argument<*> = then(CloudServerArgument(nodeName).setOptional(optional).apply(block))
57+
58+
inline fun CommandAPICommand.cloudServerArgument(
59+
nodeName: String,
60+
optional: Boolean = false,
61+
block: Argument<*>.() -> Unit = {}
62+
): CommandAPICommand = withArguments(CloudServerArgument(nodeName).setOptional(optional).apply(block))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package dev.slne.surf.cloud.api.client.velocity.command.args
2+
3+
import com.mojang.brigadier.arguments.StringArgumentType
4+
import com.mojang.brigadier.context.CommandContext
5+
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
6+
import com.velocitypowered.api.command.VelocityBrigadierMessage
7+
import dev.jorel.commandapi.CommandAPICommand
8+
import dev.jorel.commandapi.CommandTree
9+
import dev.jorel.commandapi.arguments.Argument
10+
import dev.jorel.commandapi.arguments.ArgumentSuggestions
11+
import dev.jorel.commandapi.arguments.CommandAPIArgumentType
12+
import dev.jorel.commandapi.executors.CommandArguments
13+
import dev.slne.surf.cloud.api.common.server.CloudServerManager
14+
import dev.slne.surf.cloud.api.common.util.annotation.InternalApi
15+
import dev.slne.surf.surfapi.core.api.messages.adventure.text
16+
17+
@OptIn(InternalApi::class)
18+
class CloudServerGroupArgument(nodeName: String) :
19+
Argument<String>(nodeName, StringArgumentType.string()) {
20+
override fun getPrimitiveType(): Class<String> = String::class.java
21+
override fun getArgumentType(): CommandAPIArgumentType = CommandAPIArgumentType.PRIMITIVE_TEXT
22+
23+
init {
24+
replaceSuggestions(ArgumentSuggestions.stringCollection {
25+
CloudServerManager.retrieveAllServers()
26+
.filter { it.group.isNotEmpty() }
27+
.map { it.group }
28+
.distinct()
29+
})
30+
}
31+
32+
override fun <Source : Any> parseArgument(
33+
cmdCtx: CommandContext<Source>,
34+
key: String,
35+
previousArgs: CommandArguments
36+
): String {
37+
val groupName = StringArgumentType.getString(cmdCtx, key)
38+
39+
if (!CloudServerManager.existsServerGroup(groupName)) {
40+
throw SimpleCommandExceptionType(VelocityBrigadierMessage.tooltip(text("Server group '$groupName' not found or no servers in this group are online!"))).create()
41+
}
42+
43+
return groupName
44+
}
45+
}
46+
47+
inline fun CommandTree.cloudServerGroupArgument(
48+
nodeName: String,
49+
optional: Boolean = false,
50+
block: Argument<*>.() -> Unit = {}
51+
): CommandTree = then(CloudServerGroupArgument(nodeName).setOptional(optional).apply(block))
52+
53+
inline fun Argument<*>.cloudServerGroupArgument(
54+
nodeName: String,
55+
optional: Boolean = false,
56+
block: Argument<*>.() -> Unit = {}
57+
): Argument<*> = then(CloudServerGroupArgument(nodeName).setOptional(optional).apply(block))
58+
59+
inline fun CommandAPICommand.cloudServerGroupArgument(
60+
nodeName: String,
61+
optional: Boolean = false,
62+
block: Argument<*>.() -> Unit = {}
63+
): CommandAPICommand = withArguments(CloudServerGroupArgument(nodeName).setOptional(optional).apply(block))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package dev.slne.surf.cloud.api.client.velocity.command.args
2+
3+
import com.mojang.brigadier.arguments.StringArgumentType
4+
import com.mojang.brigadier.context.CommandContext
5+
import com.velocitypowered.api.command.CommandSource
6+
import dev.jorel.commandapi.Brigadier
7+
import dev.jorel.commandapi.CommandAPICommand
8+
import dev.jorel.commandapi.CommandTree
9+
import dev.jorel.commandapi.arguments.Argument
10+
import dev.jorel.commandapi.arguments.CommandAPIArgumentType
11+
import dev.jorel.commandapi.executors.CommandArguments
12+
import dev.slne.surf.cloud.api.client.velocity.InternalCloudVelocityBridge
13+
import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer
14+
import dev.slne.surf.cloud.api.common.player.toOfflineCloudPlayer
15+
import dev.slne.surf.cloud.api.common.util.annotation.InternalApi
16+
import dev.slne.surf.surfapi.core.api.messages.adventure.sendText
17+
import dev.slne.surf.surfapi.core.api.service.PlayerLookupService
18+
import kotlinx.coroutines.CompletableDeferred
19+
import kotlinx.coroutines.Deferred
20+
21+
class OfflineCloudPlayerArgument(nodeName: String) :
22+
Argument<Deferred<OfflineCloudPlayer?>>(nodeName, StringArgumentType.word()) {
23+
24+
@Suppress("UNCHECKED_CAST")
25+
override fun getPrimitiveType(): Class<Deferred<OfflineCloudPlayer?>> =
26+
Deferred::class.java as Class<Deferred<OfflineCloudPlayer?>>
27+
28+
override fun getArgumentType(): CommandAPIArgumentType = CommandAPIArgumentType.PRIMITIVE_STRING
29+
30+
@OptIn(InternalApi::class)
31+
override fun <Source : Any> parseArgument(
32+
cmdCtx: CommandContext<Source>,
33+
key: String,
34+
previousArgs: CommandArguments
35+
): Deferred<OfflineCloudPlayer?> {
36+
val playerName = StringArgumentType.getString(cmdCtx, key)
37+
val onlinePlayer = InternalCloudVelocityBridge.instance.getPlayer(playerName)
38+
39+
if (onlinePlayer != null) {
40+
return CompletableDeferred(onlinePlayer.toOfflineCloudPlayer())
41+
}
42+
43+
val deferred = CompletableDeferred<OfflineCloudPlayer?>()
44+
InternalCloudVelocityBridge.instance.launch {
45+
val uuid = PlayerLookupService.getUuid(playerName)
46+
val cloudPlayer = uuid.toOfflineCloudPlayer()
47+
48+
if (cloudPlayer == null) {
49+
val sender = Brigadier.getCommandSenderFromContext<CommandSource>(cmdCtx)
50+
sender.sendText { error("Player with name '$playerName' not found.") }
51+
}
52+
53+
deferred.complete(cloudPlayer)
54+
}
55+
56+
return deferred
57+
}
58+
}
59+
60+
inline fun CommandTree.offlineCloudPlayerArgument(
61+
nodeName: String,
62+
optional: Boolean = false,
63+
block: Argument<*>.() -> Unit = {}
64+
): CommandTree = then(OfflineCloudPlayerArgument(nodeName).setOptional(optional).apply(block))
65+
66+
inline fun Argument<*>.offlineCloudPlayerArgument(
67+
nodeName: String,
68+
optional: Boolean = false,
69+
block: Argument<*>.() -> Unit = {}
70+
): Argument<*> = then(OfflineCloudPlayerArgument(nodeName).setOptional(optional).apply(block))
71+
72+
inline fun CommandAPICommand.offlineCloudPlayerArgument(
73+
nodeName: String,
74+
optional: Boolean = false,
75+
block: Argument<*>.() -> Unit = {}
76+
): CommandAPICommand =
77+
withArguments(OfflineCloudPlayerArgument(nodeName).setOptional(optional).apply(block))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package dev.slne.surf.cloud.api.client.velocity.command.args
2+
3+
import com.mojang.brigadier.arguments.StringArgumentType
4+
import com.mojang.brigadier.context.CommandContext
5+
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
6+
import com.velocitypowered.api.command.VelocityBrigadierMessage
7+
import dev.jorel.commandapi.CommandAPICommand
8+
import dev.jorel.commandapi.CommandTree
9+
import dev.jorel.commandapi.arguments.Argument
10+
import dev.jorel.commandapi.arguments.CommandAPIArgumentType
11+
import dev.jorel.commandapi.executors.CommandArguments
12+
import dev.slne.surf.cloud.api.common.player.CloudPlayer
13+
import dev.slne.surf.cloud.api.common.player.CloudPlayerManager
14+
import dev.slne.surf.surfapi.core.api.messages.adventure.text
15+
16+
class OnlineCloudPlayerArgument(nodeName: String) :
17+
Argument<CloudPlayer>(nodeName, StringArgumentType.word()) {
18+
override fun getPrimitiveType(): Class<CloudPlayer> = CloudPlayer::class.java
19+
override fun getArgumentType(): CommandAPIArgumentType = CommandAPIArgumentType.PRIMITIVE_STRING
20+
21+
override fun <Source : Any> parseArgument(
22+
cmdCtx: CommandContext<Source>,
23+
key: String,
24+
previousArgs: CommandArguments
25+
): CloudPlayer {
26+
val playerName = StringArgumentType.getString(cmdCtx, key)
27+
val player = CloudPlayerManager.getPlayer(playerName)
28+
29+
if (player == null) {
30+
throw SimpleCommandExceptionType(VelocityBrigadierMessage.tooltip(text("Player '$playerName' not found"))).create()
31+
}
32+
33+
return player
34+
}
35+
}
36+
37+
inline fun CommandTree.onlineCloudPlayerArgument(
38+
nodeName: String,
39+
optional: Boolean = false,
40+
block: Argument<*>.() -> Unit = {}
41+
): CommandTree = then(OnlineCloudPlayerArgument(nodeName).setOptional(optional).apply(block))
42+
43+
inline fun Argument<*>.onlineCloudPlayerArgument(
44+
nodeName: String,
45+
optional: Boolean = false,
46+
block: Argument<*>.() -> Unit = {}
47+
): Argument<*> = then(OnlineCloudPlayerArgument(nodeName).setOptional(optional).apply(block))
48+
49+
inline fun CommandAPICommand.onlineCloudPlayerArgument(
50+
nodeName: String,
51+
optional: Boolean = false,
52+
block: Argument<*>.() -> Unit = {}
53+
): CommandAPICommand =
54+
withArguments(OnlineCloudPlayerArgument(nodeName).setOptional(optional).apply(block))

surf-cloud-api/surf-cloud-api-client/surf-cloud-api-client-velocity/src/main/kotlin/dev/slne/surf/cloud/api/client/velocity/server/VelocityCloudServeExtensions.kt renamed to surf-cloud-api/surf-cloud-api-client/surf-cloud-api-client-velocity/src/main/kotlin/dev/slne/surf/cloud/api/client/velocity/server/VelocityCloudServerExtensions.kt

File renamed without changes.

surf-cloud-core/surf-cloud-core-common/src/main/kotlin/dev/slne/surf/cloud/core/common/coroutines/scopes.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,9 @@ object SyncValueScope : BaseScope(
196196
object CommonObservableScope : BaseScope(
197197
dispatcher = Dispatchers.Default,
198198
name = "common-observable"
199+
)
200+
201+
object CommonScope : BaseScope(
202+
dispatcher = Dispatchers.Default,
203+
name = "common"
199204
)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package dev.slne.surf.cloud.velocity
2+
3+
import com.google.auto.service.AutoService
4+
import com.velocitypowered.api.proxy.Player
5+
import dev.slne.surf.cloud.api.client.velocity.InternalCloudVelocityBridge
6+
import dev.slne.surf.cloud.core.common.coroutines.CommonScope
7+
import kotlinx.coroutines.CoroutineScope
8+
import kotlinx.coroutines.Job
9+
import kotlinx.coroutines.launch
10+
11+
@AutoService(InternalCloudVelocityBridge::class)
12+
class CloudVelocityBridgeImpl : InternalCloudVelocityBridge {
13+
override fun getPlayer(name: String): Player? {
14+
return plugin.server.getPlayer(name).orElse(null)
15+
}
16+
17+
override fun launch(block: suspend CoroutineScope.() -> Unit): Job {
18+
return CommonScope.launch(block = block)
19+
}
20+
}

0 commit comments

Comments
 (0)