It compiles! Finished MC tab completion
- Custom tab complete methods are now case-sensitive - Custom tab complete methods are also not supported for now
This commit is contained in:
parent
ee7e531dc0
commit
5e1f378ec7
9 changed files with 137 additions and 214 deletions
|
@ -62,7 +62,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
<version>3.2.1</version>
|
<version>3.4.1</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>package</phase>
|
<phase>package</phase>
|
||||||
|
|
|
@ -17,6 +17,7 @@ abstract class TBMCChatEventBase(
|
||||||
*/
|
*/
|
||||||
val groupID: String,
|
val groupID: String,
|
||||||
) : Event(true), Cancellable {
|
) : Event(true), Cancellable {
|
||||||
|
@JvmField
|
||||||
var isCancelled: Boolean = false
|
var isCancelled: Boolean = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -139,18 +139,9 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
||||||
val ann = meth.getAnnotation(Subcommand::class.java) ?: continue
|
val ann = meth.getAnnotation(Subcommand::class.java) ?: continue
|
||||||
val fullPath = command.commandPath + CommandUtils.getCommandPath(meth.name, ' ')
|
val fullPath = command.commandPath + CommandUtils.getCommandPath(meth.name, ' ')
|
||||||
val (lastNode, mainNode, remainingPath) = registerNodeFromPath(fullPath)
|
val (lastNode, mainNode, remainingPath) = registerNodeFromPath(fullPath)
|
||||||
lastNode.addChild(
|
lastNode.addChild(getExecutableNode(meth, command, ann, remainingPath, CommandArgumentHelpManager(command), fullPath))
|
||||||
getExecutableNode(
|
|
||||||
meth,
|
|
||||||
command,
|
|
||||||
ann,
|
|
||||||
remainingPath,
|
|
||||||
CommandArgumentHelpManager(command),
|
|
||||||
fullPath
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (mainCommandNode == null) mainCommandNode = mainNode
|
if (mainCommandNode == null) mainCommandNode = mainNode
|
||||||
else if (mainNode!!.name != mainCommandNode.name) {
|
else if (mainNode.name != mainCommandNode.name) {
|
||||||
MainPlugin.instance.logger.warning("Multiple commands are defined in the same class! This is not supported. Class: " + command.javaClass.simpleName)
|
MainPlugin.instance.logger.warning("Multiple commands are defined in the same class! This is not supported. Class: " + command.javaClass.simpleName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,7 +193,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
||||||
* @return The last no-op node that can be used to register the executable node,
|
* @return The last no-op node that can be used to register the executable node,
|
||||||
* the main command node and the last part of the command path (that isn't registered yet)
|
* the main command node and the last part of the command path (that isn't registered yet)
|
||||||
*/
|
*/
|
||||||
private fun registerNodeFromPath(path: String): Triple<CommandNode<TP>, CoreCommandNode<TP, *>?, String> {
|
private fun registerNodeFromPath(path: String): Triple<CommandNode<TP>, CoreCommandNode<TP, *>, String> {
|
||||||
val split = path.split(" ")
|
val split = path.split(" ")
|
||||||
var parent: CommandNode<TP> = dispatcher.root
|
var parent: CommandNode<TP> = dispatcher.root
|
||||||
var mainCommand: CoreCommandNode<TP, *>? = null
|
var mainCommand: CoreCommandNode<TP, *>? = null
|
||||||
|
@ -214,7 +205,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
||||||
if (i == 0) mainCommand =
|
if (i == 0) mainCommand =
|
||||||
parent as CoreCommandNode<TP, *> // Has to be our own literal node, if not, well, error
|
parent as CoreCommandNode<TP, *> // Has to be our own literal node, if not, well, error
|
||||||
}
|
}
|
||||||
return Triple(parent, mainCommand, split.last())
|
return Triple(parent, mainCommand!!, split.last())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSubcommandList(): (Any) -> Array<String> {
|
private fun getSubcommandList(): (Any) -> Array<String> {
|
||||||
|
@ -425,17 +416,15 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
||||||
fun getSubcommands(
|
fun getSubcommands(
|
||||||
mainCommand: LiteralCommandNode<TP>,
|
mainCommand: LiteralCommandNode<TP>,
|
||||||
deep: Boolean = true
|
deep: Boolean = true
|
||||||
): List<CoreCommandNode<TP, SubcommandData<TC, TP>>> {
|
): List<CoreExecutableNode<TP, TC>> {
|
||||||
return getSubcommands(mainCommand, deep, mainCommand.core())
|
return getSubcommands(deep, mainCommand.core())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSubcommands(
|
private fun getSubcommands(
|
||||||
mainCommand: LiteralCommandNode<TP>,
|
|
||||||
deep: Boolean = true,
|
deep: Boolean = true,
|
||||||
root: CoreCommandNode<TP, NoOpSubcommandData>
|
root: CoreNoOpNode<TP>
|
||||||
): List<CoreCommandNode<TP, SubcommandData<TC, TP>>> {
|
): List<CoreExecutableNode<TP, TC>> {
|
||||||
|
|
||||||
return root.children.mapNotNull { it.coreExecutable<TP, TC>() } +
|
return root.children.mapNotNull { it.coreExecutable<TP, TC>() } +
|
||||||
if (deep) root.children.flatMap { getSubcommands(mainCommand, deep, it.core()) } else emptyList()
|
if (deep) root.children.flatMap { getSubcommands(deep, it.core()) } else emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,19 +5,15 @@ import buttondevteam.lib.TBMCCoreAPI
|
||||||
import buttondevteam.lib.architecture.ButtonPlugin
|
import buttondevteam.lib.architecture.ButtonPlugin
|
||||||
import buttondevteam.lib.architecture.Component
|
import buttondevteam.lib.architecture.Component
|
||||||
import buttondevteam.lib.chat.commands.CommandUtils
|
import buttondevteam.lib.chat.commands.CommandUtils
|
||||||
|
import buttondevteam.lib.chat.commands.CommandUtils.coreArgument
|
||||||
import buttondevteam.lib.chat.commands.CommandUtils.coreExecutable
|
import buttondevteam.lib.chat.commands.CommandUtils.coreExecutable
|
||||||
import buttondevteam.lib.chat.commands.MCCommandSettings
|
import buttondevteam.lib.chat.commands.MCCommandSettings
|
||||||
import buttondevteam.lib.chat.commands.SubcommandData
|
import buttondevteam.lib.chat.commands.SubcommandData
|
||||||
import buttondevteam.lib.player.ChromaGamerBase
|
import buttondevteam.lib.player.ChromaGamerBase
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType
|
import com.mojang.brigadier.arguments.StringArgumentType
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder.literal
|
||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder
|
||||||
import com.mojang.brigadier.context.CommandContext
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder.argument
|
||||||
import com.mojang.brigadier.suggestion.Suggestion
|
|
||||||
import com.mojang.brigadier.suggestion.SuggestionProvider
|
|
||||||
import com.mojang.brigadier.suggestion.Suggestions
|
|
||||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder
|
|
||||||
import com.mojang.brigadier.tree.ArgumentCommandNode
|
|
||||||
import com.mojang.brigadier.tree.CommandNode
|
import com.mojang.brigadier.tree.CommandNode
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode
|
import com.mojang.brigadier.tree.LiteralCommandNode
|
||||||
import me.lucko.commodore.Commodore
|
import me.lucko.commodore.Commodore
|
||||||
|
@ -31,9 +27,7 @@ import org.bukkit.entity.Player
|
||||||
import org.bukkit.event.Listener
|
import org.bukkit.event.Listener
|
||||||
import org.bukkit.permissions.Permission
|
import org.bukkit.permissions.Permission
|
||||||
import org.bukkit.permissions.PermissionDefault
|
import org.bukkit.permissions.PermissionDefault
|
||||||
import java.lang.reflect.Parameter
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.BiConsumer
|
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
@ -179,7 +173,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
bukkitCommand = oldcmd
|
bukkitCommand = oldcmd
|
||||||
if (bukkitCommand is PluginCommand) bukkitCommand.setExecutor(this::executeCommand)
|
if (bukkitCommand is PluginCommand) bukkitCommand.setExecutor(this::executeCommand)
|
||||||
}
|
}
|
||||||
if (CommodoreProvider.isSupported()) TabcompleteHelper.registerTabcomplete(command, node, bukkitCommand)
|
TabcompleteHelper.registerTabcomplete(command, node, bukkitCommand)
|
||||||
bukkitCommand
|
bukkitCommand
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (command.component == null)
|
if (command.component == null)
|
||||||
|
@ -220,12 +214,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IllegalArgumentException::class)
|
@Throws(IllegalArgumentException::class)
|
||||||
override fun tabComplete(
|
override fun tabComplete(sender: CommandSender, alias: String, args: Array<out String>?, location: Location?): MutableList<String> {
|
||||||
sender: CommandSender,
|
|
||||||
alias: String,
|
|
||||||
args: Array<out String>?,
|
|
||||||
location: Location?
|
|
||||||
): MutableList<String> {
|
|
||||||
return mutableListOf()
|
return mutableListOf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,151 +223,75 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
private val commodore: Commodore by lazy {
|
private val commodore: Commodore by lazy {
|
||||||
val commodore = CommodoreProvider.getCommodore(MainPlugin.instance) //Register all to the Core, it's easier
|
val commodore = CommodoreProvider.getCommodore(MainPlugin.instance) //Register all to the Core, it's easier
|
||||||
commodore.register(
|
commodore.register(
|
||||||
LiteralArgumentBuilder.literal<Any?>("un") // TODO: This is a test
|
literal<Any?>("un") // TODO: This is a test
|
||||||
.redirect(
|
.redirect(argument<Any?, String>("unsomething", StringArgumentType.word())
|
||||||
RequiredArgumentBuilder.argument<Any?, String>(
|
.suggests { _, builder -> builder.suggest("untest").buildFuture() }.build()
|
||||||
"unsomething",
|
|
||||||
StringArgumentType.word()
|
|
||||||
).suggests { _, builder ->
|
|
||||||
builder.suggest("untest").buildFuture()
|
|
||||||
}.build()
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
commodore
|
commodore
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerTabcomplete(
|
fun registerTabcomplete(command2MC: ICommand2MC, commandNode: CoreCommandNode<Command2MCSender, *>, bukkitCommand: Command) {
|
||||||
command2MC: ICommand2MC,
|
if (!CommodoreProvider.isSupported()) {
|
||||||
commandNode: LiteralCommandNode<Command2MCSender>,
|
throw UnsupportedOperationException("Commodore is not supported! Please use 1.14 or higher. Server version: ${Bukkit.getVersion()}")
|
||||||
bukkitCommand: Command
|
}
|
||||||
) {
|
// TODO: Allow extending annotation processing for methods and parameters
|
||||||
commodore.dispatcher.root.getChild(commandNode.name) // TODO: Probably unnecessary
|
val customTabCompleteMethods = command2MC.javaClass.declaredMethods
|
||||||
val customTCmethods =
|
.flatMap { method ->
|
||||||
Arrays.stream(command2MC.javaClass.declaredMethods) //val doesn't recognize the type arguments
|
method.getAnnotation(CustomTabCompleteMethod::class.java)?.let { ctcmAnn ->
|
||||||
.flatMap { method ->
|
(ctcmAnn.subcommand.takeIf { it.isNotEmpty() }
|
||||||
Optional.ofNullable(method.getAnnotation(CustomTabCompleteMethod::class.java)).stream()
|
?: arrayOf(CommandUtils.getCommandPath(method.name, ' ').trim { it <= ' ' }))
|
||||||
.flatMap { ctcmAnn ->
|
.map { name -> Triple(name, ctcmAnn, method) }
|
||||||
val paths = Optional.of(ctcmAnn.subcommand).filter { s -> s.isNotEmpty() }
|
} ?: emptyList()
|
||||||
.orElseGet {
|
}
|
||||||
arrayOf(
|
val mcNode = CommandUtils.mapSubcommands(commandNode) { node ->
|
||||||
CommandUtils.getCommandPath(method.name, ' ').trim { it <= ' ' })
|
val builder = node.createBuilder()
|
||||||
}
|
val argNode = node.coreArgument() ?: return@mapSubcommands builder
|
||||||
Arrays.stream(paths).map { name: String? -> Triple(name, ctcmAnn, method) }
|
val subpath = "" // TODO: This needs the same processing as the command path to have the same flexibility
|
||||||
}
|
val argData = argNode.commandData.arguments[argNode.name] ?: return@mapSubcommands builder
|
||||||
}.toList()
|
val customTCTexts = argData.annotations.filterIsInstance<CustomTabComplete>().flatMap { it.value.asList() }
|
||||||
for (subcmd in subcmds) {
|
val customTCmethod = customTabCompleteMethods.firstOrNull { (name, ann, _) ->
|
||||||
val subpathAsOne = CommandUtils.getCommandPath(subcmd.method.getName(), ' ').trim { it <= ' ' }
|
name == subpath && argData.name.replace("[\\[\\]<>]".toRegex(), "") == ann.param
|
||||||
val subpath = subpathAsOne.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
}
|
||||||
var scmd: CommandNode<Any> = cmd
|
(builder as RequiredArgumentBuilder<Command2MCSender, *>).suggests { context, b ->
|
||||||
if (subpath[0].isNotEmpty()) { //If the method is def, it will contain one empty string
|
val sbuilder = if (argData.greedy) { //Do it before the builder is used
|
||||||
for (s in subpath) {
|
val nextTokenStart = context.input.lastIndexOf(' ') + 1
|
||||||
scmd =
|
b.createOffset(nextTokenStart)
|
||||||
appendSubcommand(s, scmd, subcmd) //Add method name part of the path (could_be_multiple())
|
} else b
|
||||||
|
// Suggest custom tab complete texts
|
||||||
|
for (ctc in customTCTexts) {
|
||||||
|
sbuilder.suggest(ctc)
|
||||||
|
}
|
||||||
|
val ignoreCustomParamType = false // TODO: This should be set by the @CustomTabCompleteMethod annotation
|
||||||
|
// TODO: Custom tab complete method handling
|
||||||
|
if (!ignoreCustomParamType) {
|
||||||
|
val converter = getParamConverter(argData.type, command2MC)
|
||||||
|
if (converter != null) {
|
||||||
|
val suggestions = converter.allSupplier.get()
|
||||||
|
for (suggestion in suggestions) sbuilder.suggest(suggestion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argData.type === Boolean::class.javaPrimitiveType || argData.type === Boolean::class.java)
|
||||||
|
sbuilder.suggest("true").suggest("false")
|
||||||
|
val loweredInput = sbuilder.remaining.lowercase(Locale.getDefault())
|
||||||
|
// The list is automatically ordered, so we need to put the <param> at the end after that
|
||||||
|
// We're also removing all suggestions that don't start with the input
|
||||||
|
sbuilder.suggest(argData.name).buildFuture().whenComplete { ss, _ ->
|
||||||
|
ss.list.add(ss.list.removeAt(0))
|
||||||
|
}.whenComplete { ss, _ ->
|
||||||
|
ss.list.removeIf { s ->
|
||||||
|
s.text.lowercase().let { !it.startsWith("<") && !it.startsWith("[") && !it.startsWith(loweredInput) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val parameters: Array<Parameter> = subcmd.method.getParameters()
|
builder
|
||||||
for (i in 1 until parameters.size) { //Skip sender
|
|
||||||
val parameter = parameters[i]
|
|
||||||
val customParamType: Boolean
|
|
||||||
// TODO: Arg type
|
|
||||||
val param: String = subcmd.parameters.get(i - 1)
|
|
||||||
val customTC = Optional.ofNullable(parameter.getAnnotation(CustomTabComplete::class.java))
|
|
||||||
.map { obj -> obj.value }
|
|
||||||
val customTCmethod =
|
|
||||||
customTCmethods.stream().filter { t -> subpathAsOne.equals(t.first, ignoreCase = true) }
|
|
||||||
.filter { t -> param.replace("[\\[\\]<>]", "").equals(t.second.param, ignoreCase = true) }
|
|
||||||
.findAny()
|
|
||||||
val argb: RequiredArgumentBuilder<S, T> = RequiredArgumentBuilder.argument(param, type)
|
|
||||||
.suggests(SuggestionProvider<S?> { context: CommandContext<S?>, builder: SuggestionsBuilder ->
|
|
||||||
if (parameter.isVarArgs) { //Do it before the builder is used
|
|
||||||
val nextTokenStart = context.getInput().lastIndexOf(' ') + 1
|
|
||||||
builder = builder.createOffset(nextTokenStart)
|
|
||||||
}
|
|
||||||
if (customTC.isPresent) for (ctc in customTC.get()) builder.suggest(ctc)
|
|
||||||
var ignoreCustomParamType = false
|
|
||||||
if (customTCmethod.isPresent) {
|
|
||||||
val tr = customTCmethod.get()
|
|
||||||
if (tr.second.ignoreTypeCompletion) ignoreCustomParamType = true
|
|
||||||
val method = tr.third
|
|
||||||
val params = method.parameters
|
|
||||||
val args = arrayOfNulls<Any>(params.size)
|
|
||||||
var j = 0
|
|
||||||
var k = 0
|
|
||||||
while (j < args.size && k < subcmd.parameters.length) {
|
|
||||||
val paramObj = params[j]
|
|
||||||
if (CommandSender::class.java.isAssignableFrom(paramObj.type)) {
|
|
||||||
args[j] = commodore.getBukkitSender(context.getSource())
|
|
||||||
j++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val paramValueString = context.getArgument(subcmd.parameters.get(k), String::class.java)
|
|
||||||
if (paramObj.type == String::class.java) {
|
|
||||||
args[j] = paramValueString
|
|
||||||
j++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
//Break if converter is not found or for example, the player provided an invalid plugin name
|
|
||||||
val converter = getParamConverter(params[j].type, command2MC) ?: break
|
|
||||||
val paramValue = converter.converter.apply(paramValueString) ?: break
|
|
||||||
args[j] = paramValue
|
|
||||||
k++ //Only increment if not CommandSender
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
if (args.isEmpty() || args[args.size - 1] != null) { //Arguments filled entirely
|
|
||||||
try {
|
|
||||||
when (val suggestions = method.invoke(command2MC, *args)) {
|
|
||||||
is Iterable<*> -> {
|
|
||||||
for (suggestion in suggestions) if (suggestion is String) builder.suggest(
|
|
||||||
suggestion as String?
|
|
||||||
) else throw ClassCastException("Bad return type! It should return an Iterable<String> or a String[].")
|
|
||||||
}
|
|
||||||
|
|
||||||
is Array<*> -> for (suggestion in suggestions) builder.suggest(suggestion)
|
|
||||||
else -> throw ClassCastException("Bad return type! It should return a String[] or an Iterable<String>.")
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
val msg = "Failed to run tabcomplete method " + method.name + " for command " + command2MC.javaClass.simpleName
|
|
||||||
if (command2MC.component == null) TBMCCoreAPI.SendException(msg, e, command2MC.plugin) else TBMCCoreAPI.SendException(msg, e, command2MC.component)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!ignoreCustomParamType && customParamType) {
|
|
||||||
val converter = getParamConverter(ptype, command2MC)
|
|
||||||
if (converter != null) {
|
|
||||||
val suggestions = converter.allSupplier.get()
|
|
||||||
for (suggestion in suggestions) builder.suggest(suggestion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ptype === Boolean::class.javaPrimitiveType || ptype === Boolean::class.java) builder.suggest("true").suggest("false")
|
|
||||||
val loweredInput = builder.remaining.lowercase(Locale.getDefault())
|
|
||||||
builder.suggest(param).buildFuture().whenComplete(BiConsumer<Suggestions, Throwable> { s: Suggestions, e: Throwable? -> //The list is automatically ordered
|
|
||||||
s.list.add(s.list.removeAt(0))
|
|
||||||
}) //So we need to put the <param> at the end after that
|
|
||||||
.whenComplete(BiConsumer<Suggestions, Throwable> { ss: Suggestions, e: Throwable? ->
|
|
||||||
ss.list.removeIf { s: Suggestion ->
|
|
||||||
val text = s.text
|
|
||||||
!text.startsWith("<") && !text.startsWith("[") && !text.lowercase(Locale.getDefault()).startsWith(loweredInput)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
val arg: ArgumentCommandNode<S, T> = argb.build()
|
|
||||||
scmd.addChild(arg)
|
|
||||||
scmd = arg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (shouldRegister.get()) {
|
|
||||||
commodore.register(maincmd)
|
commodore.register(mcNode as LiteralCommandNode<*>)
|
||||||
//MinecraftArgumentTypes.getByKey(NamespacedKey.minecraft(""))
|
commodore.register(literal<Command2MCSender>("${command2MC.plugin.name.lowercase()}:${mcNode.name}").redirect(mcNode))
|
||||||
val pluginName = command2MC.plugin.name.lowercase(Locale.getDefault())
|
for (alias in bukkitCommand.aliases) {
|
||||||
val prefixedcmd = LiteralArgumentBuilder.literal<Any>(pluginName + ":" + path.get(0))
|
commodore.register(literal<Command2MCSender>(alias).redirect(mcNode))
|
||||||
.redirect(maincmd).build()
|
commodore.register(literal<Command2MCSender>("${command2MC.plugin.name.lowercase()}:${alias}").redirect(mcNode))
|
||||||
commodore.register(prefixedcmd)
|
|
||||||
for (alias in bukkitCommand.aliases) {
|
|
||||||
commodore.register(LiteralArgumentBuilder.literal<Any>(alias).redirect(maincmd).build())
|
|
||||||
commodore.register(
|
|
||||||
LiteralArgumentBuilder.literal<Any>("$pluginName:$alias").redirect(maincmd).build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,4 +308,6 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
return converter
|
return converter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private typealias CNode = CommandNode<Command2MCSender>
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
package buttondevteam.lib.chat
|
package buttondevteam.lib.chat
|
||||||
|
|
||||||
|
import buttondevteam.lib.chat.commands.SubcommandData
|
||||||
import com.mojang.brigadier.arguments.ArgumentType
|
import com.mojang.brigadier.arguments.ArgumentType
|
||||||
import com.mojang.brigadier.builder.ArgumentBuilder
|
import com.mojang.brigadier.builder.ArgumentBuilder
|
||||||
import com.mojang.brigadier.suggestion.SuggestionProvider
|
import com.mojang.brigadier.suggestion.SuggestionProvider
|
||||||
|
|
||||||
class CoreArgumentBuilder<S, T>(
|
class CoreArgumentBuilder<S : Command2Sender, T>(
|
||||||
private val name: String,
|
private val name: String,
|
||||||
private val type: ArgumentType<T>,
|
private val type: ArgumentType<T>,
|
||||||
private val optional: Boolean
|
private val optional: Boolean
|
||||||
) : ArgumentBuilder<S, CoreArgumentBuilder<S, T>>() {
|
) : ArgumentBuilder<S, CoreArgumentBuilder<S, T>>() {
|
||||||
private var suggestionsProvider: SuggestionProvider<S>? = null
|
private var suggestionsProvider: SuggestionProvider<S>? = null
|
||||||
|
internal lateinit var data: SubcommandData<*, S>
|
||||||
fun suggests(provider: SuggestionProvider<S>): CoreArgumentBuilder<S, T> {
|
fun suggests(provider: SuggestionProvider<S>): CoreArgumentBuilder<S, T> {
|
||||||
suggestionsProvider = provider
|
suggestionsProvider = provider
|
||||||
return this
|
return this
|
||||||
|
@ -29,12 +31,13 @@ class CoreArgumentBuilder<S, T>(
|
||||||
redirectModifier,
|
redirectModifier,
|
||||||
isFork,
|
isFork,
|
||||||
suggestionsProvider,
|
suggestionsProvider,
|
||||||
optional
|
optional,
|
||||||
|
data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun <S, T> argument(name: String, type: ArgumentType<T>, optional: Boolean): CoreArgumentBuilder<S, T> {
|
fun <S : Command2Sender, T> argument(name: String, type: ArgumentType<T>, optional: Boolean): CoreArgumentBuilder<S, T> {
|
||||||
return CoreArgumentBuilder(name, type, optional)
|
return CoreArgumentBuilder(name, type, optional)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,25 @@
|
||||||
package buttondevteam.lib.chat;
|
package buttondevteam.lib.chat
|
||||||
|
|
||||||
import com.mojang.brigadier.Command;
|
import buttondevteam.lib.chat.commands.SubcommandData
|
||||||
import com.mojang.brigadier.RedirectModifier;
|
import com.mojang.brigadier.Command
|
||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.RedirectModifier
|
||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
import com.mojang.brigadier.arguments.ArgumentType
|
||||||
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder
|
||||||
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
import com.mojang.brigadier.suggestion.SuggestionProvider
|
||||||
import com.mojang.brigadier.tree.CommandNode;
|
import com.mojang.brigadier.tree.ArgumentCommandNode
|
||||||
|
import com.mojang.brigadier.tree.CommandNode
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
import java.util.function.Predicate;
|
class CoreArgumentCommandNode<S : Command2Sender, T>(
|
||||||
|
name: String?, type: ArgumentType<T>?, command: Command<S>?, requirement: Predicate<S>?, redirect: CommandNode<S>?, modifier: RedirectModifier<S>?, forks: Boolean, customSuggestions: SuggestionProvider<S>?,
|
||||||
|
val optional: Boolean, val commandData: SubcommandData<*, S>
|
||||||
|
) :
|
||||||
|
ArgumentCommandNode<S, T>(name, type, command, requirement, redirect, modifier, forks, customSuggestions) {
|
||||||
|
override fun getUsageText(): String {
|
||||||
|
return if (optional) "[$name]" else "<$name>"
|
||||||
|
}
|
||||||
|
|
||||||
public class CoreArgumentCommandNode<S, T> extends ArgumentCommandNode<S, T> {
|
override fun createBuilder(): RequiredArgumentBuilder<S, T> {
|
||||||
private final boolean optional;
|
return super.createBuilder()
|
||||||
|
}
|
||||||
public CoreArgumentCommandNode(String name, ArgumentType<T> type, Command<S> command, Predicate<S> requirement, CommandNode<S> redirect, RedirectModifier<S> modifier, boolean forks, SuggestionProvider<S> customSuggestions, boolean optional) {
|
}
|
||||||
super(name, type, command, requirement, redirect, modifier, forks, customSuggestions);
|
|
||||||
this.optional = optional;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsageText() {
|
|
||||||
return optional ? "[" + getName() + "]" : "<" + getName() + ">";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RequiredArgumentBuilder<S, T> createBuilder() {
|
|
||||||
return super.createBuilder();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,9 +3,10 @@ package buttondevteam.lib.chat
|
||||||
import buttondevteam.lib.chat.commands.CommandArgument
|
import buttondevteam.lib.chat.commands.CommandArgument
|
||||||
import buttondevteam.lib.chat.commands.NoOpSubcommandData
|
import buttondevteam.lib.chat.commands.NoOpSubcommandData
|
||||||
import buttondevteam.lib.chat.commands.SubcommandData
|
import buttondevteam.lib.chat.commands.SubcommandData
|
||||||
|
import com.mojang.brigadier.builder.ArgumentBuilder
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
||||||
|
|
||||||
class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcommandData> private constructor(
|
class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<S>, TSD : NoOpSubcommandData> private constructor(
|
||||||
literal: String,
|
literal: String,
|
||||||
val data: TSD
|
val data: TSD
|
||||||
) : LiteralArgumentBuilder<S>(literal) {
|
) : LiteralArgumentBuilder<S>(literal) {
|
||||||
|
@ -30,6 +31,14 @@ class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcom
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun then(argument: ArgumentBuilder<S, *>): LiteralArgumentBuilder<S> {
|
||||||
|
if (argument is CoreArgumentBuilder<*, *> && data is SubcommandData<*, *>) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
(argument as CoreArgumentBuilder<S, *>).data = data as SubcommandData<*, S>
|
||||||
|
}
|
||||||
|
return super.then(argument)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Start building an executable command node.
|
* Start building an executable command node.
|
||||||
|
@ -44,7 +53,7 @@ class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcom
|
||||||
* @param annotations All annotations implemented by the method that executes the command
|
* @param annotations All annotations implemented by the method that executes the command
|
||||||
* @param fullPath The full command path of this subcommand.
|
* @param fullPath The full command path of this subcommand.
|
||||||
*/
|
*/
|
||||||
fun <S : Command2Sender, TC : ICommand2<*>> literal(
|
fun <S : Command2Sender, TC : ICommand2<S>> literal(
|
||||||
name: String,
|
name: String,
|
||||||
senderType: Class<*>,
|
senderType: Class<*>,
|
||||||
arguments: Map<String, CommandArgument>,
|
arguments: Map<String, CommandArgument>,
|
||||||
|
@ -67,7 +76,7 @@ class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcom
|
||||||
* @param name The subcommand name as written by the user
|
* @param name The subcommand name as written by the user
|
||||||
* @param helpTextGetter Custom help text that can depend on the context. The function receives the sender as the command itself receives it.
|
* @param helpTextGetter Custom help text that can depend on the context. The function receives the sender as the command itself receives it.
|
||||||
*/
|
*/
|
||||||
fun <S : Command2Sender, TC : ICommand2<*>> literalNoOp(
|
fun <S : Command2Sender, TC : ICommand2<S>> literalNoOp(
|
||||||
name: String,
|
name: String,
|
||||||
helpTextGetter: (Any) -> Array<String>,
|
helpTextGetter: (Any) -> Array<String>,
|
||||||
): CoreCommandBuilder<S, TC, NoOpSubcommandData> {
|
): CoreCommandBuilder<S, TC, NoOpSubcommandData> {
|
||||||
|
|
|
@ -4,11 +4,11 @@ package buttondevteam.lib.chat.commands
|
||||||
* A command argument's information to be used to construct the command.
|
* A command argument's information to be used to construct the command.
|
||||||
*/
|
*/
|
||||||
class CommandArgument(
|
class CommandArgument(
|
||||||
val name: String,
|
val name: String, // TODO: Remove <> from name and add it where appropriate
|
||||||
val type: Class<*>,
|
val type: Class<*>,
|
||||||
val greedy: Boolean,
|
val greedy: Boolean,
|
||||||
val limits: Pair<Double, Double>,
|
val limits: Pair<Double, Double>,
|
||||||
val optional: Boolean,
|
val optional: Boolean,
|
||||||
val description: String,
|
val description: String,
|
||||||
val annotations: Array<Annotation> // TODO: Annotations for parameters as well
|
val annotations: Array<Annotation>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package buttondevteam.lib.chat.commands
|
package buttondevteam.lib.chat.commands
|
||||||
|
|
||||||
import buttondevteam.lib.chat.Command2Sender
|
import buttondevteam.lib.chat.*
|
||||||
import buttondevteam.lib.chat.CoreCommandNode
|
import com.mojang.brigadier.builder.ArgumentBuilder
|
||||||
import buttondevteam.lib.chat.CoreExecutableNode
|
|
||||||
import buttondevteam.lib.chat.ICommand2
|
|
||||||
import com.mojang.brigadier.tree.CommandNode
|
import com.mojang.brigadier.tree.CommandNode
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -20,6 +18,15 @@ object CommandUtils {
|
||||||
.lowercase(Locale.getDefault())
|
.lowercase(Locale.getDefault())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the given action on the given node and all of its nodes recursively and creates new nodes.
|
||||||
|
*/
|
||||||
|
fun <S : Command2Sender> mapSubcommands(node: CommandNode<S>, action: (CommandNode<S>) -> ArgumentBuilder<S, *>): CommandNode<S> {
|
||||||
|
val newNode = action(node)
|
||||||
|
node.children.map { mapSubcommands(it, action) }.forEach(newNode::then)
|
||||||
|
return newNode.build()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Casts the node to whatever you say. Use responsibly.
|
* Casts the node to whatever you say. Use responsibly.
|
||||||
*/
|
*/
|
||||||
|
@ -35,4 +42,8 @@ object CommandUtils {
|
||||||
val ret = core<TP, NoOpSubcommandData>()
|
val ret = core<TP, NoOpSubcommandData>()
|
||||||
return if (ret.data is SubcommandData<*, *>) ret.core() else null
|
return if (ret.data is SubcommandData<*, *>) ret.core() else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <TP : Command2Sender> CommandNode<TP>.coreArgument(): CoreArgumentCommandNode<TP, *>? {
|
||||||
|
return if (this is CoreArgumentCommandNode<*, *>) this as CoreArgumentCommandNode<TP, *> else null
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue