diff --git a/pom.xml b/pom.xml index 2bb146a..49f8e71 100644 --- a/pom.xml +++ b/pom.xml @@ -63,6 +63,4 @@ provided - - \ No newline at end of file diff --git a/src/main/kotlin/com/qrakn/honcho/Honcho.kt b/src/main/kotlin/com/qrakn/honcho/Honcho.kt index 7033aea..98ec45c 100644 --- a/src/main/kotlin/com/qrakn/honcho/Honcho.kt +++ b/src/main/kotlin/com/qrakn/honcho/Honcho.kt @@ -1,9 +1,11 @@ package com.qrakn.honcho +import com.qrakn.honcho.command.CommandOption import com.qrakn.honcho.command.adapter.CommandTypeAdapter -import com.qrakn.honcho.command.adapter.impl.PlayerTypeAdapter -import com.qrakn.honcho.command.adapter.impl.StringTypeAdapter +import com.qrakn.honcho.command.adapter.impl.* import org.bukkit.ChatColor +import org.bukkit.GameMode +import org.bukkit.OfflinePlayer import org.bukkit.entity.Player import org.bukkit.plugin.java.JavaPlugin @@ -13,8 +15,15 @@ class Honcho(val plugin: JavaPlugin) { var noPermissionMessage = "${ChatColor.RED}You don't have permission to do this." init { - registerTypeAdapter(String::class.java, StringTypeAdapter()) + registerTypeAdapter(Boolean::class.java, BooleanTypeAdapter()) + registerTypeAdapter(ChatColor::class.java, ChatColorTypeAdapter()) + registerTypeAdapter(Double::class.java, DoubleTypeAdapter()) + registerTypeAdapter(GameMode::class.java, GameModeTypeAdapter()) + registerTypeAdapter(Integer::class.java, IntegerTypeAdapter()) + registerTypeAdapter(OfflinePlayer::class.java, OfflinePlayerTypeAdapter()) + registerTypeAdapter(CommandOption::class.java, CommandOptionTypeAdapter()) registerTypeAdapter(Player::class.java, PlayerTypeAdapter()) + registerTypeAdapter(String::class.java, StringTypeAdapter()) } fun registerCommand(command: Any) { diff --git a/src/main/kotlin/com/qrakn/honcho/HonchoExecutor.kt b/src/main/kotlin/com/qrakn/honcho/HonchoExecutor.kt index 4f19cf6..5e02443 100644 --- a/src/main/kotlin/com/qrakn/honcho/HonchoExecutor.kt +++ b/src/main/kotlin/com/qrakn/honcho/HonchoExecutor.kt @@ -2,6 +2,7 @@ package com.qrakn.honcho import com.qrakn.honcho.command.CPL import com.qrakn.honcho.command.CommandMeta +import com.qrakn.honcho.command.CommandOption import com.qrakn.honcho.command.adapter.CommandTypeAdapter import com.qrakn.honcho.command.adapter.NonNullableCommandTypeAdapter import org.apache.commons.lang.StringUtils @@ -18,7 +19,7 @@ import org.bukkit.scheduler.BukkitRunnable import java.lang.Exception import java.lang.NullPointerException import java.lang.reflect.Method -import java.util.HashMap +import java.util.* internal class HonchoExecutor(private val honcho: Honcho) : CommandExecutor { @@ -36,7 +37,7 @@ internal class HonchoExecutor(private val honcho: Honcho) : CommandExecutor { } } - val binding = CommandBinding(methods.toTypedArray(), command) + val binding = CommandBinding(methods.toTypedArray(), meta, command) for (label in HonchoCommand.getHierarchicalLabel(command.javaClass, ArrayList())) { commands[label] = binding @@ -57,10 +58,11 @@ internal class HonchoExecutor(private val honcho: Honcho) : CommandExecutor { } } - fun execute(sender: CommandSender, command: Command, label: String, args: Array): Boolean { + fun execute(sender: CommandSender, command: Command, label: String, args: Array): Boolean { val binding: CommandBinding = commands[label] ?: return false val meta = binding.command.javaClass.getAnnotation(CommandMeta::class.java) val instance = binding.command + var option = false if (meta.permission.isNotEmpty() && !sender.hasPermission(meta.permission)) { var message = honcho.noPermissionMessage @@ -78,12 +80,22 @@ internal class HonchoExecutor(private val honcho: Honcho) : CommandExecutor { override fun run() { outer@ for (method in binding.methods) { val parameters = method.parameters + var doContinue = true if (parameters[0].type is Player && sender !is Player) continue if (method.declaringClass != instance.javaClass) continue if (method.parameterCount - 1 > args.size) { - continue + for(param in parameters) { + if(param.type == CommandOption::class.java && method.parameterCount - 2 <= args.size) { + doContinue = false + break + } + } + + if(doContinue) { + continue + } } /** @@ -119,15 +131,23 @@ internal class HonchoExecutor(private val honcho: Honcho) : CommandExecutor { } try { - val conversion = adapter.convert(input, parameter.type) + val conversion = adapter.convert(input!!, parameter.type) if (conversion == null && adapter is NonNullableCommandTypeAdapter) { throw NullPointerException() } + if(conversion is CommandOption) { + if(!meta.options.contains(conversion.tag.toLowerCase())) { + sender.sendMessage("${ChatColor.RED}Unrecognized command option '-${conversion.tag}'!") + sender.sendMessage("${ChatColor.RED}Usage: ${command.usage}") + return + } + } + arguments.add(conversion) } catch (exception: Exception) { - if (!adapter.onException(exception, sender, input)) { // if exception not handled by adapter + if (!adapter.onException(exception, sender, input!!)) { // if exception not handled by adapter sender.sendMessage("${ChatColor.RED}An error occurred (${exception.message}), please contact an administrator") } return @@ -154,7 +174,7 @@ internal class HonchoExecutor(private val honcho: Honcho) : CommandExecutor { return true } - override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { + override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { return execute(sender, command, label, args) } @@ -176,7 +196,19 @@ internal class HonchoExecutor(private val honcho: Honcho) : CommandExecutor { private fun getCommandUsage(label: String): String { val command = commands[label]!! val builder = StringBuilder("/").append(label) - val arguments: MutableMap = HashMap() + val arguments: MutableMap = HashMap() + + if(command.meta.options.isNotEmpty()) { + val options = ArrayList() + + command.meta.options.forEach { + options.add("-" + it.toLowerCase()) + } + + builder.append(" [") + builder.append(StringUtils.join(options, ",")) + builder.append("]") + } for (method in command.methods) { val parameters = method.parameters @@ -191,24 +223,31 @@ internal class HonchoExecutor(private val honcho: Honcho) : CommandExecutor { parameter.name } - if (!(argument.arguments.contains(name))) { - argument.arguments.add(name) - arguments[i - 1] = argument + if(parameter.type == CommandOption::class.java) { + arguments[i - 1] = null + } else { + if (argument != null) { + if (!(argument.arguments.contains(name))) { + argument.arguments.add(name) + arguments[i - 1] = argument + } + } } } - } for (index in 0 until arguments.size) { val argument = arguments[index] - builder.append(" <").append(StringUtils.join(argument?.arguments, "/")).append(">") + if(argument != null) { + builder.append(" <").append(StringUtils.join(argument.arguments, "/")).append(">") + } } return builder.toString() } private inner class CommandArguments(val arguments: MutableList) - internal inner class CommandBinding(val methods: Array, val command: Any) + internal inner class CommandBinding(val methods: Array, val meta: CommandMeta, val command: Any) } \ No newline at end of file diff --git a/src/main/kotlin/com/qrakn/honcho/command/CommandMeta.kt b/src/main/kotlin/com/qrakn/honcho/command/CommandMeta.kt index 3c27d30..6fdf43c 100644 --- a/src/main/kotlin/com/qrakn/honcho/command/CommandMeta.kt +++ b/src/main/kotlin/com/qrakn/honcho/command/CommandMeta.kt @@ -11,6 +11,7 @@ package com.qrakn.honcho.command @Retention(AnnotationRetention.RUNTIME) annotation class CommandMeta( vararg val label: String, + val options: Array = [""], val permission: String = "", val description: String = "", val subcommands: Boolean = false, diff --git a/src/main/kotlin/com/qrakn/honcho/command/CommandOption.kt b/src/main/kotlin/com/qrakn/honcho/command/CommandOption.kt new file mode 100644 index 0000000..ffa0f57 --- /dev/null +++ b/src/main/kotlin/com/qrakn/honcho/command/CommandOption.kt @@ -0,0 +1,3 @@ +package com.qrakn.honcho.command + +class CommandOption(val tag: String) \ No newline at end of file diff --git a/src/main/kotlin/com/qrakn/honcho/command/adapter/CommandTypeAdapter.kt b/src/main/kotlin/com/qrakn/honcho/command/adapter/CommandTypeAdapter.kt index 23e144f..41e065c 100644 --- a/src/main/kotlin/com/qrakn/honcho/command/adapter/CommandTypeAdapter.kt +++ b/src/main/kotlin/com/qrakn/honcho/command/adapter/CommandTypeAdapter.kt @@ -4,6 +4,6 @@ import org.bukkit.command.CommandSender import java.lang.Exception interface CommandTypeAdapter { - fun convert(string: String, type: Class): T + fun convert(string: String, type: Class): T? fun onException(exception: Exception, sender: CommandSender, input: String): Boolean { return false } } \ No newline at end of file diff --git a/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/BooleanTypeAdapter.kt b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/BooleanTypeAdapter.kt new file mode 100644 index 0000000..e7b2fa4 --- /dev/null +++ b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/BooleanTypeAdapter.kt @@ -0,0 +1,31 @@ +package com.qrakn.honcho.command.adapter.impl + +import com.qrakn.honcho.command.adapter.NonNullableCommandTypeAdapter +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import java.lang.Exception + +class BooleanTypeAdapter : NonNullableCommandTypeAdapter { + override fun convert(string: String, type: Class): T { + return type.cast(MAP[string.toLowerCase()]) + } + + override fun onException(exception: Exception, sender: CommandSender, input: String): Boolean { + sender.sendMessage("${ChatColor.RED}The value '$input' is not a boolean. ${ChatColor.GRAY}[yes, no]") + return true + } + + companion object { + private val MAP = HashMap() + + init { + MAP["true"] = true + MAP["on"] = true + MAP["yes"] = true + + MAP["false"] = false + MAP["off"] = false + MAP["no"] = false + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/ChatColorTypeAdapter.kt b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/ChatColorTypeAdapter.kt new file mode 100644 index 0000000..1a72444 --- /dev/null +++ b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/ChatColorTypeAdapter.kt @@ -0,0 +1,17 @@ +package com.qrakn.honcho.command.adapter.impl + +import com.qrakn.honcho.command.adapter.NonNullableCommandTypeAdapter +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import java.lang.Exception + +class ChatColorTypeAdapter : NonNullableCommandTypeAdapter { + override fun convert(string: String, type: Class): T? { + return type.cast(ChatColor.valueOf(string.toUpperCase())) + } + + override fun onException(exception: Exception, sender: CommandSender, input: String): Boolean { + sender.sendMessage("${ChatColor.RED}The value '$input' is not a chatcolor. ${ChatColor.GRAY}[RED, DARK_RED]") + return true + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/CommandOptionTypeAdapter.kt b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/CommandOptionTypeAdapter.kt new file mode 100644 index 0000000..721243e --- /dev/null +++ b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/CommandOptionTypeAdapter.kt @@ -0,0 +1,17 @@ +package com.qrakn.honcho.command.adapter.impl + +import com.qrakn.honcho.command.CommandOption +import com.qrakn.honcho.command.adapter.CommandTypeAdapter +import org.bukkit.command.CommandSender + +class CommandOptionTypeAdapter : CommandTypeAdapter { + override fun convert(string: String, type: Class): T? { + return if (string.startsWith("-")) { + type.cast(CommandOption(string.toLowerCase().substring(1))) + } else null + } + + override fun onException(exception: Exception, sender: CommandSender, input: String): Boolean { + return false + } +} diff --git a/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/DoubleTypeAdapter.kt b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/DoubleTypeAdapter.kt new file mode 100644 index 0000000..a5b6379 --- /dev/null +++ b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/DoubleTypeAdapter.kt @@ -0,0 +1,17 @@ +package com.qrakn.honcho.command.adapter.impl + +import com.qrakn.honcho.command.adapter.NonNullableCommandTypeAdapter +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import java.lang.Exception + +class DoubleTypeAdapter : NonNullableCommandTypeAdapter { + override fun convert(string: String, type: Class): T { + return type.cast(string.toDoubleOrNull()) + } + + override fun onException(exception: Exception, sender: CommandSender, input: String): Boolean { + sender.sendMessage("${ChatColor.RED}The value '$input' is not a double. ${ChatColor.GRAY}[2.0, 1.2, 1.5]") + return true + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/GameModeTypeAdapter.kt b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/GameModeTypeAdapter.kt new file mode 100644 index 0000000..0028c9e --- /dev/null +++ b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/GameModeTypeAdapter.kt @@ -0,0 +1,41 @@ +package com.qrakn.honcho.command.adapter.impl + +import com.qrakn.honcho.command.adapter.NonNullableCommandTypeAdapter +import org.bukkit.ChatColor +import org.bukkit.GameMode +import org.bukkit.command.CommandSender +import java.lang.Exception +import java.util.* + +class GameModeTypeAdapter : NonNullableCommandTypeAdapter { + override fun convert(string: String, type: Class): T { + return type.cast(MAP[string.toLowerCase()]) + } + + override fun onException(exception: Exception, sender: CommandSender, input: String): Boolean { + sender.sendMessage("${ChatColor.RED}The value '$input' is not a gamemode. ${ChatColor.GRAY}[creative, 0, a]") + return true + } + + companion object { + private val MAP = HashMap() + + init { + MAP["c"] = GameMode.CREATIVE + MAP["creative"] = GameMode.CREATIVE + MAP["1"] = GameMode.CREATIVE + + MAP["s"] = GameMode.SURVIVAL + MAP["survival"] = GameMode.SURVIVAL + MAP["0"] = GameMode.SURVIVAL + + MAP["a"] = GameMode.ADVENTURE + MAP["adventure"] = GameMode.ADVENTURE + MAP["2"] = GameMode.ADVENTURE + + MAP["sp"] = GameMode.SPECTATOR + MAP["spectator"] = GameMode.SPECTATOR + MAP["3"] = GameMode.SPECTATOR + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/IntegerTypeAdapter.kt b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/IntegerTypeAdapter.kt new file mode 100644 index 0000000..6b91b46 --- /dev/null +++ b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/IntegerTypeAdapter.kt @@ -0,0 +1,17 @@ +package com.qrakn.honcho.command.adapter.impl + +import com.qrakn.honcho.command.adapter.NonNullableCommandTypeAdapter +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import java.lang.Exception + +class IntegerTypeAdapter : NonNullableCommandTypeAdapter { + override fun convert(string: String, type: Class): T { + return type.cast(Integer.parseInt(string)) + } + + override fun onException(exception: Exception, sender: CommandSender, input: String): Boolean { + sender.sendMessage("${ChatColor.RED}The value '$input' is not an integer. ${ChatColor.GRAY}[1, 2, 3]") + return true + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/OfflinePlayerTypeAdapter.kt b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/OfflinePlayerTypeAdapter.kt new file mode 100644 index 0000000..9bb4bbe --- /dev/null +++ b/src/main/kotlin/com/qrakn/honcho/command/adapter/impl/OfflinePlayerTypeAdapter.kt @@ -0,0 +1,18 @@ +package com.qrakn.honcho.command.adapter.impl + +import com.qrakn.honcho.command.adapter.NonNullableCommandTypeAdapter +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender +import java.lang.Exception + +class OfflinePlayerTypeAdapter : NonNullableCommandTypeAdapter { + override fun convert(string: String, type: Class): T { + return type.cast(Bukkit.getOfflinePlayer(string)) + } + + override fun onException(exception: Exception, sender: CommandSender, input: String): Boolean { + sender.sendMessage("${ChatColor.RED}The player '$input' could not be found.") + return true + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/qrakn/honcho/command/example/ExampleCommand.kt b/src/main/kotlin/com/qrakn/honcho/command/example/ExampleCommand.kt index d571773..24d4c16 100644 --- a/src/main/kotlin/com/qrakn/honcho/command/example/ExampleCommand.kt +++ b/src/main/kotlin/com/qrakn/honcho/command/example/ExampleCommand.kt @@ -1,23 +1,24 @@ package com.qrakn.honcho.command.example +import com.qrakn.honcho.command.CPL import com.qrakn.honcho.command.CommandMeta +import com.qrakn.honcho.command.CommandOption import org.bukkit.ChatColor import org.bukkit.command.CommandSender import org.bukkit.entity.Player -@CommandMeta(label = ["example"], description = "Example command", subcommands = true) +@CommandMeta(label = ["example"], options = ["s"], description = "Example command", subcommands = true) open class ExampleCommand { - fun execute(sender: CommandSender, player: Player?) { + fun execute(sender: CommandSender, option: CommandOption?, player: Player) { + sender.sendMessage("You poked ${player.name}.") - if (player == null) { - sender.sendMessage("${ChatColor.RED}Player not found.") - return + if(option == null) { + player.sendMessage("${sender.name} poked you.") + } else { + player.sendMessage("Someone poked you, but who?") } - sender.sendMessage("You poked ${player.name}.") - - player.sendMessage("${sender.name} poked you.") player.damage(0.0) } @@ -25,9 +26,7 @@ open class ExampleCommand { inner class ExampleSubCommand : ExampleCommand() { fun execute(sender: CommandSender) { - sender.sendMessage("${ChatColor.RED}honcho v1.0-SNAPSHOT") + sender.sendMessage("${ChatColor.RED}honcho v1.3-SNAPSHOT") } - } - } \ No newline at end of file