Convert some more code to Kotlin

Basically done with converting Command2
Also moved the Minecraft part of the param converter to Command2MC, although the user object getter should be made more generic
This commit is contained in:
Norbi Peti 2023-02-21 00:41:23 +01:00
parent 0bf1f9789b
commit a5099a65d1
10 changed files with 132 additions and 124 deletions

View file

@ -72,7 +72,6 @@
<artifactSet> <artifactSet>
<includes> <includes>
<include>me.lucko:commodore</include> <include>me.lucko:commodore</include>
<include>org.javatuples:javatuples</include>
</includes> </includes>
</artifactSet> </artifactSet>
<relocations> <relocations>
@ -226,11 +225,6 @@
<version>1.11</version> <version>1.11</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.javatuples</groupId>
<artifactId>javatuples</artifactId>
<version>1.2</version>
</dependency>
<dependency> <dependency>
<groupId>org.jetbrains.kotlin</groupId> <groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId> <artifactId>kotlin-stdlib</artifactId>

View file

@ -106,7 +106,7 @@ public class MainPlugin extends ButtonPlugin {
registerCommand(new ComponentCommand()); registerCommand(new ComponentCommand());
registerCommand(new ChromaCommand()); registerCommand(new ChromaCommand());
TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this); TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this);
TBMCCoreAPI.RegisterEventsForExceptions(getCommand2MC(), this); TBMCCoreAPI.RegisterEventsForExceptions(Companion.getCommand2MC(), this);
ChromaGamerBase.addConverter(commandSender -> Optional.ofNullable(commandSender instanceof ConsoleCommandSender || commandSender instanceof BlockCommandSender ChromaGamerBase.addConverter(commandSender -> Optional.ofNullable(commandSender instanceof ConsoleCommandSender || commandSender instanceof BlockCommandSender
? TBMCPlayer.getPlayer(new UUID(0, 0), TBMCPlayer.class) : null)); //Console & cmdblocks ? TBMCPlayer.getPlayer(new UUID(0, 0), TBMCPlayer.class) : null)); //Console & cmdblocks
ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof Player ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof Player
@ -125,8 +125,8 @@ public class MainPlugin extends ButtonPlugin {
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§bBLUE§f", Color.Blue, "blue")); TBMCChatAPI.RegisterChatChannel(new ChatRoom("§bBLUE§f", Color.Blue, "blue"));
TBMCChatAPI.RegisterChatChannel(new ChatRoom("§5PURPLE§f", Color.DarkPurple, "purple")); TBMCChatAPI.RegisterChatChannel(new ChatRoom("§5PURPLE§f", Color.DarkPurple, "purple"));
Supplier<Iterable<String>> playerSupplier = () -> Bukkit.getOnlinePlayers().stream().map(HumanEntity::getName)::iterator; Supplier<Iterable<String>> playerSupplier = () -> Bukkit.getOnlinePlayers().stream().map(HumanEntity::getName)::iterator;
getCommand2MC().addParamConverter(OfflinePlayer.class, Bukkit::getOfflinePlayer, "Player not found!", playerSupplier); Companion.getCommand2MC().addParamConverter(OfflinePlayer.class, Bukkit::getOfflinePlayer, "Player not found!", playerSupplier);
getCommand2MC().addParamConverter(Player.class, Bukkit::getPlayer, "Online player not found!", playerSupplier); Companion.getCommand2MC().addParamConverter(Player.class, Bukkit::getPlayer, "Online player not found!", playerSupplier);
if (writePluginList.get()) { if (writePluginList.get()) {
try { try {
Files.write(new File("plugins", "plugins.txt").toPath(), Arrays.stream(Bukkit.getPluginManager().getPlugins()).map(p -> (CharSequence) p.getDataFolder().getName())::iterator); Files.write(new File("plugins", "plugins.txt").toPath(), Arrays.stream(Bukkit.getPluginManager().getPlugins()).map(p -> (CharSequence) p.getDataFolder().getName())::iterator);

View file

@ -5,8 +5,6 @@ import buttondevteam.core.ComponentManager
import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.Component.Companion.updateConfig import buttondevteam.lib.architecture.Component.Companion.updateConfig
import buttondevteam.lib.chat.Command2MC import buttondevteam.lib.chat.Command2MC
import buttondevteam.lib.chat.Command2MC.registerCommand
import buttondevteam.lib.chat.Command2MC.unregisterCommands
import buttondevteam.lib.chat.ICommand2MC import buttondevteam.lib.chat.ICommand2MC
import lombok.AccessLevel import lombok.AccessLevel
import lombok.Getter import lombok.Getter
@ -22,8 +20,7 @@ import java.util.function.Function
@HasConfig(global = true) @HasConfig(global = true)
abstract class ButtonPlugin : JavaPlugin() { abstract class ButtonPlugin : JavaPlugin() {
@Getter(AccessLevel.PROTECTED) protected val iConfig = IHaveConfig { saveConfig() }
private val iConfig = IHaveConfig { saveConfig() }
private var yaml: CommentedConfiguration? = null private var yaml: CommentedConfiguration? = null
@Getter(AccessLevel.PROTECTED) @Getter(AccessLevel.PROTECTED)
@ -74,7 +71,7 @@ abstract class ButtonPlugin : JavaPlugin() {
ComponentManager.unregComponents(this) ComponentManager.unregComponents(this)
pluginDisable() pluginDisable()
if (ConfigData.saveNow(config)) logger.info("Saved configuration changes.") if (ConfigData.saveNow(config)) logger.info("Saved configuration changes.")
ButtonPlugin.getCommand2MC().unregisterCommands(this) command2MC.unregisterCommands(this)
} catch (e: Exception) { } catch (e: Exception) {
TBMCCoreAPI.SendException("Error while disabling plugin $name!", e, this) TBMCCoreAPI.SendException("Error while disabling plugin $name!", e, this)
} }
@ -122,7 +119,7 @@ abstract class ButtonPlugin : JavaPlugin() {
override fun getConfig(): FileConfiguration { override fun getConfig(): FileConfiguration {
if (yaml == null) justReload() if (yaml == null) justReload()
return if (yaml == null) YamlConfiguration() else yaml //Return a temporary instance return yaml ?: YamlConfiguration() //Return a temporary instance
} }
override fun saveConfig() { override fun saveConfig() {
@ -140,16 +137,15 @@ abstract class ButtonPlugin : JavaPlugin() {
*/ */
fun registerCommand(command: ICommand2MC) { fun registerCommand(command: ICommand2MC) {
command.registerToPlugin(this) command.registerToPlugin(this)
ButtonPlugin.getCommand2MC().registerCommand(command) command2MC.registerCommand(command)
} }
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)
annotation class ConfigOpts(val disableConfigGen: Boolean = false) annotation class ConfigOpts(val disableConfigGen: Boolean = false)
companion object { companion object {
@Getter //Needs to be static as we don't know the plugin when a command is handled //Needs to be static as we don't know the plugin when a command is handled
val command2MC = Command2MC()
private val command2MC = Command2MC()
fun configGenAllowed(obj: Any): Boolean { fun configGenAllowed(obj: Any): Boolean {
return !Optional.ofNullable(obj.javaClass.getAnnotation(ConfigOpts::class.java)) return !Optional.ofNullable(obj.javaClass.getAnnotation(ConfigOpts::class.java))
.map(Function<ConfigOpts, Boolean> { obj: ConfigOpts -> obj.disableConfigGen() }).orElse(false) .map(Function<ConfigOpts, Boolean> { obj: ConfigOpts -> obj.disableConfigGen() }).orElse(false)

View file

@ -2,9 +2,8 @@ package buttondevteam.lib.chat
import buttondevteam.core.MainPlugin import buttondevteam.core.MainPlugin
import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.chat.Command2.Subcommand
import buttondevteam.lib.chat.commands.* import buttondevteam.lib.chat.commands.*
import buttondevteam.lib.player.ChromaGamerBase import buttondevteam.lib.chat.commands.CommandUtils.core
import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.* import com.mojang.brigadier.arguments.*
import com.mojang.brigadier.builder.ArgumentBuilder import com.mojang.brigadier.builder.ArgumentBuilder
@ -15,8 +14,6 @@ import com.mojang.brigadier.tree.CommandNode
import com.mojang.brigadier.tree.LiteralCommandNode import com.mojang.brigadier.tree.LiteralCommandNode
import lombok.RequiredArgsConstructor import lombok.RequiredArgsConstructor
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.javatuples.Pair
import org.javatuples.Triplet
import java.lang.reflect.Method import java.lang.reflect.Method
import java.util.function.Function import java.util.function.Function
import java.util.function.Predicate import java.util.function.Predicate
@ -90,7 +87,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
</T> */ </T> */
open fun <T> addParamConverter(cl: Class<T>, converter: Function<String, T>, errormsg: String, open fun <T> addParamConverter(cl: Class<T>, converter: Function<String, T>, errormsg: String,
allSupplier: Supplier<Iterable<String>>) { allSupplier: Supplier<Iterable<String>>) {
paramConverters[cl] = ParamConverter<T>(converter, errormsg, allSupplier) paramConverters[cl] = ParamConverter(converter, errormsg, allSupplier)
} }
open fun handleCommand(sender: TP, commandline: String): Boolean { open fun handleCommand(sender: TP, commandline: String): Boolean {
@ -112,22 +109,10 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
} }
//TODO: Add to the help //TODO: Add to the help
private fun processSenderType(sender: TP, sd: SubcommandData<TC, TP>, params: ArrayList<Any>): Boolean { open fun convertSenderType(sender: TP, senderType: Class<*>): Any? {
val sendertype = sd.senderType //The command either expects a CommandSender or it is a Player, or some other expected type
val cg: ChromaGamerBase if (senderType.isAssignableFrom(sender.javaClass)) return sender
if (sendertype.isAssignableFrom(sender.javaClass)) params.add(sender) //The command either expects a CommandSender or it is a Player, or some other expected type return null
else if (sender is Command2MCSender // TODO: This is Minecraft only
&& sendertype.isAssignableFrom((sender as Command2MCSender).sender.javaClass))
params.add((sender as Command2MCSender).sender)
else if ((ChromaGamerBase::class.java.isAssignableFrom(sendertype) && sender is Command2MCSender)
&& ChromaGamerBase.getFromSender((sender as Command2MCSender).sender).also { cg = it } != null && cg.javaClass == sendertype) //The command expects a user of our system
params.add(cg) else {
val type = sendertype.simpleName.fold("") { s, ch -> s + if (ch.isUpperCase()) " " + ch.lowercase() else ch }
sender.sendMessage("§cYou need to be a $type to use this command.")
sender.sendMessage(sd.getHelpText(sender)) //Send what the command is about, could be useful for commands like /member where some subcommands aren't player-only
return true
}
return false
} }
/** /**
@ -146,17 +131,18 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
*/ */
protected fun registerCommandSuper(command: TC): LiteralCommandNode<TP> { protected fun registerCommandSuper(command: TC): LiteralCommandNode<TP> {
var mainCommandNode: LiteralCommandNode<TP>? = null var mainCommandNode: LiteralCommandNode<TP>? = null
for (meth in command.javaClass.getMethods()) { for (meth in command.javaClass.methods) {
val ann = meth.getAnnotation<Subcommand>(Subcommand::class.java) ?: continue val ann = meth.getAnnotation(Subcommand::class.java) ?: continue
val methodPath = CommandUtils.getCommandPath(meth.name, ' ') val methodPath = CommandUtils.getCommandPath(meth.name, ' ')
val result = registerNodeFromPath(command!!.commandPath + methodPath) val (lastNode, mainNode, remainingPath) = registerNodeFromPath(command.commandPath + methodPath)
result.value0.addChild(getExecutableNode(meth, command, ann, result.value2, CommandArgumentHelpManager(command))) lastNode.addChild(getExecutableNode(meth, command, ann, remainingPath, CommandArgumentHelpManager(command)))
if (mainCommandNode == null) mainCommandNode = result.value1 else if (result.value1!!.name != mainCommandNode.name) { 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) MainPlugin.Instance.logger.warning("Multiple commands are defined in the same class! This is not supported. Class: " + command.javaClass.simpleName)
} }
} }
if (mainCommandNode == null) { if (mainCommandNode == null) {
throw RuntimeException("There are no subcommands defined in the command class " + command.javaClass.getSimpleName() + "!") throw RuntimeException("There are no subcommands defined in the command class " + command.javaClass.simpleName + "!")
} }
return mainCommandNode return mainCommandNode
} }
@ -170,18 +156,18 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
* @return The executable node * @return The executable node
*/ */
private fun getExecutableNode(method: Method, command: TC, ann: Subcommand, path: String, argHelpManager: CommandArgumentHelpManager<TC, TP>): LiteralCommandNode<TP> { private fun getExecutableNode(method: Method, command: TC, ann: Subcommand, path: String, argHelpManager: CommandArgumentHelpManager<TC, TP>): LiteralCommandNode<TP> {
val paramsAndSenderType = getCommandParametersAndSender(method, argHelpManager) // Param order is important val (params, _) = getCommandParametersAndSender(method, argHelpManager) // Param order is important
val params = paramsAndSenderType.value0
val paramMap = HashMap<String, CommandArgument?>() val paramMap = HashMap<String, CommandArgument?>()
for (param in params) { for (param in params) {
paramMap[param!!.name] = param paramMap[param.name] = param
} }
val node = CoreCommandBuilder.literal<TP, TC>(path, params[0]!!.type, paramMap, params, command) val node = CoreCommandBuilder.literal<TP, TC>(path, params[0].type, paramMap, params, command)
.helps(command!!.getHelpText(method, ann)).permits { sender: TP -> hasPermission(sender, command, method) } .helps(command.getHelpText(method, ann)).permits { sender: TP -> hasPermission(sender, command, method) }
.executes { context: CommandContext<TP> -> executeCommand(context) } .executes { context: CommandContext<TP> -> executeCommand(context) }
var parent: ArgumentBuilder<TP, *> = node var parent: ArgumentBuilder<TP, *> = node
for (param in params) { // Register parameters in the right order for (param in params) { // Register parameters in the right order
parent.then(CoreArgumentBuilder.argument(param!!.name, getArgumentType(param), param.optional).also { parent = it }) val argType = getArgumentType(param)
parent.then(CoreArgumentBuilder.argument<TP, _>(param.name, argType, param.optional).also { parent = it })
} }
return node.build() return node.build()
} }
@ -193,7 +179,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): Triplet<CommandNode<TP>, LiteralCommandNode<TP>?, String> { private fun registerNodeFromPath(path: String): Triple<CommandNode<TP>, LiteralCommandNode<TP>?, String> {
val split = path.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val split = path.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
var parent: CommandNode<TP> = dispatcher.root var parent: CommandNode<TP> = dispatcher.root
var mainCommand: LiteralCommandNode<TP>? = null var mainCommand: LiteralCommandNode<TP>? = null
@ -203,7 +189,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
if (child == null) parent.addChild(CoreCommandBuilder.literalNoOp<TP, TC>(part).executes { context: CommandContext<TP> -> executeHelpText(context) }.build().also { parent = it }) else parent = child if (child == null) parent.addChild(CoreCommandBuilder.literalNoOp<TP, TC>(part).executes { context: CommandContext<TP> -> executeHelpText(context) }.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 LiteralCommandNode<TP> // Has to be a literal, if not, well, error
} }
return Triplet(parent, mainCommand, split[split.size - 1]) return Triple(parent, mainCommand, split[split.size - 1])
} }
/** /**
@ -214,21 +200,20 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
* @return Parameter data objects and the sender type * @return Parameter data objects and the sender type
* @throws RuntimeException If there is no sender parameter declared in the method * @throws RuntimeException If there is no sender parameter declared in the method
*/ */
private fun getCommandParametersAndSender(method: Method, argHelpManager: CommandArgumentHelpManager<TC, TP>): Pair<Array<CommandArgument?>, Class<*>> { private fun getCommandParametersAndSender(method: Method, argHelpManager: CommandArgumentHelpManager<TC, TP>): Pair<List<CommandArgument>, Class<*>> {
val parameters = method.parameters val parameters = method.parameters
if (parameters.size == 0) throw RuntimeException("No sender parameter for method '$method'") if (parameters.isEmpty()) throw RuntimeException("No sender parameter for method '$method'")
val ret = arrayOfNulls<CommandArgument>(parameters.size)
val usage = argHelpManager.getParameterHelpForMethod(method) val usage = argHelpManager.getParameterHelpForMethod(method)
val paramNames = usage?.split(" ".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray() val paramNames = usage?.split(" ")
for (i in 1 until parameters.size) { return Pair(parameters.zip(paramNames ?: (1 until parameters.size).map { i -> "param$i" })
val numAnn = parameters[i].getAnnotation(NumberArg::class.java) .map { (param, name) ->
ret[i - 1] = CommandArgument(paramNames?.get(i) ?: "param$i", parameters[i].type, val numAnn = param.getAnnotation(NumberArg::class.java)
parameters[i].isVarArgs || parameters[i].isAnnotationPresent(TextArg::class.java), CommandArgument(name, param.type,
if (numAnn == null) null else Pair(numAnn.lowerLimit(), numAnn.upperLimit()), param.isVarArgs || param.isAnnotationPresent(TextArg::class.java),
parameters[i].isAnnotationPresent(OptionalArg::class.java), if (numAnn == null) Pair(Double.MIN_VALUE, Double.MAX_VALUE) else Pair(numAnn.lowerLimit, numAnn.upperLimit),
paramNames?.get(i) ?: "param$i") // TODO: Description (JavaDoc?) param.isAnnotationPresent(OptionalArg::class.java),
} name)
return Pair(ret, parameters[0].type) }, parameters[0].type)
} }
/** /**
@ -238,13 +223,24 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
* @param arg Our representation of the command argument * @param arg Our representation of the command argument
* @return The Brigadier representation of the command argument * @return The Brigadier representation of the command argument
*/ */
private fun getArgumentType(arg: CommandArgument?): ArgumentType<*> { private fun getArgumentType(arg: CommandArgument?): ArgumentType<out Any> {
val ptype = arg!!.type val ptype = arg!!.type
val lowerLimit: Number = arg.limits.value0 val (lowerLimit, upperLimit) = arg.limits
val upperLimit: Number = arg.limits.value1 return if (arg.greedy) StringArgumentType.greedyString()
return if (arg.greedy) StringArgumentType.greedyString() else if (ptype == String::class.java) StringArgumentType.word() else if (ptype == Int::class.javaPrimitiveType || ptype == Int::class.java || ptype == Byte::class.javaPrimitiveType || ptype == Byte::class.java || ptype == Short::class.javaPrimitiveType || ptype == Short::class.java) IntegerArgumentType.integer(lowerLimit.toInt(), upperLimit.toInt()) else if (ptype == Long::class.javaPrimitiveType || ptype == Long::class.java) LongArgumentType.longArg(lowerLimit.toLong(), upperLimit.toLong()) else if (ptype == Float::class.javaPrimitiveType || ptype == Float::class.java) FloatArgumentType.floatArg(lowerLimit.toFloat(), upperLimit.toFloat()) else if (ptype == Double::class.javaPrimitiveType || ptype == Double::class.java) DoubleArgumentType.doubleArg(lowerLimit.toDouble(), upperLimit.toDouble()) else if (ptype == Char::class.javaPrimitiveType || ptype == Char::class.java) StringArgumentType.word() else if (ptype == Boolean::class.javaPrimitiveType || ptype == Boolean::class.java) BoolArgumentType.bool() else { else if (ptype == String::class.java) StringArgumentType.word()
StringArgumentType.word() else if (ptype == Int::class.javaPrimitiveType || ptype == Int::class.java
} || ptype == Byte::class.javaPrimitiveType || ptype == Byte::class.java
|| ptype == Short::class.javaPrimitiveType || ptype == Short::class.java)
IntegerArgumentType.integer(lowerLimit.toInt(), upperLimit.toInt())
else if (ptype == Long::class.javaPrimitiveType || ptype == Long::class.java)
LongArgumentType.longArg(lowerLimit.toLong(), upperLimit.toLong())
else if (ptype == Float::class.javaPrimitiveType || ptype == Float::class.java)
FloatArgumentType.floatArg(lowerLimit.toFloat(), upperLimit.toFloat())
else if (ptype == Double::class.javaPrimitiveType || ptype == Double::class.java)
DoubleArgumentType.doubleArg(lowerLimit, upperLimit)
else if (ptype == Char::class.javaPrimitiveType || ptype == Char::class.java) StringArgumentType.word()
else if (ptype == Boolean::class.javaPrimitiveType || ptype == Boolean::class.java) BoolArgumentType.bool()
else StringArgumentType.word()
} }
/** /**
@ -277,6 +273,11 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
return; return;
} }
// TODO: WIP // TODO: WIP
val type = sendertype.simpleName.fold("") { s, ch -> s + if (ch.isUpperCase()) " " + ch.lowercase() else ch }
sender.sendMessage("§cYou need to be a $type to use this command.")
sender.sendMessage(sd.getHelpText(sender)) //Send what the command is about, could be useful for commands like /member where some subcommands aren't player-only
if (processSenderType(sender, sd, params, parameterTypes)) return; // Checks if the sender is the wrong type if (processSenderType(sender, sd, params, parameterTypes)) return; // Checks if the sender is the wrong type
val args = parsed.getContext().getArguments(); val args = parsed.getContext().getArguments();
for (var arg : sd.arguments.entrySet()) {*/ for (var arg : sd.arguments.entrySet()) {*/
@ -325,16 +326,15 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
} }
abstract fun hasPermission(sender: TP, command: TC, subcommand: Method?): Boolean abstract fun hasPermission(sender: TP, command: TC, subcommand: Method?): Boolean
val commandsText: Array<String> val commandsText: Array<String> get() = commandHelp.toTypedArray()
get() = commandHelp.toTypedArray()
/** /**
* Get all registered command nodes. This returns all registered Chroma commands with all the information about them. * Get all registered command nodes. This returns all registered Chroma commands with all the information about them.
* *
* @return A set of command node objects containing the commands * @return A set of command node objects containing the commands
*/ */
val commandNodes: Set<CoreCommandNode<TP, TC?>?> val commandNodes: Set<CoreCommandNode<TP, TC>>
get() = dispatcher.root.children.stream().map { node: CommandNode<TP>? -> node as CoreCommandNode<TP, TC?>? }.collect(Collectors.toUnmodifiableSet()) get() = dispatcher.root.children.stream().map { node: CommandNode<TP> -> node.core<TP, TC>() }.collect(Collectors.toUnmodifiableSet())
/** /**
* Get a node that belongs to the given command. * Get a node that belongs to the given command.
@ -343,7 +343,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
* @return A command node * @return A command node
*/ */
fun getCommandNode(command: String?): CoreCommandNode<TP, TC> { fun getCommandNode(command: String?): CoreCommandNode<TP, TC> {
return dispatcher.root.getChild(command) as CoreCommandNode<TP, TC> return dispatcher.root.getChild(command).core()
} }
/** /**
@ -352,7 +352,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
* @param command The command class (object) to unregister * @param command The command class (object) to unregister
*/ */
fun unregisterCommand(command: ICommand2<TP>) { fun unregisterCommand(command: ICommand2<TP>) {
dispatcher.root.children.removeIf { node: CommandNode<TP> -> (node as CoreCommandNode<TP, TC>).data.command === command } dispatcher.root.children.removeIf { node: CommandNode<TP> -> node.core<TP, TC>().data.command === command }
} }
/** /**
@ -360,14 +360,14 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
* *
* @param condition The condition for removing a given command * @param condition The condition for removing a given command
*/ */
fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, TC>?>, nested: Boolean) { fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, TC>>, nested: Boolean) {
dispatcher.root.children.removeIf { node: CommandNode<TP>? -> condition.test(node as CoreCommandNode<TP, TC>?) } dispatcher.root.children.removeIf { node: CommandNode<TP> -> condition.test(node.core()) }
if (nested) for (child in dispatcher.root.children) unregisterCommandIf(condition, child as CoreCommandNode<TP, TC>) if (nested) for (child in dispatcher.root.children) unregisterCommandIf(condition, child.core())
} }
private fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, TC>?>, root: CoreCommandNode<TP, TC>) { private fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, TC>>, root: CoreCommandNode<TP, TC>) {
// Can't use getCoreChildren() here because the collection needs to be modifiable // Can't use getCoreChildren() here because the collection needs to be modifiable
root.children.removeIf { node: CommandNode<TP>? -> condition.test(node as CoreCommandNode<TP, TC>?) } root.children.removeIf { node: CommandNode<TP> -> condition.test(node.core()) }
for (child in root.coreChildren) unregisterCommandIf(condition, child) for (child in root.coreChildren) unregisterCommandIf(condition, child)
} }
} }

View file

@ -28,7 +28,6 @@ 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 org.javatuples.Triplet
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.Parameter import java.lang.reflect.Parameter
import java.util.* import java.util.*
@ -36,7 +35,7 @@ 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
class Command2MC : Command2<ICommand2MC?, Command2MCSender?>('/', true), Listener { class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener {
/** /**
* Don't use directly, use the method in Component and ButtonPlugin to automatically unregister the command when needed. * Don't use directly, use the method in Component and ButtonPlugin to automatically unregister the command when needed.
* *
@ -149,8 +148,25 @@ class Command2MC : Command2<ICommand2MC?, Command2MCSender?>('/', true), Listene
super.addParamConverter(cl, converter, "§c$errormsg", allSupplier) super.addParamConverter(cl, converter, "§c$errormsg", allSupplier)
} }
override fun convertSenderType(sender: Command2MCSender, senderType: Class<*>): Any? {
val original = super.convertSenderType(sender, senderType)
if (original != null) {
return original
}
// Check Bukkit sender type
if (senderType.isAssignableFrom(sender.sender.javaClass))
return sender.sender
//The command expects a user of our system
if (ChromaGamerBase::class.java.isAssignableFrom(senderType)) {
val cg = ChromaGamerBase.getFromSender(sender.sender)
if (cg?.javaClass == senderType)
return cg
}
return null
}
fun unregisterCommands(plugin: ButtonPlugin) { fun unregisterCommands(plugin: ButtonPlugin) {
unregisterCommandIf({ node: CoreCommandNode<Command2MCSender?, ICommand2MC?> -> Optional.ofNullable(node.data.command).map { obj: ICommand2MC -> obj.plugin }.map { obj: ButtonPlugin? -> plugin.equals(obj) }.orElse(false) }, true) unregisterCommandIf({ node -> Optional.ofNullable(node.data.command).map { obj -> obj.plugin }.map { obj -> plugin == obj }.orElse(false) }, true)
} }
fun unregisterCommands(component: Component<*>) { fun unregisterCommands(component: Component<*>) {

View file

@ -1,17 +0,0 @@
package buttondevteam.lib.chat.commands;
import lombok.RequiredArgsConstructor;
import org.javatuples.Pair;
/**
* A command argument's information to be used to construct the command.
*/
@RequiredArgsConstructor
public class CommandArgument {
public final String name;
public final Class<?> type;
public final boolean greedy;
public final Pair<Double, Double> limits;
public final boolean optional;
public final String description;
}

View file

@ -0,0 +1,13 @@
package buttondevteam.lib.chat.commands
/**
* A command argument's information to be used to construct the command.
*/
class CommandArgument(
val name: String,
val type: Class<*>,
val greedy: Boolean,
val limits: Pair<Double, Double>,
val optional: Boolean,
val description: String
)

View file

@ -1,19 +0,0 @@
package buttondevteam.lib.chat.commands;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;
@UtilityClass
public class CommandUtils {
/**
* Returns the path of the given subcommand excluding the class' path. It will start with the given replace char.
*
* @param methodName The method's name, method.getName()
* @param replaceChar The character to use between subcommands
* @return The command path starting with the replacement char.
*/
@NotNull
public static String getCommandPath(String methodName, char replaceChar) {
return methodName.equals("def") ? "" : replaceChar + methodName.replace('_', replaceChar).toLowerCase();
}
}

View file

@ -0,0 +1,25 @@
package buttondevteam.lib.chat.commands
import buttondevteam.lib.chat.Command2Sender
import buttondevteam.lib.chat.CoreCommandNode
import buttondevteam.lib.chat.ICommand2
import com.mojang.brigadier.tree.CommandNode
import java.util.*
object CommandUtils {
/**
* Returns the path of the given subcommand excluding the class' path. It will start with the given replace char.
*
* @param methodName The method's name, method.getName()
* @param replaceChar The character to use between subcommands
* @return The command path starting with the replacement char.
*/
fun getCommandPath(methodName: String, replaceChar: Char): String {
return if (methodName == "def") "" else replaceChar.toString() + methodName.replace('_', replaceChar).lowercase(Locale.getDefault())
}
@Suppress("UNCHECKED_CAST")
fun <TP : Command2Sender, TC : ICommand2<*>> CommandNode<TP>.core(): CoreCommandNode<TP, TC> {
return this as CoreCommandNode<TP, TC>
}
}

View file

@ -153,7 +153,7 @@ public abstract class ChromaGamerBase {
* @param sender The sender to use * @param sender The sender to use
* @return A user as returned by a converter or null if none can supply it * @return A user as returned by a converter or null if none can supply it
*/ */
public static ChromaGamerBase getFromSender(CommandSender sender) { public static ChromaGamerBase getFromSender(CommandSender sender) { // TODO: Use Command2Sender
for (val converter : senderConverters) { for (val converter : senderConverters) {
val ocg = converter.apply(sender); val ocg = converter.apply(sender);
if (ocg.isPresent()) if (ocg.isPresent())