paper 編 part1 コマンド補完
基本中の基本でありながら早速引っ掛けがあったのでメモ
🎵 本日の一曲
pv がしぬほどかわいい。
CommandExecutor vs TabExecutor
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
direction TB
class CommandExecutor {
onCommand()
}
class TabExecutor {
onTabComplete()
}
<<Interface>> CommandExecutor
<<Interface>> TabExecutor
TabExecutor --|> CommandExecutor
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.command.TabExecutor
class ExampleCommand : TabExecutor {
override fun onTabComplete(
p0: CommandSender,
p1: Command,
p2: String,
p3: Array<out String>?
): List<String?>? {
TODO("Not yet implemented")
}
override fun onCommand(
p0: CommandSender,
p1: Command,
p2: String,
p3: Array<out String>?
): Boolean {
TODO("Not yet implemented")
}
}
TabExecutor を実装すれば、CommandExecutor と TabExecutor 両方を実装することになります。
TabExecutor には、onTabComplete というメソッドがあります。 これはコマンド補完機能を提供するものです。
基本的にはTabExecutorを使おう!
Tab 補完あれこれ
onCommand メソッドに関する情報はたくさんあると思うので、ここでは onTabComplete のメモです
基本的な使い方はこんな感じ
override fun onTabComplete(
sender: CommandSender,
command: Command,
label: String,
args: Array<out String>
): MutableList<String>? {
return when {
args[0] == "add" -> Material.entries.filter { it.isBlock }.map { it.name.lowercase() }.toMutableList()
args.isEmpty() -> "add,del,list".split(",").toMutableList()
else -> null
}
}
kotlin の場合は MutableList で返すことになります。 とりあえず java では ArrayList、rust では mut Vec ですね。
whenが式なのを生かして、when の結果をそのまま return に流してます。
kotlin を使うメリット 1 ですね。
note
rust の使い勝手に近いならそれはメリットですよね!(圧)
onTabComplete が呼ばれるタイミング
実際に調べてみます。
package org.adw39.examplePlugin2
import org.adw39.examplePlugin2.commands.ExampleCommand
import org.bukkit.plugin.java.JavaPlugin
class ExamplePlugin2 : JavaPlugin() {
override fun onEnable() {
getCommand("example")?.setExecutor(ExampleCommand())
}
override fun onDisable() {
// Plugin shutdown logic
}
}
package org.adw39.examplePlugin2.commands
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.command.TabExecutor
class ExampleCommand : TabExecutor {
override fun onTabComplete(
p0: CommandSender,
p1: Command,
p2: String,
p3: Array<out String>?
): List<String?>? {
p3?.forEach {
println(it)
}
return null
}
override fun onCommand(
p0: CommandSender,
p1: Command,
p2: String,
p3: Array<out String>?
): Boolean {
return true
}
}
tip
array は配列なので、forEach で中身を分解して出力します。
tip
内部に Logger オブジェクトが存在しますが、println も使用できます。(多分非推奨)
resources/plugin.yml
name: MyPaperPlugin
version: 0.0.1
main: org.adw39.examplePlugin2.ExamplePlugin2
description: An example plugin
author: nikki
website: https://adw39.org
api-version: "1.21.0"
commands:
example:
description: テスト
usage: "/example <arg>"
permission: org.adw39.examplePlugin2.example
note
ファイルの位置は、package 文をみていただくのが早いかと思います。
複雑になり始めたら tree を載せます。
warning
plugin.yaml の編集を忘れずに!!
実行結果
/コマンド名 コマンド引数1 コマンド引数2... と入力しますが、
実装した Executor を**getCommandで登録した時に用いた名前がコマンド名となります。**
コマンド名が入力され、引数を入力する際に TabExecutor が呼び出されます。
引数の入力が変更される度にonTabCompleteが呼び出されます。 Tab が入力された時に呼び出されるわけではない模様
ブロック名で補完させる
worldeditのようなプラグインを作りたくなった場合、ブロック名の補完は必須です。
最も単純な方補は、Material列挙型を使うことです。
note
minecraft:oak_logのような表記を名前空間付き idなどと呼ばれるそうです。
今回は名前空間を省略いたします。
また、ハーフブロックなどはブロックステータスを持つのですが、
こちらも省略します。
Material 列挙隊をみてみよう
- ACACIA_BOAT
- ACACIA_BUTTON
- ACACIA_CHEST_BOAT ...
- LEGACY_RECORD_12 Link icon
と、ブロック id が並んでいます。
また、メソッドにはisAir、isBlockなど、便利そうなものが実装されています。
実際に実装してみる
kotlin では、enum につくentriesが活用できそうです。
これは、一応 getter らしく、()は不要とのこと。 中身はイテレーターになってますです!
isBlockで block に限定し、文字列に直して mutableList で返却すれば良さそうです。
package org.adw39.examplePlugin2.commands
import org.bukkit.Material
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.command.TabExecutor
class ExampleCommand : TabExecutor {
override fun onTabComplete(
p0: CommandSender,
p1: Command,
p2: String,
p3: Array<out String>?
): List<String?>? {
println(p3?.getOrNull(0) ?: "")
return if (p3?.size == 1) {
Material.entries.filter { it.isBlock }.map {it.name.lowercase() }.toMutableList()
} else {
mutableListOf("")
}
}
override fun onCommand(
p0: CommandSender,
p1: Command,
p2: String,
p3: Array<out String>?
): Boolean {
val block = p3?.getOrNull(0) ?: return false
p0.sendMessage(block)
return true
}
}
entries はイテレーターですので、filter や map メソッドが活用できます。
it.isBlockはもはや英語として通じてしまいそうで面白いです。
lowercase()にしてるのは、標準実装されているコマンドsetなどが、lowercase で入力するからです。
(あと入力が楽)
実行例

まとめ
コマンド補完は便利だから実装しよう。
ご意見募集中
当サイトのリポジトリにて、issue 募集中です!
- 投稿には github アカウントが必要です。
- テンプレート用意してます。 ぜひ活用してください。