Desenho feito por đHirođŠ#3360
Apenas mais outra framework bacana e incrĂvel para facilitar a criação e despacho de comandos! Genericamente utilizĂĄvel em mĂșltiplas plataformas diferentes, mas ao mesmo tempo incrivelmente customizĂĄvel para implementar funcionalidades especĂficas para cada plataforma.
Ela foi criada para a Loritta e para o SparklyPower, e foi planejada com as seguintes ideias:
- Comandos devem ser criados de uma forma simples e intuitiva.
- Mesmo sendo simples, precisa permitir customizar como os comandos para implementar funcionalidades especĂficas para cada plataforma.
- Precisa suportar as funcionalidades que o Kotlin oferece. (NĂŁo precisar ficar colocando
@Optional
em parĂąmetros, suportar argumentos padrĂ”es, suportar criação de comandos usando DSL, etc) - Precisa ser genĂ©rica, para que seja possĂvel implementar a framework em plataformas diversas para ter um Ășnico sistema unificado para comandos.
- Precisa suportar sub sub sub ... argumentos.
- Suportar contextos personalizados para comandos, como, por exemplo, retornar a instĂąncia de um
Player
ao digitarLoritta
! - Suportar Kotlin Coroutines.
- NĂŁo ser "mĂĄgico", como registrar comandos magicamente ao declarar eles.
Ela foi inspirada em vĂĄrias frameworks diferentes: Annotation Command Framework, Plugin Annotations e KotlinBukkitAPI, essas frameworks sĂŁo excelentes, mas cada uma tem algum problema diferente que nĂŁo se "encaixa" no que a gente precisa, por exemplo:
- NĂŁo sĂŁo feitas em Kotlin, ou seja, existem coisas redudantes que nĂŁo seriam necessĂĄrias (como
@Optional
) (Annotation Command Framework, Plugin Annotations) - Dependem de plataformas para funcionar, em vez de serem "genéricas" para usar em qualquer tipo de sistema (Plugin Annotations, KotlinBukkitAPI)
- Apenas suporta DSL (KotlinBukkitAPI)
<repository>
<id>perfectdreams-repo</id>
<url>https://repo.perfectdreams.net/</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
... coisas coisas coisas ...
<dependency>
<groupId>net.perfectdreams.commands</groupId>
<artifactId>command-framework-core</artifactId>
<version>0.0.2-SNAPSHOT</version> <!-- Veja a Ășltima versĂŁo no pom.xml! -->
</dependency>
Nestes exemplos, estamos usando a nossa implementação que fizemos para unit tests, como vocĂȘ terĂĄ que implementar algumas coisas vocĂȘ mesmo, talvez existam algumas diferenças. (Mas nada tĂŁĂŁĂŁĂŁĂŁo diferente, nĂŁo se preocupe!)
class HelloWorldCommand : DreamCommand("hello", "olĂĄ") {
@Subcommand
fun helloWorld(sender: Sender) {
// Isto serĂĄ executado se o usuĂĄrio escrever
// "hello"
// "olĂĄ"
// "hello algo_aqui_blah"
// "olĂĄ qdhd qdqgdyqwd dhqudquhd"
sender.sendMessage("OlĂĄ, mundo!")
}
}
fun main() {
val commandManager = ConsoleCommandManager() // Criar o nosso CommandManager
commandManager.registerCommand(HelloWorldCommand()) // Registrar o nosso lindo comando
while (true) {
val line = readLine()!!
commandManager.dispatchBlocking( // Caso vocĂȘ esteja usando coroutines, use apenas "dispatch"
ConsoleSender(),
line
)
}
}
E Ă© isto!
"Que nada, cadĂȘ as implementaçÔes que vocĂȘ falou??"
Verdade, elas estĂŁo aqui:
ImplementaçÔes Marotas
NĂŁo se preocupe, tem coisas aqui que vocĂȘ nĂŁo precisarĂĄ implementar (como o Sender
), ele sĂł estĂĄ aqui para demonstrar o exemplo acima!
Senders:
interface Sender {
fun sendMessage(message: String)
}
class ConsoleSender(val senderName: String) : Sender {
override fun sendMessage(message: String) {
println(message)
}
}
DeclaraçÔes de Comandos:
open class DreamCommand(override vararg val labels: String) : BaseCommand {
override val subcommands: MutableList<BaseCommand> = mutableListOf()
init {
registerSubcommands()
}
}
open class DreamDSLCommand(vararg labels: String, override val executors: List<DreamDSLExecutorWrapper>, dslSubcommands: List<BaseDSLCommand>) : DreamCommand(*labels), BaseDSLCommand {
init {
// lol nope, vamos ignorar todos os subcomandos registrados pela classe principal, elas sĂŁo chatas!
subcommands.clear()
// E colocar todos os subcomandos de DSL apĂłs iniciar
subcommands.addAll(dslSubcommands)
// Deste jeito ainda Ă© possĂvel usar o "subcommands" para adicionar subcomandos de outras classes! Yay!
}
}
Command Manager:
class ConsoleCommandManager : DispatchableCommandManager<Sender, DreamCommand, DreamDSLCommand>() {
private val commands = mutableListOf<DreamCommand>()
override fun registerCommand(command: DreamCommand) {
commands.add(command)
}
override fun unregisterCommand(command: DreamCommand) {
commands.remove(command)
}
override fun getRegisteredCommands(): List<DreamCommand> {
return commands
}
override suspend fun dispatch(sender: Sender, command: DreamCommand, label: String, arguments: Array<String>): Boolean {
if (!command.labels.contains(label))
return false
for (subCommand in command.subcommands) {
if (dispatch(sender, subCommand as DreamCommand, arguments.drop(0).firstOrNull() ?: "", arguments.drop(1).toTypedArray()))
return true
}
return execute(sender, command, arguments)
}
}
E Ă© claro, Ă© possĂvel criar exemplos mais complexos, por exemplo:
class RoleplayCommand : DreamCommand("undertale") {
@Subcommand
fun root(sender: Sender) {
sender.sendMessage("Para começar o roleplay, use \"undertale roleplay personagem\"")
}
inner class RoleplaySubCommand : DreamCommand("roleplay") {
@Subcommand
fun showCharacters(sender: Sender) {
Character.values().forEach { sender.sendMessage(it.name) }
}
@Subcommand
fun chooseCharacter(sender: Sender, @InjectArgument(ArgumentType.PEEK_STRING) characterName: String, character: Character?) {
if (character == null) {
sender.sendMessage("O personagem $characterName nĂŁo existe, bobinho!")
return
}
sender.sendMessage("VocĂȘ escolheu $character!")
}
}
enum class Character {
ASRIEL,
TORIEL,
FRISK,
CHARA
}
}
VocĂȘ pode atĂ© usar DSLs para criar comandos!
class DomainSpecificLanguageCommand {
fun generateCommand() = command("dslexample") {
command("easter") {
whenever<Sender, String?> { sender, input ->
if (input == null) {
sender.sendMessage("Apenas os fortes que possuem a senha poderĂŁo ver o easter egg.")
return@whenever
}
if (input != "lorotajubinha") {
sender.sendMessage("Errou! $input não é a senha, tente novamente, mas agora com mais confiança!")
return@whenever
}
sender.sendMessage("ParabĂ©ns, vocĂȘ encotrou o easter egg! Guarde ele com muito carinho ^-^ https://bit.ly/segredolori")
}
}
whenever<Sender> {
it.sendMessage("i love u :3")
}
}
}
E Ă© isto, have fun!
YourKit, criadores do YourKit Java Profiler, suportam projetos open source de todos os tipos com o excelente profiler de aplicaçÔes Java e .NET. NĂłs agradecemos por darem uma licença open source para conseguir deixar nossos projetos mais incrĂveis e maravilhosos para todos os nossos usuĂĄrios!
O cĂłdigo-fonte da Loritta estĂĄ licenciado sob a GNU Affero General Public License v3.0