Release candidate 1.0.0.0

This commit is contained in:
redtea committed 2023-08-29 16:43:56 +03:00
1 parent 51ae06d7cd
commit 339ac366e9
284 files changed
+361 -2784

No files matched your search

@@ -1,3 +0,0 @@
package tech.carcadex.kotlinbukkitkit.commands
open class CommandBase{}
@@ -1,3 +0,0 @@
package tech.carcadex.kotlinbukkitkit.commands.annotations
annotation class Aliases(val aliases: Array<String>)
@@ -1,4 +0,0 @@
package tech.carcadex.kotlinbukkitkit.commands.annotations
@Retention(AnnotationRetention.RUNTIME)
annotation class Default()
@@ -1,4 +0,0 @@
package tech.carcadex.kotlinbukkitkit.commands.annotations
@Retention(AnnotationRetention.RUNTIME)
annotation class Optional()
@@ -0,0 +1,43 @@
package tech.carcadex.kotlinbukkitkit.commands.dsl
import org.bukkit.command.CommandSender
import tech.carcadex.kotlinbukkitkit.commands.service.MessagesService
/**
* Interface for SubCommand and Command
* @author itzRedTea
* @since 1.0.0
*/
interface Command {
/**
* Context of this command
* @author itzRedTea
* @since 1.0.0
*/
val context: CommandContext
/**
* Invokes when command must be executed
* @author itzRedTea
* @since 1.0.0
*/
fun execute(invoker: ExecutorContext)
/**
* Invokes when command must be executed
* @param sender who executes this command
* @param args arguments that sender typed in chat
* (if this is subcommand args did not contains
* name of sub command)
* @author itzRedTea
* @since 1.0.0
*/
fun tabComplete(sender: CommandSender, args: Array<String>): List<String>
fun hasNotPermission(sender: CommandSender): Boolean =
context.permission != "" && !sender.hasPermission(context.permission).also {
if(it) MessagesService.byTag("#no-perm")(sender)
}
}
@@ -0,0 +1,19 @@
package tech.carcadex.kotlinbukkitkit.commands.dsl
/**
* Class that contains command data
* @param name name of command
* @param aliases array of aliases of this command (includes name of command)
* @param usage usage message of command
* @param description description message of command
* @param permission permission that requires to use this command (set empty string if
* command did not requires any permissions)
*
* @author itzRedTea
* @since 1.0.0
*/
class CommandContext(val name: String,
var aliases: Array<String> = arrayOf(name),
var usage: String = "",
var description: String = "",
var permission: String = "")
@@ -0,0 +1,7 @@
package tech.carcadex.kotlinbukkitkit.commands.dsl
import tech.carcadex.kotlinbukkitkit.commands.dsl.Command
interface CommandNode : Command {
fun add(command: Command)
}
@@ -0,0 +1,19 @@
package tech.carcadex.kotlinbukkitkit.commands.dsl
import org.bukkit.command.CommandSender
import tech.carcadex.kotlinbukkitkit.commands.exceptions.ArgumentNotFoundException
import tech.carcadex.kotlinbukkitkit.commands.exceptions.TypeParseException
import tech.carcadex.kotlinbukkitkit.commands.service.MessagesService
class ExecutorContext(var sender: CommandSender, val args: Array<String>) {
fun argument(index: Int): String? = if (args.size <= index) null else args[index]
fun string(index: Int): String = argument(index) ?: throw ArgumentNotFoundException(index)
fun stringOrNull(index: Int): String? = argument(index)
fun integer(index: Int): Int = try {
argument(index)?.toInt()
} catch (e: NumberFormatException) {
MessagesService.byTag("#not-a-number")(sender)
throw TypeParseException()
} ?: throw ArgumentNotFoundException(index)
fun integerOrNull(index: Int): Int? = argument(index)?.toIntOrNull()
}
@@ -0,0 +1,69 @@
package tech.carcadex.kotlinbukkitkit.commands.dsl.builder
import org.bukkit.plugin.Plugin
import tech.carcadex.kotlinbukkitkit.commands.dsl.Command
import tech.carcadex.kotlinbukkitkit.commands.dsl.CommandContext
import tech.carcadex.kotlinbukkitkit.commands.dsl.ExecutorContext
import tech.carcadex.kotlinbukkitkit.commands.dsl.leaf.CommandLeaf
import tech.carcadex.kotlinbukkitkit.commands.dsl.node.CommandNodeImpl
import tech.carcadex.kotlinbukkitkit.commands.exceptions.LeafCommandHasNotExecutorException
import tech.carcadex.kotlinbukkitkit.commands.register.CommandRegister
class CommandBuilder(
val context: CommandContext,
var tabComplete: Collection<String> = emptyList(),
val subs: MutableMap<String, Command> = mutableMapOf(),
var executor: (ExecutorContext.() -> Unit)? = null
) {
var aliases by context::aliases
var usage by context::usage
var description by context::description
var permission by context::permission
}
fun CommandBuilder.subCommand(name: String, permission: String, tabComplete: Collection<String>, invoke: CommandBuilder.() -> Unit) {
subs[name.lowercase()] = createCommand(name, permission, tabComplete, invoke)
}
fun CommandBuilder.executor(executor: ExecutorContext.() -> Unit) {
this.executor = executor
}
private fun createCommand(name: String, permission: String, tabComplete: Collection<String>, invoke: CommandBuilder.() -> Unit): Command {
val builder = CommandBuilder(CommandContext(name))
builder.permission = permission
builder.tabComplete = tabComplete
builder.invoke()
return createCommand(builder)
}
private fun createCommand(builder: CommandBuilder): Command {
return if (builder.subs.isEmpty()) {
if (builder.executor == null) throw LeafCommandHasNotExecutorException(builder.context.name)
CommandLeaf(builder.context, builder.executor!!, builder.tabComplete.toList())
} else {
CommandNodeImpl(builder.context, builder.subs)
}
}
fun Plugin.simpleCommand(name: String, permission: String, tabComplete: Collection<String>, executor: ExecutorContext.() -> Unit) {
val command = createCommand(CommandBuilder(CommandContext(name),
executor = executor).apply {
this.permission = permission
this.tabComplete = tabComplete
})
CommandRegister.register(this, { sender, _, _, args -> command.execute(ExecutorContext(sender, args)); true },
command.context.aliases, command.context.description, command.context.usage, { sender, _, _, args ->
command.tabComplete(sender, args)
}
)
}
fun Plugin.command(name: String, permission: String, tabComplete: Collection<String>, invoke: CommandBuilder.() -> Unit) {
val command = createCommand(name, permission, tabComplete, invoke)
CommandRegister.register(this, { sender, _, _, args -> command.execute(ExecutorContext(sender, args)); true },
command.context.aliases, command.context.description, command.context.usage, { sender, _, _, args ->
command.tabComplete(sender, args)
}
)
}
@@ -0,0 +1,42 @@
package tech.carcadex.kotlinbukkitkit.commands.dsl.builder
import org.bukkit.plugin.Plugin
import tech.carcadex.kotlinbukkitkit.commands.dsl.ExecutorContext
import tech.carcadex.kotlinbukkitkit.commands.register.CommandRegister
fun Plugin.command(name: String, invoke: CommandBuilder.() -> Unit) {
command(name, "", listOf(), invoke)
}
fun Plugin.command(name: String, permission: String, invoke: CommandBuilder.() -> Unit) {
command(name, permission, listOf(), invoke)
}
fun Plugin.command(name: String, tabComplete: Collection<String>, invoke: CommandBuilder.() -> Unit) {
command(name, "", tabComplete, invoke)
}
fun CommandBuilder.subCommand(name: String, invoke: CommandBuilder.() -> Unit) {
subCommand(name, "", listOf(), invoke)
}
fun CommandBuilder.subCommand(name: String, permission: String, invoke: CommandBuilder.() -> Unit) {
subCommand(name, permission, listOf(), invoke)
}
fun CommandBuilder.subCommand(name: String, tabComplete: Collection<String>, invoke: CommandBuilder.() -> Unit) {
subCommand(name, "", tabComplete, invoke)
}
fun Plugin.simpleCommand(name: String, executor: ExecutorContext.() -> Unit) {
simpleCommand(name, "", listOf(), executor)
}
fun Plugin.simpleCommand(name: String, permission: String, executor: ExecutorContext.() -> Unit) {
simpleCommand(name, permission, listOf(), executor)
}
fun Plugin.simpleCommand(name: String, tabComplete: Collection<String>, executor: ExecutorContext.() -> Unit) {
simpleCommand(name, "", tabComplete, executor)
}
@@ -0,0 +1,31 @@
package tech.carcadex.kotlinbukkitkit.commands.dsl.leaf
import org.bukkit.command.CommandSender
import tech.carcadex.kotlinbukkitkit.commands.dsl.Command
import tech.carcadex.kotlinbukkitkit.commands.dsl.CommandContext
import tech.carcadex.kotlinbukkitkit.commands.dsl.ExecutorContext
import tech.carcadex.kotlinbukkitkit.commands.exceptions.ArgumentNotFoundException
import tech.carcadex.kotlinbukkitkit.commands.service.MessagesService
import tech.carcadex.kotlinbukkitkit.commands.service.TabCompleteService
class CommandLeaf(
override val context: CommandContext,
private val executor: (ExecutorContext.() -> Unit),
completeTags: List<String> = emptyList()
) : Command {
private val completer = TabCompleteService.parseTags(completeTags)
override fun execute(context: ExecutorContext) {
if(hasNotPermission(context.sender)) return
try {
executor(context)
} catch (e: ArgumentNotFoundException) {
MessagesService.byTag("#wrong-usage")(context.sender)
}
}
override fun tabComplete(sender: CommandSender, args: Array<String>): List<String> {
if(args.isEmpty() || completer.size < args.size || hasNotPermission(sender)) return emptyList()
return completer[args.size-1](sender)
}
}
@@ -0,0 +1,35 @@
package tech.carcadex.kotlinbukkitkit.commands.dsl.node
import org.bukkit.command.CommandSender
import tech.carcadex.kotlinbukkitkit.commands.dsl.Command
import tech.carcadex.kotlinbukkitkit.commands.dsl.CommandContext
import tech.carcadex.kotlinbukkitkit.commands.dsl.CommandNode
import tech.carcadex.kotlinbukkitkit.commands.dsl.ExecutorContext
import tech.carcadex.kotlinbukkitkit.commands.service.MessagesService
class CommandNodeImpl(
override val context: CommandContext,
private val subs: MutableMap<String, Command> = mutableMapOf()
) : CommandNode {
private var default: Command? = null
override fun execute(context: ExecutorContext) {
if(hasNotPermission(context.sender)) return
if(context.args.isNotEmpty() && context.args[0].lowercase() in subs) subs[context.args[0].lowercase()]?.execute(
ExecutorContext(context.sender, context.args.copyOfRange(1, context.args.size)))
else {
if(default != null) {
default!!.execute(ExecutorContext(context.sender, context.args))
} else MessagesService.byTag("#wrong-usage")(context.sender)
}
}
override fun tabComplete(sender: CommandSender, args: Array<String>): List<String> {
if(hasNotPermission(sender)) return emptyList()
return if(args.isNotEmpty() && args[0].lowercase() in subs) subs[args[0].lowercase()]?.tabComplete(sender, args.copyOfRange(1, args.size)) ?: emptyList()
else subs.keys.toList()
}
override fun add(command: Command) {
subs[command.context.name] = command
}
}
@@ -0,0 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands.exceptions
class ArgumentNotFoundException(index: Int) : NoSuchElementException("Argument with index $index not found! Args array out of bounds") {
}
@@ -0,0 +1,5 @@
package tech.carcadex.kotlinbukkitkit.commands.exceptions
class LeafCommandHasNotExecutorException(commandName: String)
: IllegalArgumentException("Command $commandName has not subcommands or default executor!") {
}
@@ -0,0 +1,20 @@
package tech.carcadex.kotlinbukkitkit.commands.manager
import org.bukkit.command.CommandSender
import org.bukkit.plugin.Plugin
import kotlin.reflect.KFunction
interface CommandManager {
fun register(commandClass: Any)
fun register(func: KFunction<Unit>)
fun tabComplete(tag: String, func: (CommandSender) -> List<String>)
fun message(tag: String, func: (CommandSender) -> Unit)
companion object {
@JvmStatic
fun get(plugin: Plugin): CommandManager = CommandManagerImpl(plugin)
}
}
fun Plugin.commandManager(): CommandManager = CommandManagerImpl(this)
@@ -4,14 +4,14 @@ import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.command.TabCompleter
import org.bukkit.plugin.Plugin
import tech.carcadex.kotlinbukkitkit.commands.CommandParams
import tech.carcadex.kotlinbukkitkit.commands.Parser
import tech.carcadex.kotlinbukkitkit.commands.Parser.paramsFromAnnotations
import tech.carcadex.kotlinbukkitkit.commands.annotations.Command
import tech.carcadex.kotlinbukkitkit.commands.reflection.CommandParams
import tech.carcadex.kotlinbukkitkit.commands.reflection.Parser
import tech.carcadex.kotlinbukkitkit.commands.reflection.Parser.paramsFromAnnotations
import tech.carcadex.kotlinbukkitkit.commands.reflection.annotations.Command
import tech.carcadex.kotlinbukkitkit.commands.exceptions.TypeParseException
import tech.carcadex.kotlinbukkitkit.commands.register.CommandRegister
import tech.carcadex.kotlinbukkitkit.commands.service.MessagesService
import tech.carcadex.kotlinbukkitkit.commands.service.SubCommandsService
import tech.carcadex.kotlinbukkitkit.commands.reflection.service.SubCommandsService
import tech.carcadex.kotlinbukkitkit.commands.service.TabCompleteService
import kotlin.reflect.KFunction
import kotlin.reflect.full.createType
@@ -19,7 +19,9 @@ import kotlin.reflect.full.hasAnnotation
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.jvm.javaType
class ReflectionCommandManager(private val plugin: Plugin) {
class CommandManagerImpl(private val plugin: Plugin) : CommandManager {
private fun register(
commandExecutor: CommandExecutor, commandParams: CommandParams, tabCompleter: TabCompleter =
@@ -35,7 +37,10 @@ class ReflectionCommandManager(private val plugin: Plugin) {
);
}
fun register(commandClass: Any) {
override fun register(commandClass: Any) {
if(commandClass::class.objectInstance == null) {
throw IllegalArgumentException("commandClass is not a object!")
}
val (name, subs) = SubCommandsService.registerRootCommand(commandClass)
val subsNames = mutableListOf<String>()
subs.forEach {
@@ -65,7 +70,7 @@ class ReflectionCommandManager(private val plugin: Plugin) {
})
}
fun register(func: KFunction<Unit>) {
override fun register(func: KFunction<Unit>) {
if (func.hasAnnotation<Command>()) {
val cmd = paramsFromAnnotations(func)
var argsSize = func.parameters.size
@@ -108,12 +113,12 @@ class ReflectionCommandManager(private val plugin: Plugin) {
} else throw IllegalArgumentException("It must be a command!")
}
fun tabComplete(tag: String, func: (CommandSender) -> List<String>) {
override fun tabComplete(tag: String, func: (CommandSender) -> List<String>) {
TabCompleteService.register(tag, func)
}
fun message(tag: String, func: (CommandSender) -> Unit) {
override fun message(tag: String, func: (CommandSender) -> Unit) {
MessagesService.register(tag, func)
}
}
@@ -1,4 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands
package tech.carcadex.kotlinbukkitkit.commands.reflection
data class CommandParams(
val name: String,
@@ -1,11 +1,11 @@
package tech.carcadex.kotlinbukkitkit.commands
package tech.carcadex.kotlinbukkitkit.commands.reflection
import org.bukkit.command.CommandSender
import tech.carcadex.kotlinbukkitkit.commands.annotations.*
import tech.carcadex.kotlinbukkitkit.commands.exceptions.TypeParseException
import tech.carcadex.kotlinbukkitkit.commands.exceptions.UnsupportedTypeException
import tech.carcadex.kotlinbukkitkit.commands.reflection.annotations.*
import tech.carcadex.kotlinbukkitkit.commands.service.MessagesService
import tech.carcadex.kotlinbukkitkit.commands.service.TypeService
import tech.carcadex.kotlinbukkitkit.commands.reflection.service.TypeService
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
@@ -0,0 +1,3 @@
package tech.carcadex.kotlinbukkitkit.commands.reflection.annotations
annotation class Aliases(val aliases: Array<String>)
@@ -1,4 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands.annotations
package tech.carcadex.kotlinbukkitkit.commands.reflection.annotations
@Retention(AnnotationRetention.RUNTIME)
annotation class Command(val name: String)
@@ -0,0 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands.reflection.annotations
@Retention(AnnotationRetention.RUNTIME)
annotation class Default()
@@ -1,4 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands.annotations
package tech.carcadex.kotlinbukkitkit.commands.reflection.annotations
@Retention(AnnotationRetention.RUNTIME)
annotation class Description(val desc: String)
@@ -0,0 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands.reflection.annotations
@Retention(AnnotationRetention.RUNTIME)
annotation class Optional()
@@ -1,4 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands.annotations
package tech.carcadex.kotlinbukkitkit.commands.reflection.annotations
@Retention(AnnotationRetention.RUNTIME)
annotation class Permission(val perm: String)
@@ -1,4 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands.annotations
package tech.carcadex.kotlinbukkitkit.commands.reflection.annotations
@Retention(AnnotationRetention.RUNTIME)
annotation class SubCommand(val name: String)
@@ -1,4 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands.annotations
package tech.carcadex.kotlinbukkitkit.commands.reflection.annotations
@Retention(AnnotationRetention.RUNTIME)
annotation class TabComplete(val complete: Array<String>)
@@ -1,4 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands.annotations
package tech.carcadex.kotlinbukkitkit.commands.reflection.annotations
@Retention(AnnotationRetention.RUNTIME)
annotation class Usage(val usage: String)
@@ -1,21 +1,21 @@
package tech.carcadex.kotlinbukkitkit.commands.service
package tech.carcadex.kotlinbukkitkit.commands.reflection.service
import org.bukkit.command.CommandSender
import org.bukkit.command.TabCompleter
import tech.carcadex.kotlinbukkitkit.commands.CommandParams
import tech.carcadex.kotlinbukkitkit.commands.Parser
import tech.carcadex.kotlinbukkitkit.commands.annotations.Command
import tech.carcadex.kotlinbukkitkit.commands.annotations.Default
import tech.carcadex.kotlinbukkitkit.commands.service.MessagesService
import tech.carcadex.kotlinbukkitkit.commands.service.TabCompleteService
import tech.carcadex.kotlinbukkitkit.commands.reflection.CommandParams
import tech.carcadex.kotlinbukkitkit.commands.reflection.Parser
import tech.carcadex.kotlinbukkitkit.commands.reflection.annotations.Command
import tech.carcadex.kotlinbukkitkit.commands.reflection.annotations.Default
import tech.carcadex.kotlinbukkitkit.commands.exceptions.TypeParseException
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.full.*
import kotlin.reflect.jvm.javaType
object SubCommandsService {
data class SubCommand(val name: String, val context: CommandParams,
val executor: (sender: CommandSender, args: Array<String>) -> Boolean, val tabCompleter: TabCompleter)
val executor: (sender: CommandSender, args: Array<String>) -> Boolean, val tabCompleter: TabCompleter)
private val subs = mutableMapOf<String, List<SubCommand>>()
@@ -28,7 +28,7 @@ object SubCommandsService {
val res = mutableListOf<SubCommand>()
for(func in clazz.functions) {
if(func.hasAnnotation<tech.carcadex.kotlinbukkitkit.commands.annotations.SubCommand>()) {
if(func.hasAnnotation<tech.carcadex.kotlinbukkitkit.commands.reflection.annotations.SubCommand>()) {
res.add(parseSubCommand(func, command))
} else if(func.hasAnnotation<Default>()) {
defaults[name] = parseSubCommand(func, command)
@@ -42,6 +42,10 @@ object SubCommandsService {
if(rootCommand.lowercase() in subs) {
if(args.isNotEmpty()) for(sub in subs[rootCommand.lowercase()]!!) {
if(sub.name == args[0]) {
if (sub.context.perm != null && sub.context.perm != "" && !sender.hasPermission(sub.context.perm)) {
MessagesService.byTag("#no-perm")(sender)
return
}
sub.executor(sender, args.copyOfRange(1, args.size))
return
}
@@ -53,7 +57,7 @@ object SubCommandsService {
private fun parseSubCommand(func: KFunction<*>, rootCommand: Any): SubCommand {
val name = if(func.hasAnnotation<Default>()) "default" else func.findAnnotations(tech.carcadex.kotlinbukkitkit.commands.annotations.SubCommand::class).first().name
val name = if(func.hasAnnotation<Default>()) "default" else func.findAnnotations(tech.carcadex.kotlinbukkitkit.commands.reflection.annotations.SubCommand::class).first().name
val context = Parser.paramsFromAnnotations(func)
val argsParsers = Parser.parseParam(func.parameters.toTypedArray().copyOfRange(1, func.parameters.size).toList())
var argsSize = func.parameters.size
@@ -1,4 +1,4 @@
package tech.carcadex.kotlinbukkitkit.commands.service
package tech.carcadex.kotlinbukkitkit.commands.reflection.service
import org.bukkit.Bukkit
import org.bukkit.Material
@@ -9,8 +9,8 @@ class CommandRegister(
aliases: Array<String>,
desc: String?,
usage: String?,
private val executor: CommandExecutor,
private val tabCompleter: TabCompleter,
var executor: CommandExecutor,
var tabCompleter: TabCompleter,
private val pl: Plugin
) : Command(aliases[0], desc!!, usage!!, listOf(*aliases)), PluginIdentifiableCommand {
@@ -28,17 +28,15 @@ class CommandRegister(
}
companion object {
fun register(plugin: Plugin, executor: CommandExecutor, aliases: Array<String>, desc: String?, usage: String?, tabCompleter: TabCompleter) {
fun register(plugin: Plugin, executor: CommandExecutor, aliases: Array<String>,
desc: String?, usage: String?, tabCompleter: TabCompleter): CommandRegister {
if(aliases.size < 1) throw IllegalArgumentException("Could not found name")
try {
val reg = CommandRegister(aliases, desc, usage, executor, tabCompleter, plugin)
val field = Bukkit.getServer().javaClass.getDeclaredField("commandMap")
field.isAccessible = true
val map = field[Bukkit.getServer()] as CommandMap
map.register(plugin.description.name, reg)
} catch (e: Exception) {
e.printStackTrace()
}
val reg = CommandRegister(aliases, desc, usage, executor, tabCompleter, plugin)
val field = Bukkit.getServer().javaClass.getDeclaredField("commandMap")
field.isAccessible = true
val map = field[Bukkit.getServer()] as CommandMap
map.register(plugin.description.name, reg)
return reg
}
}
@@ -28,6 +28,12 @@ object TabCompleteService {
return createTabCompleter(complete)
}
fun completer(list: List<String>): TabCompleter {
return createTabCompleter(parseTags(list))
}
fun parseTags(list: List<String>): List<(CommandSender) -> List<String>> = list.map { defaultCompleteParse(it) }
private fun createTabCompleter(completes: List<(CommandSender) -> List<String>>): TabCompleter =
TabCompleter { sender, _, _, args ->
if(args.size <= completes.size)