Fixed getSubcommands() and other command fixes
- Command2MC tab completion still awaits - but then that might be all that's left before testing
This commit is contained in:
parent
30a2fe0b68
commit
8616ecbefc
12 changed files with 189 additions and 167 deletions
|
@ -14,7 +14,7 @@ import java.util.Optional;
|
|||
@CommandClass
|
||||
public class ChromaCommand extends ICommand2MC {
|
||||
public ChromaCommand() {
|
||||
manager.addParamConverter(ButtonPlugin.class, name ->
|
||||
getManager().addParamConverter(ButtonPlugin.class, name ->
|
||||
(ButtonPlugin) Optional.ofNullable(Bukkit.getPluginManager().getPlugin(name))
|
||||
.filter(plugin -> plugin instanceof ButtonPlugin).orElse(null),
|
||||
"No Chroma plugin found by that name.", () -> Arrays.stream(Bukkit.getPluginManager().getPlugins())
|
||||
|
|
|
@ -57,7 +57,7 @@ class MainPlugin : ButtonPlugin() {
|
|||
public override fun pluginEnable() {
|
||||
instance = this
|
||||
val pdf = description
|
||||
if (!setupPermissions()) throw NullPointerException("No permission plugin found!")
|
||||
setupPermissions()
|
||||
if (!setupEconomy()) //Though Essentials always provides economy, but we don't require Essentials
|
||||
logger.warning("No economy plugin found! Components using economy will not be registered.")
|
||||
saveConfig()
|
||||
|
@ -134,9 +134,8 @@ class MainPlugin : ButtonPlugin() {
|
|||
logger.info("Player data saved.")
|
||||
}
|
||||
|
||||
private fun setupPermissions(): Boolean {
|
||||
permission = setupProvider(Permission::class.java)
|
||||
return permission != null
|
||||
private fun setupPermissions() {
|
||||
permission = setupProvider(Permission::class.java) ?: throw NullPointerException("No permission plugin found!")
|
||||
}
|
||||
|
||||
private fun setupEconomy(): Boolean {
|
||||
|
@ -159,8 +158,7 @@ class MainPlugin : ButtonPlugin() {
|
|||
companion object {
|
||||
lateinit var instance: MainPlugin
|
||||
|
||||
@JvmField
|
||||
var permission: Permission? = null
|
||||
lateinit var permission: Permission
|
||||
|
||||
@JvmField
|
||||
var ess: Essentials? = null
|
||||
|
|
|
@ -42,7 +42,4 @@ class PrimeRestartCommand : ICommand2MC() {
|
|||
Bukkit.spigot().restart()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
}
|
||||
}
|
|
@ -77,7 +77,7 @@ abstract class Component<TP : JavaPlugin> {
|
|||
* @param command Custom coded command class
|
||||
*/
|
||||
fun registerCommand(command: ICommand2MC) {
|
||||
if (plugin is ButtonPlugin) command.registerToPlugin(plugin as ButtonPlugin)
|
||||
if (plugin is ButtonPlugin) command.registerToPlugin(plugin as ButtonPlugin) // TODO: Require ButtonPlugin
|
||||
command.registerToComponent(this)
|
||||
ButtonPlugin.command2MC.registerCommand(command)
|
||||
}
|
||||
|
@ -102,8 +102,7 @@ abstract class Component<TP : JavaPlugin> {
|
|||
*/
|
||||
fun getConfigMap(key: String, defaultProvider: Map<String, Consumer<IHaveConfig>>): Map<String, IHaveConfig> {
|
||||
val c: ConfigurationSection = config.config
|
||||
var cs = c.getConfigurationSection(key)
|
||||
if (cs == null) cs = c.createSection(key)
|
||||
val cs = c.getConfigurationSection(key) ?: c.createSection(key)
|
||||
val res = cs.getValues(false).entries.stream()
|
||||
.filter { (_, value) -> value is ConfigurationSection }
|
||||
.collect(Collectors.toMap(
|
||||
|
|
|
@ -133,13 +133,22 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
|||
* @param command The command to register
|
||||
* @return The Brigadier command node if you need it for something (like tab completion)
|
||||
*/
|
||||
protected fun registerCommandSuper(command: TC): LiteralCommandNode<TP> {
|
||||
var mainCommandNode: LiteralCommandNode<TP>? = null
|
||||
protected fun registerCommandSuper(command: TC): CoreCommandNode<TP, *> {
|
||||
var mainCommandNode: CoreCommandNode<TP, *>? = null
|
||||
for (meth in command.javaClass.methods) {
|
||||
val ann = meth.getAnnotation(Subcommand::class.java) ?: continue
|
||||
val fullPath = command.commandPath + CommandUtils.getCommandPath(meth.name, ' ')
|
||||
val (lastNode, mainNode, remainingPath) = registerNodeFromPath(fullPath)
|
||||
lastNode.addChild(getExecutableNode(meth, command, ann, remainingPath, CommandArgumentHelpManager(command), fullPath))
|
||||
lastNode.addChild(
|
||||
getExecutableNode(
|
||||
meth,
|
||||
command,
|
||||
ann,
|
||||
remainingPath,
|
||||
CommandArgumentHelpManager(command),
|
||||
fullPath
|
||||
)
|
||||
)
|
||||
if (mainCommandNode == null) mainCommandNode = mainNode
|
||||
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)
|
||||
|
@ -193,16 +202,17 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
|||
* @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)
|
||||
*/
|
||||
private fun registerNodeFromPath(path: String): Triple<CommandNode<TP>, LiteralCommandNode<TP>?, String> {
|
||||
private fun registerNodeFromPath(path: String): Triple<CommandNode<TP>, CoreCommandNode<TP, *>?, String> {
|
||||
val split = path.split(" ")
|
||||
var parent: CommandNode<TP> = dispatcher.root
|
||||
var mainCommand: LiteralCommandNode<TP>? = null
|
||||
var mainCommand: CoreCommandNode<TP, *>? = null
|
||||
split.forEachIndexed { i, part ->
|
||||
val child = parent.getChild(part)
|
||||
if (child == null) parent.addChild(CoreCommandBuilder.literalNoOp<TP, TC>(part, getSubcommandList())
|
||||
.executes(::executeHelpText).build().also { parent = it })
|
||||
else parent = child
|
||||
if (i == 0) mainCommand = parent as LiteralCommandNode<TP> // Has to be a literal, if not, well, error
|
||||
if (i == 0) mainCommand =
|
||||
parent as CoreCommandNode<TP, *> // Has to be our own literal node, if not, well, error
|
||||
}
|
||||
return Triple(parent, mainCommand, split.last())
|
||||
}
|
||||
|
@ -362,9 +372,9 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
|||
*
|
||||
* @return A set of command node objects containing the commands
|
||||
*/
|
||||
val commandNodes: Set<CoreCommandNode<TP, TC, NoOpSubcommandData>>
|
||||
val commandNodes: Set<CoreCommandNode<TP, NoOpSubcommandData>>
|
||||
get() = dispatcher.root.children.stream()
|
||||
.map { node: CommandNode<TP> -> node.core<TP, TC, NoOpSubcommandData>() }
|
||||
.map { node: CommandNode<TP> -> node.core<TP, NoOpSubcommandData>() }
|
||||
.collect(Collectors.toUnmodifiableSet())
|
||||
|
||||
/**
|
||||
|
@ -373,7 +383,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
|||
* @param command The exact name of the command
|
||||
* @return A command node
|
||||
*/
|
||||
fun getCommandNode(command: String): CoreCommandNode<TP, TC, NoOpSubcommandData> { // TODO: What should this return? No-op? Executable? What's the use case?
|
||||
fun getCommandNode(command: String): CoreCommandNode<TP, NoOpSubcommandData> { // TODO: What should this return? No-op? Executable? What's the use case?
|
||||
return dispatcher.root.getChild(command).core()
|
||||
}
|
||||
|
||||
|
@ -391,14 +401,14 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
|||
*
|
||||
* @param condition The condition for removing a given command
|
||||
*/
|
||||
fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, TC, SubcommandData<TC, TP>>>, nested: Boolean) {
|
||||
fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, SubcommandData<TC, TP>>>, nested: Boolean) {
|
||||
dispatcher.root.children.removeIf { node -> node.coreExecutable<TP, TC>()?.let { condition.test(it) } ?: false }
|
||||
if (nested) for (child in dispatcher.root.children) unregisterCommandIf(condition, child.core())
|
||||
}
|
||||
|
||||
private fun unregisterCommandIf(
|
||||
condition: Predicate<CoreCommandNode<TP, TC, SubcommandData<TC, TP>>>,
|
||||
root: CoreCommandNode<TP, TC, NoOpSubcommandData>
|
||||
condition: Predicate<CoreCommandNode<TP, SubcommandData<TC, TP>>>,
|
||||
root: CoreCommandNode<TP, NoOpSubcommandData>
|
||||
) {
|
||||
// TODO: Remvoe no-op nodes without children
|
||||
// Can't use getCoreChildren() here because the collection needs to be modifiable
|
||||
|
@ -408,8 +418,24 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
|||
|
||||
/**
|
||||
* Get all subcommands of the specified command. Only returns executable nodes.
|
||||
*
|
||||
* @param mainCommand The command to get the subcommands of
|
||||
* @param deep Whether to get all subcommands recursively or only the direct children
|
||||
*/
|
||||
fun getSubcommands(mainCommand: LiteralCommandNode<TP>): List<CoreCommandNode<TP, TC, SubcommandData<TC, TP>>> {
|
||||
return dispatcher.root.children.mapNotNull { it.coreExecutable<TP, TC>() } // TODO: Needs more depth
|
||||
fun getSubcommands(
|
||||
mainCommand: LiteralCommandNode<TP>,
|
||||
deep: Boolean = true
|
||||
): List<CoreCommandNode<TP, SubcommandData<TC, TP>>> {
|
||||
return getSubcommands(mainCommand, deep, mainCommand.core())
|
||||
}
|
||||
|
||||
private fun getSubcommands(
|
||||
mainCommand: LiteralCommandNode<TP>,
|
||||
deep: Boolean = true,
|
||||
root: CoreCommandNode<TP, NoOpSubcommandData>
|
||||
): List<CoreCommandNode<TP, SubcommandData<TC, TP>>> {
|
||||
|
||||
return root.children.mapNotNull { it.coreExecutable<TP, TC>() } +
|
||||
if (deep) root.children.flatMap { getSubcommands(mainCommand, deep, it.core()) } else emptyList()
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import buttondevteam.lib.TBMCCoreAPI
|
|||
import buttondevteam.lib.architecture.ButtonPlugin
|
||||
import buttondevteam.lib.architecture.Component
|
||||
import buttondevteam.lib.chat.commands.CommandUtils
|
||||
import buttondevteam.lib.chat.commands.CommandUtils.coreExecutable
|
||||
import buttondevteam.lib.chat.commands.MCCommandSettings
|
||||
import buttondevteam.lib.chat.commands.SubcommandData
|
||||
import buttondevteam.lib.player.ChromaGamerBase
|
||||
|
@ -29,7 +30,6 @@ import org.bukkit.entity.Player
|
|||
import org.bukkit.event.Listener
|
||||
import org.bukkit.permissions.Permission
|
||||
import org.bukkit.permissions.PermissionDefault
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Parameter
|
||||
import java.util.*
|
||||
import java.util.function.BiConsumer
|
||||
|
@ -43,42 +43,24 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
* @param command The command to register
|
||||
*/
|
||||
override fun registerCommand(command: ICommand2MC) {
|
||||
/*String mainpath;
|
||||
var plugin = command.getPlugin();
|
||||
{
|
||||
String cpath = command.getCommandPath();
|
||||
int i = cpath.indexOf(' ');
|
||||
mainpath = cpath.substring(0, i == -1 ? cpath.length() : i);
|
||||
}*/
|
||||
val commandNode = super.registerCommandSuper(command)
|
||||
val bcmd = registerOfficially(command, commandNode)
|
||||
if (bcmd != null) // TODO: Support aliases
|
||||
super.registerCommandSuper(command)
|
||||
val perm = "chroma.command." + command.commandPath.replace(' ', '.')
|
||||
if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset
|
||||
Bukkit.getPluginManager().addPermission(Permission(perm,
|
||||
PermissionDefault.TRUE)) //Allow commands by default, it will check mod-only
|
||||
for (node in getSubcommands(commandNode)) {
|
||||
if (path.length > 0) {
|
||||
val subperm = perm + path
|
||||
if (Bukkit.getPluginManager().getPermission(subperm) == null) //Check needed for plugin reset
|
||||
Bukkit.getPluginManager().addPermission(
|
||||
Permission(
|
||||
subperm,
|
||||
PermissionDefault.TRUE
|
||||
)
|
||||
) //Allow commands by default, it will check mod-only
|
||||
}
|
||||
val permPrefix = "chroma.command."
|
||||
//Allow commands by default, it will check mod-only
|
||||
val nodes = commandNode.coreExecutable<Command2MCSender, ICommand2MC>()
|
||||
?.let { getSubcommands(commandNode) + it } ?: getSubcommands(commandNode)
|
||||
for (node in nodes) {
|
||||
val subperm = permPrefix + node.data.fullPath.replace(' ', '.')
|
||||
if (Bukkit.getPluginManager().getPermission(subperm) == null) //Check needed for plugin reset
|
||||
Bukkit.getPluginManager().addPermission(Permission(subperm, PermissionDefault.TRUE))
|
||||
val pg = permGroup(node.data)
|
||||
if (pg.isEmpty()) continue
|
||||
val permGroup = "chroma.$pg"
|
||||
//Do not allow any commands that belong to a group by default
|
||||
if (Bukkit.getPluginManager().getPermission(permGroup) == null) //It may occur multiple times
|
||||
Bukkit.getPluginManager().addPermission(
|
||||
Permission(
|
||||
permGroup,
|
||||
PermissionDefault.OP
|
||||
)
|
||||
) //Do not allow any commands that belong to a group
|
||||
Bukkit.getPluginManager().addPermission(Permission(permGroup, PermissionDefault.OP))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +93,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
/**
|
||||
* Returns the first group found in the hierarchy starting from the command method **or** the mod group if *any* of the superclasses are mod only.
|
||||
*
|
||||
* @param method The subcommand to check
|
||||
* @param data The data of the subcommand to check
|
||||
* @return The permission group for the subcommand or empty string
|
||||
*/
|
||||
private fun permGroup(data: SubcommandData<ICommand2MC, Command2MCSender>): String {
|
||||
|
@ -170,7 +152,8 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
val i = commandline.indexOf(' ')
|
||||
val mainpath = commandline.substring(1, if (i == -1) commandline.length else i) //Without the slash
|
||||
//Our commands aren't PluginCommands, unless it's specified in the plugin.yml
|
||||
return if ((!checkPlugin || (MainPlugin.instance.prioritizeCustomCommands.get() == true))
|
||||
// So we need to handle the command if it's not a plugin command or if it's a plugin command, but for a ButtonPlugin
|
||||
return if (!checkPlugin || MainPlugin.instance.prioritizeCustomCommands.get()
|
||||
|| Bukkit.getPluginCommand(mainpath)?.let { it.plugin is ButtonPlugin } != false
|
||||
)
|
||||
super.handleCommand(sender, commandline) else false
|
||||
|
@ -178,28 +161,30 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
|
||||
private var shouldRegisterOfficially = true
|
||||
private fun registerOfficially(command: ICommand2MC, node: LiteralCommandNode<Command2MCSender>): Command? {
|
||||
return if (!shouldRegisterOfficially || command.plugin == null) null else try {
|
||||
val cmdmap = Bukkit.getServer().javaClass.getMethod("getCommandMap").invoke(Bukkit.getServer()) as SimpleCommandMap
|
||||
return if (!shouldRegisterOfficially) null else try {
|
||||
val cmdmap =
|
||||
Bukkit.getServer().javaClass.getMethod("getCommandMap").invoke(Bukkit.getServer()) as SimpleCommandMap
|
||||
val path = command.commandPath
|
||||
val x = path.indexOf(' ')
|
||||
val mainPath = path.substring(0, if (x == -1) path.length else x)
|
||||
var bukkitCommand: Command
|
||||
run {
|
||||
//Commands conflicting with Essentials have to be registered in plugin.yml
|
||||
val oldcmd = cmdmap.getCommand(command.plugin.name + ":" + mainPath) //The label with the fallback prefix is always registered
|
||||
if (oldcmd == null) {
|
||||
bukkitCommand = BukkitCommand(mainPath)
|
||||
cmdmap.register(command.plugin.name, bukkitCommand)
|
||||
} else {
|
||||
bukkitCommand = oldcmd
|
||||
if (bukkitCommand is PluginCommand) (bukkitCommand as PluginCommand).executor = CommandExecutor { sender: CommandSender, command: Command, label: String, args: Array<String> -> this.executeCommand(sender, command, label, args) }
|
||||
}
|
||||
bukkitCommand = oldcmd ?: BukkitCommand(mainPath)
|
||||
val bukkitCommand: Command
|
||||
//Commands conflicting with Essentials have to be registered in plugin.yml
|
||||
val oldcmd =
|
||||
cmdmap.getCommand(command.plugin.name + ":" + mainPath) //The label with the fallback prefix is always registered
|
||||
if (oldcmd == null) {
|
||||
bukkitCommand = BukkitCommand(mainPath)
|
||||
cmdmap.register(command.plugin.name, bukkitCommand)
|
||||
} else {
|
||||
bukkitCommand = oldcmd
|
||||
if (bukkitCommand is PluginCommand) bukkitCommand.setExecutor(this::executeCommand)
|
||||
}
|
||||
if (CommodoreProvider.isSupported()) TabcompleteHelper.registerTabcomplete(command, node, bukkitCommand)
|
||||
bukkitCommand
|
||||
} catch (e: Exception) {
|
||||
if (command.component == null) TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.plugin) else TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.component)
|
||||
if (command.component == null)
|
||||
TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.plugin)
|
||||
else
|
||||
TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.component)
|
||||
shouldRegisterOfficially = false
|
||||
null
|
||||
}
|
||||
|
@ -216,12 +201,14 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
sender.sendMessage("§cAn internal error occurred.")
|
||||
return true
|
||||
}
|
||||
///trim(): remove space if there are no args
|
||||
handleCommand(Command2MCSender(sender, user.channel.get(), sender),
|
||||
("/" + command.name + " " + java.lang.String.join(" ", *args)).trim { it <= ' ' }, false) ///trim(): remove space if there are no args
|
||||
("/${command.name} ${args.joinToString(" ")}").trim { it <= ' ' }, false
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
private class BukkitCommand(name: String?) : Command(name) {
|
||||
private class BukkitCommand(name: String) : Command(name) {
|
||||
override fun execute(sender: CommandSender, commandLabel: String, args: Array<String>): Boolean {
|
||||
return ButtonPlugin.command2MC.executeCommand(sender, this, commandLabel, args)
|
||||
}
|
||||
|
@ -232,20 +219,42 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
}
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
override fun tabComplete(sender: CommandSender, alias: String, args: Array<String>, location: Location): List<String> {
|
||||
return emptyList()
|
||||
override fun tabComplete(
|
||||
sender: CommandSender,
|
||||
alias: String,
|
||||
args: Array<out String>?,
|
||||
location: Location?
|
||||
): MutableList<String> {
|
||||
return mutableListOf()
|
||||
}
|
||||
}
|
||||
|
||||
private object TabcompleteHelper {
|
||||
private var commodore: Commodore? = null
|
||||
private fun appendSubcommand(path: String, parent: CommandNode<Any>,
|
||||
subcommand: SubcommandData<ICommand2MC>?): LiteralCommandNode<Any> {
|
||||
private val commodore: Commodore by lazy {
|
||||
val commodore = CommodoreProvider.getCommodore(MainPlugin.instance) //Register all to the Core, it's easier
|
||||
commodore.register(
|
||||
LiteralArgumentBuilder.literal<Any?>("un") // TODO: This is a test
|
||||
.redirect(
|
||||
RequiredArgumentBuilder.argument<Any?, String>(
|
||||
"unsomething",
|
||||
StringArgumentType.word()
|
||||
).suggests { _, builder ->
|
||||
builder.suggest("untest").buildFuture()
|
||||
}.build()
|
||||
)
|
||||
)
|
||||
commodore
|
||||
}
|
||||
|
||||
private fun appendSubcommand(
|
||||
path: String, parent: CommandNode<Any>,
|
||||
subcommand: SubcommandData<ICommand2MC>?
|
||||
): LiteralCommandNode<Any> {
|
||||
var scmd: LiteralCommandNode<Any>
|
||||
if (parent.getChild(path) as LiteralCommandNode<kotlin.Any?>?. also { scmd = it } != null) return scmd
|
||||
val scmdBuilder = LiteralArgumentBuilder.literal<Any>(path)
|
||||
if (subcommand != null) scmdBuilder.requires { o: Any? ->
|
||||
val sender = commodore!!.getBukkitSender(o)
|
||||
val sender = commodore.getBukkitSender(o)
|
||||
subcommand.hasPermission(sender)
|
||||
}
|
||||
scmd = scmdBuilder.build()
|
||||
|
@ -253,40 +262,33 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
return scmd
|
||||
}
|
||||
|
||||
private fun registerTabcomplete(command2MC: ICommand2MC, commandNode: LiteralCommandNode<Command2MCSender>, bukkitCommand: Command) {
|
||||
if (commodore == null) {
|
||||
commodore = CommodoreProvider.getCommodore(MainPlugin.instance) //Register all to the Core, it's easier
|
||||
commodore.register(LiteralArgumentBuilder.literal<Any?>("un")
|
||||
.redirect(RequiredArgumentBuilder.argument<Any?, String>(
|
||||
"unsomething",
|
||||
StringArgumentType.word()
|
||||
).suggests { context: CommandContext<Any?>?, builder: SuggestionsBuilder ->
|
||||
builder.suggest("untest").buildFuture()
|
||||
}.build()
|
||||
)
|
||||
)
|
||||
}
|
||||
commodore!!.dispatcher.root.getChild(commandNode.name) // TODO: Probably unnecessary
|
||||
val customTCmethods = Arrays.stream(command2MC.javaClass.declaredMethods) //val doesn't recognize the type arguments
|
||||
.flatMap { method: Method ->
|
||||
Optional.ofNullable(method.getAnnotation(CustomTabCompleteMethod::class.java)).stream()
|
||||
.flatMap { ctcmAnn: CustomTabCompleteMethod ->
|
||||
val paths = Optional.of<Array<String?>>(ctcmAnn.subcommand()).filter { s: Array<String?> -> s.size > 0 }
|
||||
.orElseGet {
|
||||
arrayOf(
|
||||
CommandUtils.getCommandPath(method.name, ' ').trim { it <= ' ' }
|
||||
)
|
||||
}
|
||||
Arrays.stream(paths).map { name: String? -> Triplet(name, ctcmAnn, method) }
|
||||
}
|
||||
}.toList()
|
||||
fun registerTabcomplete(
|
||||
command2MC: ICommand2MC,
|
||||
commandNode: LiteralCommandNode<Command2MCSender>,
|
||||
bukkitCommand: Command
|
||||
) {
|
||||
commodore.dispatcher.root.getChild(commandNode.name) // TODO: Probably unnecessary
|
||||
val customTCmethods =
|
||||
Arrays.stream(command2MC.javaClass.declaredMethods) //val doesn't recognize the type arguments
|
||||
.flatMap { method ->
|
||||
Optional.ofNullable(method.getAnnotation(CustomTabCompleteMethod::class.java)).stream()
|
||||
.flatMap { ctcmAnn ->
|
||||
val paths = Optional.of(ctcmAnn.subcommand).filter { s -> s.isNotEmpty() }
|
||||
.orElseGet {
|
||||
arrayOf(
|
||||
CommandUtils.getCommandPath(method.name, ' ').trim { it <= ' ' })
|
||||
}
|
||||
Arrays.stream(paths).map { name: String? -> Triple(name, ctcmAnn, method) }
|
||||
}
|
||||
}.toList()
|
||||
for (subcmd in subcmds) {
|
||||
val subpathAsOne = CommandUtils.getCommandPath(subcmd.method.getName(), ' ').trim { it <= ' ' }
|
||||
val subpath = subpathAsOne.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
var scmd: CommandNode<Any> = cmd
|
||||
if (subpath[0].length > 0) { //If the method is def, it will contain one empty string
|
||||
if (subpath[0].isNotEmpty()) { //If the method is def, it will contain one empty string
|
||||
for (s in subpath) {
|
||||
scmd = appendSubcommand(s, scmd, subcmd) //Add method name part of the path (could_be_multiple())
|
||||
scmd =
|
||||
appendSubcommand(s, scmd, subcmd) //Add method name part of the path (could_be_multiple())
|
||||
}
|
||||
}
|
||||
val parameters: Array<Parameter> = subcmd.method.getParameters()
|
||||
|
@ -294,12 +296,13 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
val parameter = parameters[i]
|
||||
val customParamType: Boolean
|
||||
// TODO: Arg type
|
||||
val param: Any = subcmd.parameters.get(i - 1)
|
||||
val param: String = subcmd.parameters.get(i - 1)
|
||||
val customTC = Optional.ofNullable(parameter.getAnnotation(CustomTabComplete::class.java))
|
||||
.map(Function<CustomTabComplete, Array<String>> { obj: CustomTabComplete -> obj.value() })
|
||||
val customTCmethod = customTCmethods.stream().filter { t: Triplet<String?, CustomTabCompleteMethod, Method> -> subpathAsOne.equals(t.value0, ignoreCase = true) }
|
||||
.filter { t: Triplet<String?, CustomTabCompleteMethod, Method> -> param.replaceAll("[\\[\\]<>]", "").equalsIgnoreCase(t.value1.param()) }
|
||||
.findAny()
|
||||
.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
|
||||
|
@ -310,8 +313,8 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
var ignoreCustomParamType = false
|
||||
if (customTCmethod.isPresent) {
|
||||
val tr = customTCmethod.get()
|
||||
if (tr.value1.ignoreTypeCompletion()) ignoreCustomParamType = true
|
||||
val method = tr.value2
|
||||
if (tr.second.ignoreTypeCompletion) ignoreCustomParamType = true
|
||||
val method = tr.third
|
||||
val params = method.parameters
|
||||
val args = arrayOfNulls<Any>(params.size)
|
||||
var j = 0
|
||||
|
@ -319,7 +322,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
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())
|
||||
args[j] = commodore.getBukkitSender(context.getSource())
|
||||
j++
|
||||
continue
|
||||
}
|
||||
|
@ -336,12 +339,18 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
k++ //Only increment if not CommandSender
|
||||
j++
|
||||
}
|
||||
if (args.size == 0 || args[args.size - 1] != null) { //Arguments filled entirely
|
||||
if (args.isEmpty() || args[args.size - 1] != null) { //Arguments filled entirely
|
||||
try {
|
||||
val suggestions = method.invoke(command2MC, *args)
|
||||
if (suggestions 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[].")
|
||||
} else if (suggestions is Array<String>) for (suggestion in suggestions as Array<String?>) builder.suggest(suggestion) else throw ClassCastException("Bad return type! It should return a String[] or an Iterable<String>.")
|
||||
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)
|
||||
|
@ -378,10 +387,12 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
val pluginName = command2MC.plugin.name.lowercase(Locale.getDefault())
|
||||
val prefixedcmd = LiteralArgumentBuilder.literal<Any>(pluginName + ":" + path.get(0))
|
||||
.redirect(maincmd).build()
|
||||
commodore!!.register(prefixedcmd)
|
||||
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())
|
||||
commodore.register(LiteralArgumentBuilder.literal<Any>(alias).redirect(maincmd).build())
|
||||
commodore.register(
|
||||
LiteralArgumentBuilder.literal<Any>("$pluginName:$alias").redirect(maincmd).build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -389,7 +400,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
|||
|
||||
companion object {
|
||||
private fun getParamConverter(cl: Class<*>, command2MC: ICommand2MC): ParamConverter<*>? {
|
||||
val converter = ButtonPlugin.getCommand2MC().paramConverters[cl]
|
||||
val converter = ButtonPlugin.command2MC.paramConverters[cl]
|
||||
if (converter == null) {
|
||||
val msg = "Could not find a suitable converter for type " + cl.simpleName
|
||||
val exception: Exception = NullPointerException("converter is null")
|
||||
|
|
|
@ -14,8 +14,8 @@ class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcom
|
|||
return this
|
||||
}
|
||||
|
||||
override fun build(): CoreCommandNode<S, TC, TSD> {
|
||||
val result = CoreCommandNode<_, TC, _>(
|
||||
override fun build(): CoreCommandNode<S, TSD> {
|
||||
val result = CoreCommandNode<_, _>(
|
||||
literal,
|
||||
command,
|
||||
requirement,
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package buttondevteam.lib.chat
|
||||
|
||||
import buttondevteam.lib.chat.commands.NoOpSubcommandData
|
||||
import buttondevteam.lib.chat.commands.SubcommandData
|
||||
import com.mojang.brigadier.Command
|
||||
import com.mojang.brigadier.RedirectModifier
|
||||
import com.mojang.brigadier.tree.CommandNode
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode
|
||||
import java.util.function.Predicate
|
||||
|
||||
class CoreCommandNode<T : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcommandData>(
|
||||
class CoreCommandNode<T : Command2Sender, TSD : NoOpSubcommandData>(
|
||||
literal: String,
|
||||
command: Command<T>,
|
||||
requirement: Predicate<T>,
|
||||
|
@ -16,3 +17,6 @@ class CoreCommandNode<T : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcomman
|
|||
forks: Boolean,
|
||||
val data: TSD
|
||||
) : LiteralCommandNode<T>(literal, command, requirement, redirect, modifier, forks)
|
||||
|
||||
typealias CoreExecutableNode<TP, TC> = CoreCommandNode<TP, SubcommandData<TC, TP>>
|
||||
typealias CoreNoOpNode<TP> = CoreCommandNode<TP, NoOpSubcommandData>
|
||||
|
|
|
@ -13,7 +13,7 @@ import java.util.function.Function
|
|||
*
|
||||
* @param TP The sender's type
|
||||
</TP> */
|
||||
abstract class ICommand2<TP : Command2Sender>(manager: Command2<*, TP>) {
|
||||
abstract class ICommand2<TP : Command2Sender>(val manager: Command2<*, TP>) {
|
||||
/**
|
||||
* Default handler for commands, can be used to copy the args too.
|
||||
*
|
||||
|
@ -21,7 +21,7 @@ abstract class ICommand2<TP : Command2Sender>(manager: Command2<*, TP>) {
|
|||
* @return The success of the command
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun def(sender: TP): Boolean {
|
||||
open fun def(sender: TP): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ abstract class ICommand2<TP : Command2Sender>(manager: Command2<*, TP>) {
|
|||
* @param message The message to send to the sender
|
||||
* @return Always true so that the usage isn't shown
|
||||
*/
|
||||
@Suppress("unused")
|
||||
protected fun respond(sender: TP, message: String): Boolean {
|
||||
sender.sendMessage(message)
|
||||
return true
|
||||
|
@ -49,23 +50,15 @@ abstract class ICommand2<TP : Command2Sender>(manager: Command2<*, TP>) {
|
|||
return if (ann.helpText.isNotEmpty() || cc == null) ann.helpText else cc.helpText //If cc is null then it's empty array
|
||||
}
|
||||
|
||||
private val path: String
|
||||
val manager: Command2<*, TP>
|
||||
open val commandPath: String
|
||||
/**
|
||||
* The command's path, or name if top-level command.<br></br>
|
||||
* For example:<br></br>
|
||||
* "u admin updateplugin" or "u" for the top level one<br></br>
|
||||
* <u>The path must be lowercase!</u><br></br>
|
||||
*
|
||||
* @return The command path, *which is the command class name by default* (removing any "command" from it) - Change via the [CommandClass] annotation
|
||||
*/
|
||||
get() = path
|
||||
|
||||
init {
|
||||
path = getcmdpath()
|
||||
this.manager = manager
|
||||
}
|
||||
/**
|
||||
* The command's path, or name if top-level command.<br></br>
|
||||
* For example:<br></br>
|
||||
* "u admin updateplugin" or "u" for the top level one<br></br>
|
||||
* <u>The path must be lowercase!</u><br></br>
|
||||
*
|
||||
* @return The command path, *which is the command class name by default* (removing any "command" from it) - Change via the [CommandClass] annotation
|
||||
*/
|
||||
open val commandPath: String = getcmdpath()
|
||||
|
||||
open val commandPaths: Array<String>
|
||||
/**
|
||||
|
@ -74,8 +67,7 @@ abstract class ICommand2<TP : Command2Sender>(manager: Command2<*, TP>) {
|
|||
*
|
||||
* @return The full command paths that this command should be registered under in addition to the default one.
|
||||
*/
|
||||
get() =// TODO: Deal with this (used for channel IDs)
|
||||
EMPTY_PATHS
|
||||
get() = EMPTY_PATHS // TODO: Deal with this (used for channel IDs)
|
||||
|
||||
private fun getcmdpath(): String {
|
||||
if (!javaClass.isAnnotationPresent(CommandClass::class.java)) throw RuntimeException(
|
||||
|
|
|
@ -7,14 +7,14 @@ import buttondevteam.lib.architecture.Component
|
|||
abstract class ICommand2MC : ICommand2<Command2MCSender>(command2MC) {
|
||||
private var _plugin: ButtonPlugin? = null
|
||||
var plugin: ButtonPlugin
|
||||
get() = _plugin ?: throw IllegalStateException("The command is not registered to a plugin!")
|
||||
get() = _plugin ?: throw IllegalStateException("The command is not registered to a Button plugin!")
|
||||
private set(value) {
|
||||
if (_plugin != null) throw IllegalStateException("The command is already assigned to a plugin!")
|
||||
if (_plugin != null) throw IllegalStateException("The command is already assigned to a Button plugin!")
|
||||
_plugin = value
|
||||
}
|
||||
private var _component: Component<*>? = null
|
||||
var component: Component<*>
|
||||
get() = _component ?: throw IllegalStateException("The command is not registered to a component!")
|
||||
var component: Component<*>?
|
||||
get() = _component
|
||||
private set(value) {
|
||||
if (_component != null) throw IllegalStateException("The command is already assigned to a component!")
|
||||
_component = value
|
||||
|
|
|
@ -2,8 +2,8 @@ package buttondevteam.lib.chat.commands
|
|||
|
||||
import buttondevteam.lib.chat.Command2Sender
|
||||
import buttondevteam.lib.chat.CoreCommandNode
|
||||
import buttondevteam.lib.chat.CoreExecutableNode
|
||||
import buttondevteam.lib.chat.ICommand2
|
||||
import com.mojang.brigadier.context.CommandContext
|
||||
import com.mojang.brigadier.tree.CommandNode
|
||||
import java.util.*
|
||||
|
||||
|
@ -24,20 +24,15 @@ object CommandUtils {
|
|||
* Casts the node to whatever you say. Use responsibly.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <TP : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcommandData> CommandNode<TP>.core(): CoreCommandNode<TP, TC, TSD> {
|
||||
return this as CoreCommandNode<TP, TC, TSD>
|
||||
fun <TP : Command2Sender, TSD : NoOpSubcommandData> CommandNode<TP>.core(): CoreCommandNode<TP, TSD> {
|
||||
return this as CoreCommandNode<TP, TSD>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node as an executable core command node or returns null if it's a no-op node.
|
||||
*/
|
||||
fun <TP : Command2Sender, TC : ICommand2<*>> CommandNode<TP>.coreExecutable(): CoreCommandNode<TP, TC, SubcommandData<TC, TP>>? {
|
||||
val ret = core<TP, TC, NoOpSubcommandData>()
|
||||
fun <TP : Command2Sender, TC : ICommand2<*>> CommandNode<TP>.coreExecutable(): CoreExecutableNode<TP, TC>? {
|
||||
val ret = core<TP, NoOpSubcommandData>()
|
||||
return if (ret.data is SubcommandData<*, *>) ret.core() else null
|
||||
}
|
||||
|
||||
val <TP : Command2Sender> CommandContext<TP>.subcommandPath
|
||||
get(): String {
|
||||
TODO("Return command path")
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ class SubcommandData<TC : ICommand2<*>, TP : Command2Sender>(
|
|||
*/
|
||||
val annotations: Array<Annotation>,
|
||||
/**
|
||||
* The full command path of this subcommand.
|
||||
* The space-separated full command path of this subcommand.
|
||||
*/
|
||||
val fullPath: String
|
||||
) : NoOpSubcommandData(helpTextGetter) {
|
||||
|
|
Loading…
Reference in a new issue