Давайте попробуем что-нибудь посложнее.
Сделаем команду kill
, что она должна делать:
1. Если просто написать /kill
, то должна убить написавшего
2. Если написать /kill НикИгрока
, то должна убить выбранного игрока
3. Если написать /kill -t ВремяВСекундах
, то должна убить написавшего через N секунд
4. Если написать /kill -НикИгрока -t ВремяВСекундах
, то должна убить выбранного игрока через N секунд.
Уже представляете, какой это ад в Spigot? В Sponge это делается проще, но обучиться немного сложнее, поверьте, это куда удобнее, чем в Spigot.
В первом примере мы использовали лямбда выражение в качестве того, что должна выполнить команда. Теперь же команда будет чуть длиннее, поэтому я буду использовать отдельный класс.
Класс должен реализовывать интерфейс CommandExecutor
.
Трудные слова? Давайте проще!
Код
public class PlayerKillCommand implements CommandExecutor {
@Override
public CommandResult execute(CommandSource src, CommandContext args) throws CommandException {
}
}
Давайте пока оставим наш класс и перейдем к созданию CommandSpec
.
Код
CommandSpec kill = CommandSpec.builder()
.description(Text.of("Убивает выбранного игрока"))
.permission("commandsexample.cmd.kill")
.arguments(
GenericArguments.onlyOne(GenericArguments.playerOrSource(Text.of("player"))),
GenericArguments.requiringPermission(
GenericArguments.flags()
.valueFlag(GenericArguments.integer(Text.of("time")),"t")
.buildWith(GenericArguments.none()), "commandsexample.cmd.kill.timed")
)
.executor(new PlayerKillCommand(this))
.build();
Аргументы
Спойлер
В этом примере у нас добавились аргументы.
Коротко об аргументах:
1. Аргументы могут быть с одиночным значением
2. Аргументы могут быть с множественным значением
Представим такую ситуацию, мы хотим убить игрока Dimon228
.
На сервере 3 игрока с никами Admin
, Diman228
и DimasTroll
Если мы напишем /kill Dima
, то мы получим двух игроков, Diman228
и DimasTroll
.(если написать /kill a
, то мы получим игрока Admin
.
Таблица с множественными и одиночными аргументами
Как же решить эту проблему?
1. Убить сразу несколько игроков
2. Потребовать ввести полный ник
Давайте остановимся на пункте 2 и вернёмся к нашей команде kill
.
В .arguments(CommandElement...)
можно передать любое кол-во аргументов. Первым аргументом будет игрок, который будет написан после kill
, например kill Diman228
или если никто не указан, то это будет сам вызывающий.
Для этого нам надо написать:
GenericArguments.playerOrSource(Text.of("player"))
Это добавит аргумент под названием player
.
Как мы помним, аргумент, связанный с игроком - множественный, а нам нужен лишь 1 игрок.
Для этого используем GenericArguments.onlyOne()
, в итоге наш аргумент выглядит так:
GenericArguments.onlyOne(GenericArguments.playerOrSource(Text.of("player")))
Кратко напишу, как это обрабатывается:
1. Если аргумент не указан, то возвращаем объект Player
вызвавшего
2. Если найдено несколько игроков, то возвращаем ошибку игроку
3. Если найден один игрок, то возвращаем его
Флаги
Спойлер
Теперь нам нужен флаг!
Код
GenericArguments.requiringPermission(
GenericArguments.flags()
.valueFlag(GenericArguments.integer(Text.of("time")),"t")
.buildWith(GenericArguments.none()), "commandsexample.cmd.kill.timed")
Флаг будет называться t
(в игре будет писаться так /kill -t Время
)
Для флага нам нужен будет permission, назовём его commandsexample.cmd.kill.timed
.
Что делает код выше:
Для permission нам нужно использовать GenericArguments.requiringPermission(CommandElement, String)
.
Напишем так:
GenericArguments.requiringPermission(GenericArguments.none(), "commandsexample.cmd.kill.timed")
Теперь нам нужен сам флаг.
Заменяем GenericArguments.none()
на
GenericArguments.flags()
.valueFlag(GenericArguments.integer(Text.of("time")),"t")
.buildWith(GenericArguments.none())
Обычный флаг, созданный через GenericArguments.flags().flag(...)
может быть только true или false, если нам нужно передать какое-то значение, то вместо flag(String...)
, нам нужно использовать valueFlag(CommandElement, String...)
Что же делает GenericArguments.flags().valueFlag(GenericArguments.integer(Text.of("time")),"t").buildWith(GenericArguments.none())
?
Создаёт флаг с названием t
и создаёт переменную флага под названием time
с типом int
.
Все аргументы после, которые идут после флага, обрабатываются с помощью CommandElement
, переданного в .buildWith(CommandElement)
, после флага у нас нет аргументов, поэтому используем GenericArguments.none()
.
В итоге у нас получается код, который я показал выше:
Код
CommandSpec kill = CommandSpec.builder()
.description(Text.of("Убивает выбранного игрока"))
.permission("commandsexample.cmd.kill")
.arguments(
GenericArguments.onlyOne(GenericArguments.playerOrSource(Text.of("player"))), // Нам нужен только 1 игрок
GenericArguments.requiringPermission( // Флаг будет с permission
GenericArguments.flags()
.valueFlag(GenericArguments.integer(Text.of("time")),"t") // Флаг принимает
.buildWith(GenericArguments.none()), "commandsexample.cmd.kill.timed") // Аргументов больше нет, поэтому используем GenericArguments.none()
)
.executor(new PlayerKillCommand(this))
.build();
С CommandSpec
разобрались, переходим к CommandExecutor
. Он куда проще, всего несколько строчек.
Код
public class PlayerKillCommand implements CommandExecutor {
private final CommandsExample plugin;
public PlayerKillCommand(CommandsExample plugin) {
this.plugin = plugin;
}
@Override
public CommandResult execute(CommandSource src, CommandContext args) throws CommandException {
Player player = args.<Player>getOne("player").get();
Optional<Integer> timeOpt = args.getOne("time");
src.sendMessage(Text.of(player.getName() + " умрет через " + timeOpt.orElse(0) + " секунд"));
Task.builder().delay(timeOpt.orElse(0), TimeUnit.SECONDS)
.execute(() -> player.offer(Keys.HEALTH, 0d))
.submit(plugin);
return CommandResult.empty();
}
}
1. Получаем игрока с помощью args.<Player>getOne("player").get();
(Если не написать <Player>
, то мы получим Object
)
2. Получаем значение time
(значение существует, если указан флаг)
3. Создаём новый поток с задержкой в time
, а если time
не указан, делаем задержку 0 секунд
4. Запускаем новый поток
Всё, что нам осталось - это зарегистрировать команду!
Это мы уже умеем, пишем так:
Sponge.getCommandManager().register(this, kill, "kill", "k");
Давайте протестируем:
Скриншоты
Всё работает!
Вы уже освоили 95% API для команд! Остались только подкоманды! Это куда проще, чем флаги и аргументы.