-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
help wantedExtra attention is neededExtra attention is needed
Description
Status
- Forge
- Paper
- Velocity
- Fabric
- Bungee
Goal
The goal is to find a way to create multiplatform commands using mojang's Brigadier.
Command Builder API
Using paper, Forge we are using something like that
import io.papermc.paper.command.brigadier.CommandSourceStack
import io.papermc.paper.command.brigadier.Commands
Commands.literal(literal)
Commands.argument(name, argumentType)Which leads us to create
interface MultiplatformCommands<CommandSourceStack> {
fun literal(literal: String): LiteralArgumentBuilder<CommandSourceStack>
fun <T : Any> argument(
name: String,
argumentType: ArgumentType<T>
): RequiredArgumentBuilder<CommandSourceStack, T>
}After, we can create multiplatform commands
class MultiplatformCommand<CommandSourceStack>(
val commands: MultiplatformCommands<CommandSourceStack>
) {
fun command(
alias: String,
block: LiteralArgumentBuilder<CommandSourceStack>.() -> Unit
): LiteralArgumentBuilder<CommandSourceStack> {
val literal = commands.literal(alias)
literal.block()
return literal
}
fun LiteralArgumentBuilder<CommandSourceStack>.literal(
alias: String,
block: LiteralArgumentBuilder<CommandSourceStack>.() -> Unit
) {
val literal = commands.literal(alias)
literal.block()
this.then(literal)
}
data class BrigadierArgument<T : Any>(
val alias: String,
val type: ArgumentType<T>,
val clazz: Class<T>
)
inline fun <reified T : Any> LiteralArgumentBuilder<CommandSourceStack>.argument(
alias: String,
type: ArgumentType<T>,
noinline block: RequiredArgumentBuilder<CommandSourceStack, T>.(BrigadierArgument<T>) -> Unit
) = argument(
alias = alias,
type = type,
clazz = T::class.java,
block = block
)
fun <T : Any> LiteralArgumentBuilder<CommandSourceStack>.argument(
alias: String,
type: ArgumentType<T>,
clazz: Class<T>,
block: RequiredArgumentBuilder<CommandSourceStack, T>.(BrigadierArgument<T>) -> Unit
) {
val argument = commands.argument(alias, type)
val brigadierArgument = BrigadierArgument(
alias = alias,
type = type,
clazz = clazz
)
argument.block(brigadierArgument)
this.then(argument)
}
inline fun <reified T : Any> RequiredArgumentBuilder<CommandSourceStack, *>.argument(
alias: String,
type: ArgumentType<T>,
noinline block: RequiredArgumentBuilder<CommandSourceStack, T>.(BrigadierArgument<T>) -> Unit
) = argument(
alias = alias,
type = type,
clazz = T::class.java,
block = block
)
fun <T : Any> RequiredArgumentBuilder<CommandSourceStack, *>.argument(
alias: String,
type: ArgumentType<T>,
clazz: Class<T>,
block: RequiredArgumentBuilder<CommandSourceStack, T>.(BrigadierArgument<T>) -> Unit
) {
val argument = commands.argument(alias, type)
val brigadierArgument = BrigadierArgument(
alias = alias,
type = type,
clazz = clazz
)
argument.block(brigadierArgument)
this.then(argument)
}
fun RequiredArgumentBuilder<CommandSourceStack, *>.runs(
onFailure: (CommandContext<CommandSourceStack>, Throwable) -> Unit = { _, _ -> },
block: (RequiredArgumentBuilder<CommandSourceStack, *>.(CommandContext<CommandSourceStack>) -> Unit)
) {
executes { ctx ->
runCatching { block.invoke(this, ctx) }
.onFailure { onFailure.invoke(ctx, it) }
Command.SINGLE_SUCCESS
}
}
fun LiteralArgumentBuilder<CommandSourceStack>.runs(
onFailure: (CommandContext<CommandSourceStack>, Throwable) -> Unit = { _, _ -> },
block: LiteralArgumentBuilder<CommandSourceStack>.(CommandContext<CommandSourceStack>) -> Unit
) {
executes { ctx ->
runCatching { block.invoke(this, ctx) }
.onFailure { onFailure.invoke(ctx, it) }
Command.SINGLE_SUCCESS
}
}
fun RequiredArgumentBuilder<CommandSourceStack, *>.hints(block: (CommandContext<CommandSourceStack>) -> List<String>) {
suggests { context, builder ->
block.invoke(context).forEach(builder::suggest)
builder.buildFuture()
}
}
@Throws(IllegalArgumentException::class)
fun <T : Any> CommandContext<CommandSourceStack>.requireArgument(bArgument: BrigadierArgument<T>): T {
return getArgument(bArgument.alias, bArgument.clazz)
}
@Throws(ArgumentConverterException::class)
fun <T : Any> CommandContext<CommandSourceStack>.requireArgument(
bArgument: BrigadierArgument<String>,
converter: ArgumentConverter<T>
): T {
val string = getArgument(bArgument.alias, bArgument.clazz)
return converter.transform(string)
}
}Command Context problem
However, we can't use CommandContext here
val command = MultiplatformCommand(PaperMultiplatformCommands)
fun multiplatform(command: MultiplatformCommand<*>) {
with(command) {
command("") {
literal("") {
argument("asd", StringArgumentType.string()) { strArg ->
runs { ctx ->
val str = ctx.requireArgument(strArg)
val custom = ctx.requireArgument(strArg, IntArgumentConverter)
// The problem
val source: Any? = ctx.source
}
}
}
}
}
}Which means, we should create custom CommandContext wrapper
sealed interface MultiplatformCommandExecutor {
data class Player(val player: MinecraftPlayer) : MultiplatformCommandExecutor
data object Console : MultiplatformCommandExecutor
}
interface MultiplatformCommandContext {
val executor: MultiplatformCommandExecutor
}
interface MultiplatformCommands<CommandSourceStack> {
// ....
fun CommandContext<CommandSourceStack>.toMultiplatformCommandContext(): MultiplatformCommandContext
}
class PaperMultiplatformCommandContext(
ctx: CommandContext<CommandSourceStack>
) : MultiplatformCommandContext {
override val executor: MultiplatformCommandExecutor = let {
(ctx.source.sender as? ConsoleCommandSender)?.let {
MultiplatformCommandExecutor.Console
} ?: (ctx.source.sender as? Player)?.let {
MultiplatformCommandExecutor.Player(it.asOnlineMinecraftPlayer())
} ?: error("Unknown executor")
}
}
val command = MultiplatformCommand(PaperMultiplatformCommands)
fun multiplatform(command: MultiplatformCommand<CommandSourceStack>) {
with(command) {
command("") {
literal("") {
argument("asd", StringArgumentType.string()) { strArg ->
runs { ctx ->
val str = ctx.requireArgument(strArg)
val custom = ctx.requireArgument(strArg, IntArgumentConverter)
// The problem
val source: Any? = ctx.source
val mppCtx = ctx.toMultiplatformCommandContext()
val executor: MultiplatformCommandExecutor = mppCtx.executor
}
}
}
}
}
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
help wantedExtra attention is neededExtra attention is needed