Skip to content

Instantly share code, notes, and snippets.

@DevSrSouza
Created December 3, 2018 11:58
Show Gist options
  • Save DevSrSouza/684b59a95eb92fa9c44de859fe64d1d7 to your computer and use it in GitHub Desktop.
Save DevSrSouza/684b59a95eb92fa9c44de859fe64d1d7 to your computer and use it in GitHub Desktop.
Old idea of Arguments for KotlinBukkitAPI
package br.com.devsrsouza.kotlinbukkitapi.dsl.command
import br.com.devsrsouza.kotlinbukkitapi.extensions.text.asText
import br.com.devsrsouza.kotlinbukkitapi.extensions.text.color
import br.com.devsrsouza.kotlinbukkitapi.extensions.text.unaryPlus
import net.md_5.bungee.api.chat.TextComponent
import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.Location
import org.bukkit.World
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import kotlin.reflect.KClass
/**
* DSL
*/
typealias ArgumentExecutorBlock<T> = ArgumentExecutor.(T) -> Unit
typealias ParseExceptionBlock = ParseException.() -> String
interface Argumentable {
var argument: Argument<*>?
fun <T : Any> argument(factoryType: KClass<T>, block: ArgumentExecutorBlock<T>): Argument<T> {
val factory = ArgumentController[factoryType]
if(factory != null) {
return argumentByFactory(factory, block)
} else throw IllegalArgumentException("Not found ArgumentFactory registred for ${factoryType.simpleName} type")
}
fun <T : Any> argumentByFactory(factory: ArgumentFactory<T>, block: ArgumentExecutorBlock<T>)
= Argument(factory, block).also { this.argument = it }
}
inline fun <reified T : Any> Argumentable.argument(noinline block: ArgumentExecutorBlock<T>)
= argument(T::class, block)
open class ArgumentExecutor(internal val sender: CommandSender, val argumentLabel: String, val args: Array<out String>) : Argumentable {
override var argument: Argument<*>? = null
}
class ParseException(val id: Int) : Exception()
class ParseBox<T>(val value: T, val argsUsage: Int)
class Argument<T : Any>(val argumentFactory: ArgumentFactory<T>, val argumentExecutor: ArgumentExecutorBlock<T>, private var whenErrorBlock: ParseExceptionBlock? = null) {
infix fun whenError(block: ParseExceptionBlock) {
whenErrorBlock = block
}
internal fun runArgumentExecutor(sender: CommandSender, label: String, args: Array<out String>) {
if(argumentFactory.minArgs > args.size) {
sender.sendMessage(+"&c/${label} ${argumentFactory.usageFormat}")
return
}
try {
val box = argumentFactory.parser(sender, args)
val argumentLabel = label + " " + args.slice(0 until box.argsUsage).joinToString(" ")
ArgumentExecutor(sender, argumentLabel, args.sliceArray(box.argsUsage until args.size)).apply {
argumentExecutor(box.value)
argument?.also {
it.runArgumentExecutor(sender, this.argumentLabel, this.args)
}
}
}catch (e: ParseException) {
if(whenErrorBlock != null) {
sender.sendMessage(whenErrorBlock?.invoke(e))
} else {
sender.sendMessage(argumentFactory.whenError(e))
}
}
}
}
abstract class ArgumentFactory<T : Any>(val kclass: KClass<T>, val minArgs: Int, val usageFormat: String) {
abstract fun parser(sender: CommandSender, args: Array<out String>): ParseBox<T>
abstract fun parserTabComplete(args: Array<String>): List<String>
abstract fun whenError(error: ParseException): String
fun register() = ArgumentController.register(this)
}
class SimpleArgumentFactory<T : Any>(kclass: KClass<T>,
minArgs: Int,
usageFormat: String,
private val parser: (args: Array<out String>) -> ParseBox<T>,
private val errorMessage: (error: ParseException) -> String)
: ArgumentFactory<T>(kclass, minArgs, usageFormat) {
override fun parser(sender: CommandSender, args: Array<out String>) = parser.invoke(args)
override fun whenError(error: ParseException) = errorMessage.invoke(error)
override fun parserTabComplete(args: Array<String>): List<String> {
return emptyList() // TODO
}
}
object PlayerArgumentFactory : ArgumentFactory<Player>(Player::class, 1, "[player name]") {
override fun parser(sender: CommandSender, args: Array<out String>): ParseBox<Player> {
return ParseBox(
args.get(0).let { Bukkit.getPlayerExact(it) } ?: throw ParseException(1),
1
)
}
override fun whenError(error: ParseException): String {
return when (error.id) {
1 -> +"&cPlayer not found"
else -> "&cError not treated. ${error.message}"
}
}
override fun parserTabComplete(args: Array<String>): List<String> {
return emptyList() // TODO
}
}
object LocationArgumentFactory : ArgumentFactory<Location>(Location::class, 1, "[x] [y] [z] [world]") {
override fun parser(sender: CommandSender, args: Array<out String>): ParseBox<Location> {
val arg1 = args[0]
if(arg1.startsWith("player:", true)) {
return ParseBox(
arg1.split(":").getOrNull(1)?.let { Bukkit.getPlayerExact(it) }?.location ?: throw ParseException(1),
1
)
} else {
val arg2 = args.getOrNull(1)
val arg3 = args.getOrNull(2)
val arg4 = args.getOrNull(3)
if(sender is Player && (arg4 == null || Bukkit.getWorld(arg4) == null)) {
return ParseBox(
Location(sender.world, arg1.toDoubleOrNull() ?: throw ParseException(2),
arg2?.toDoubleOrNull() ?: throw ParseException(3),
arg3?.toDoubleOrNull() ?: throw ParseException(4)),
3
)
} else {
return ParseBox(
Location(Bukkit.getWorld(arg4 ?: throw ParseException(5)) ?: throw ParseException(6),
arg1.toDoubleOrNull() ?: throw ParseException(2),
arg2?.toDoubleOrNull() ?: throw ParseException(3),
arg3?.toDoubleOrNull() ?: throw ParseException(4)),
4
)
}
}
}
override fun whenError(error: ParseException): String {
return when (error.id) {
1 -> "TENHO QUE FAZER OS OUTROS"
else -> "&cError not treated. ${error.message}"
}
}
override fun parserTabComplete(args: Array<String>): List<String> {
return emptyList() // TODO
}
}
object TextArgumentFactory : ArgumentFactory<String>(String::class, 1, "[text]") {
override fun parser(sender: CommandSender, args: Array<out String>): ParseBox<String> {
return ParseBox(
args.joinToString(" "),
args.size
)
}
override fun whenError(error: ParseException): String {
return "&cError not treated. ${error.message}"
}
override fun parserTabComplete(args: Array<String>): List<String> {
return emptyList()
}
}
internal fun MutableList<ArgumentFactory<*>>.register(factory: ArgumentFactory<*>): Boolean {
if(find { it.kclass.equals(factory.kclass) } != null) return false
add(factory)
return true
}
object ArgumentController {
private val arguments: MutableList<ArgumentFactory<*>> = mutableListOf()
init {
register(PlayerArgumentFactory)
register(TextArgumentFactory)
register(LocationArgumentFactory)
}
fun register(factory: ArgumentFactory<*>) = arguments.register(factory)
operator fun <T : Any> get(clazz: KClass<T>) : ArgumentFactory<T>? {
return arguments.find { it.kclass.equals(clazz) } as? ArgumentFactory<T>?
}
internal fun getArguments(): List<ArgumentFactory<*>> = arguments
}
fun meuCommand() {
command("lala") {
executorPlayer {
argument<Player> { target ->
argument<Location> { loc -> // x y z / player:SrSouza / x y z world
argumentByFactory(TextArgumentFactory) { texto ->
target.teleport(loc)
target.sendMessage(+texto)
}
}
} whenError { //ParseException.() -> String
when (id) {
1 -> +"&cJogador não encontrado."
else -> "&cOcorreu um erro que não foi tratado. $message"
}
}
}
}
command("broadcast") {
executor {
argumentByFactory(TextArgumentFactory) { message ->
Bukkit.broadcastMessage(+message)
}
}
}
command("tell") {
executor {
argument<Player> { target ->
argumentByFactory(TextArgumentFactory) { message ->
target.sendMessage(+message)
}
}
}
tabComplete {
arguments(Player::class)
}
}
}
package br.com.devsrsouza.kotlinbukkitapi.dsl.command
import br.com.devsrsouza.kotlinbukkitapi.KotlinBukkitAPI
import br.com.devsrsouza.kotlinbukkitapi.extensions.text.asText
import net.md_5.bungee.api.chat.TextComponent
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.command.SimpleCommandMap
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
private val serverCommands: SimpleCommandMap by lazy {
val packageName = Bukkit.getServer().javaClass.getPackage().getName()
val version = packageName.substring(packageName.lastIndexOf('.') + 1)
val bukkitclass = Class.forName("org.bukkit.craftbukkit.$version.CraftServer")
val f = bukkitclass.getDeclaredField("commandMap")
f.isAccessible = true
f.get(Bukkit.getServer()) as SimpleCommandMap
}
typealias ExecutorBlock = Executor<CommandSender>.() -> Unit
typealias ExecutorPlayerBlock = Executor<Player>.() -> Unit
typealias TabCompleterBlock = TabCompleter.() -> MutableList<String>
typealias CommandMaker = KCommand.() -> Unit
class CommandException(val senderMessage: TextComponent = "".asText(), val execute: () -> Unit = {}) : RuntimeException() {
constructor(senderMessage: String = "", execute: () -> Unit = {}) : this(senderMessage.asText(), execute)
}
fun simpleCommand(name: String, vararg aliases: String = arrayOf(),
description: String = "",
plugin: Plugin = KotlinBukkitAPI.INSTANCE,
block: ExecutorBlock) = command(name, plugin) {
if (description.isNotBlank()) this.description = description
if (aliases.isNotEmpty()) this.aliases = aliases.toList()
executor(block)
}
fun command(name: String,
plugin: Plugin = KotlinBukkitAPI.INSTANCE,
block: CommandMaker) = KCommand(name).apply(block).apply {
register(plugin)
}
fun Command.register(plugin: Plugin = KotlinBukkitAPI.INSTANCE) {
serverCommands.register(plugin.name, this)
}
fun Command.unregister() {
try {
val clazz = serverCommands.javaClass
val f = clazz.getDeclaredField("knownCommands")
f.isAccessible = true
val knownCommands = f.get(serverCommands) as MutableMap<String, Command>
val toRemove = ArrayList<String>()
for ((key, value) in knownCommands) {
if (value === this) {
toRemove.add(key)
}
}
for (str in toRemove) {
knownCommands.remove(str)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
open class Executor<E : CommandSender>(val sender: E,
val label: String,
val args: Array<out String>) : Argumentable {
override var argument: Argument<*>? = null
}
class TabCompleter(val sender: CommandSender,
val alias: String,
val args: Array<out String>) {
fun arguments(vararg arguments: Any): MutableList<String> {
TODO("FAZER ESSA MERDA AQUI CARAIO")
}
}
open class KCommand(name: String,
executor: ExecutorBlock = {}
) : org.bukkit.command.Command(name.trim()) {
protected open var executor: ExecutorBlock = executor
private var executorPlayer: ExecutorPlayerBlock? = null
private var tabCompleter: TabCompleterBlock? = null
val subCommands: MutableList<KCommand> = mutableListOf()
var onlyInGameMessage = ""
override fun execute(sender: CommandSender, label: String, args: Array<out String>): Boolean {
if (subCommands.isNotEmpty()) {
val subCommand = args.getOrNull(0)?.let { arg ->
subCommands.find {
it.name.equals(arg, true) ||
it.aliases.find { it.equals(arg, true) } != null
}
}
if (subCommand != null) {
subCommand.execute(sender, "$label ${args.get(0)}", args.sliceArray(1 until args.size))
return true
}
}
try {
if (executorPlayer != null) {
if (sender is Player) {
val exc = Executor(sender, label, args)
executorPlayer!!.invoke(exc)
exc.argument?.runArgumentExecutor(sender, label, args)
} else sender.sendMessage(onlyInGameMessage)
} else {
Executor(sender, label, args).apply {
executor()
argument?.runArgumentExecutor(sender, label, args)
}
}
} catch (ex: CommandException) {
if (ex.senderMessage.isNotBlank()) sender.sendMessage(ex.senderMessage)
ex.execute()
}
return true
}
override fun tabComplete(sender: CommandSender, alias: String, args: Array<out String>): MutableList<String> {
return if (tabCompleter != null) {
tabCompleter!!.invoke(TabCompleter(sender, alias, args))
} else {
defaultTabComplete(sender, alias, args)
}
}
open fun defaultTabComplete(sender: CommandSender, alias: String, args: Array<out String>): MutableList<String> {
if (args.size > 1) {
val subCommand = subCommands.find { it.name.equals(args.getOrNull(0), true) }
if (subCommand != null) {
return subCommand.tabComplete(sender, args.get(0), args.sliceArray(1 until args.size))
} else {
emptyList<String>().toMutableList()
}
} else if (args.size > 0) {
return subCommands
.filter { it.name.startsWith(args.get(0), true) }
.map { it.name }
.toMutableList()
}
return super.tabComplete(sender, alias, args)
}
open fun command(name: String, block: CommandMaker) {
subCommands.add(KCommand(name).also {
it.permission = this.permission
it.permissionMessage = this.permissionMessage
}.apply(block))
}
open fun executor(block: ExecutorBlock) {
executor = block
}
open fun executorPlayer(block: ExecutorPlayerBlock) {
executorPlayer = block
}
open fun tabComplete(block: TabCompleterBlock) {
tabCompleter = block
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment