Войти или зарегистрироваться
  • Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.

Утилита Утилита для упрощения создания Tab Completer и команд.

Статус
Закрыто для дальнейших ответов.

Zerkala8D4

Активный участник
Пользователь
Баллы
61
Имя в Minecraft
AlohaDanse
Представляю вашему вниманию простенькую утилиту для быстрой регистрации таб комплитеров и вообще, создания команд.

Как это работает?​
Аннотация, которая ставится в начало класса вашей команды, и в ней содержится вся основная информация: название команды (сама команда), права на команду и от кого будет выполнятся команда.

Примеры и сам код:​
Создаем основной интерфейс:
Код:
package com.example.package;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CommandInfo {
    String name();
    String permission() default "";
    boolean requirePlayer();
}

В этом интерфейсе мы задаем:
  • name() - название команды
  • permission() - права (по дефолту, если не написали, пусто)
  • requirePlayer() - true = команда используется только игроком, false = команда может использоваться в консоли и игроком тоже.

Класс, который мы будет использовать для классов наших команд:

Код:
package com.example.package;

import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

import java.util.Objects;

public class PluginCommand implements CommandExecutor {
    private final CommandInfo commandInfo;

    public PluginCommand() {
        this.commandInfo = getClass().getDeclaredAnnotation(CommandInfo.class);
        Objects.requireNonNull(commandInfo, "Команды должны быть помечены аннотацией @CommandInfo)");
    }

    public CommandInfo getCommandInfo() {
        return commandInfo;
    }


    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (!commandInfo.permission().isEmpty()) {
            if (!sender.hasPermission(commandInfo.permission())) {
                Player player = (Player) sender;
                sender.sendMessage(ChatColor.RED +"У вас нету прав!");
                return true;
            }
        }

        if (commandInfo.requirePlayer()) {
            if (!(sender instanceof Player)) {
                sender.sendMessage(ChatColor.RED + "Ты не игрок!");
                return true;
            }
            execute((Player) sender, args);
        }

        execute(sender, args);
        return true;
    }

    public void execute(Player player, String[] args) {}
    public void execute(CommandSender sender, String[] args) {}
}

Как это использовать:
Код:
package com.example.package.commands;

import com.example.package.CommandInfo;
import com.example.package.PluginCommand;
import org.bukkit.entity.Player;

@CommandInfo(name = "example", permission = "example.use", requirePlayer = true)
// Команда /example с правами example.use может использоваться только игроками.
public class FraqMineCommand extends PluginCommand {

    @Override
    public void execute(Player player, String[] args) {
        player.sendMessage("Привет!");
    }
}
Тут мы вначале названия нашего класса написали аннотацию @CommandInfo(), в которую передали name, permission и requirePlayer.

Теперь я хочу показать, как при помощи рефлексии быстро регистрировать наши команды.
Но для начала нужно добавить в свой проект зависимость:
MAVEN
Код:
<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.10.2</version>
</dependency>
Gradle
Код:
implementation 'org.reflections:reflections:0.10.2'

После добавления зависимости пишем в методе onEnable() в нашем основном классе вот это:
Код:
String packageName = getClass().getPackage().getName();
for (Class<? extends PluginCommand> clazz : new Reflections(packageName + ".Commands") // тут вы можете поставить путь до своего пакета с классами команд
        .getSubTypesOf(PluginCommand.class)
) {
    try {
        PluginCommand pluginCommand = clazz.getDeclaredConstructor().newInstance();
        getCommand(pluginCommand.getCommandInfo().name()).setExecutor(pluginCommand);
    } catch (InstantiationException | InvocationTargetException | IllegalAccessException |
             NoSuchMethodException e) {
        throw new RuntimeException(e);
    }
}

И все! Все ваши команды будут автоматически регистрироваться при загрузке вашего плагина!

Источник: Jordan Osterberg


И это все?​
Нет, это не все! Хочу предоставить, почти такой же скрипт для создания Tab Completer, используя все те же аннотации.

Основной интерфейс:
Код:
package com.example.package.TabCompleter;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@Repeatable(TabCompleterConfigs.class)
public @interface TabCompleterConfig {
    TabType type();
    String[] completers() default {};
    int argument();
    String[] follow() default {};
}[/SIZE]

Пройдемся по переменным:




    • type() - тип (объяснение будет ниже)
    • completers() - лист с автозаполнителями
    • argument() - аргумент начиная с 1, на котором будет действовать этот TabCompleter
Что такой type()? Эта переменная отвечает за тип комплитера. Их может быть много, но я выделил три из них: PLAYERS, CUSTOM, MATERIAL. PLAYERS - возвращает список онлайн игроков, CUSTOM - используется вместе с completers(), MATERIAL - чисто например возвращает список ID предметов.

Enum со всеми типами:
Код:
package com.example.package.TabCompleter;

public enum TabType {
    PLAYERS,
    CUSTOM,
    MATERIAL
}[/SIZE]

И еще нам нужен один интерфейс, который дает возможность добавлять неограниченное количество таб комплитеров в нашу команду:
Код:
package com.example.package.TabCompleter;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface TabCompleterConfigs {
    TabCompleterConfig[] value();
}[/SIZE]

И вот основной класс для регистрации наших автозаполнителей:
Код:
package com.example.package;

import com.example.package.Example;
import com.example.package.TabCompleter.TabCompleterConfig;
import com.example.package.TabCompleter.TabCompleterConfigs;
import com.example.package.TabCompleter.TabType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.HumanEntity;
import org.reflections.Reflections;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class CommandTabCompleter {

    public static void registerTabCompleters(FraqMine plugin) {
        String packageName = plugin.getClass().getPackage().getName();

        // Сканируем все команды (CommandExecutor)
        for (Class<?> clazz : new Reflections(packageName + ".Commands").getSubTypesOf(org.bukkit.command.CommandExecutor.class)) {
            if (clazz.isAnnotationPresent(TabCompleterConfigs.class)) {
                registerTabCompleter(plugin, clazz, clazz.getAnnotation(TabCompleterConfigs.class).value());
            } else if (clazz.isAnnotationPresent(TabCompleterConfig.class)) {
                registerTabCompleter(plugin, clazz, new TabCompleterConfig[]{clazz.getAnnotation(TabCompleterConfig.class)});
            }
        }
    }

    private static void registerTabCompleter(FraqMine plugin, Class<?> commandClass, TabCompleterConfig[] annotations) {
        try {
            // Получаем аннотацию @CommandInfo
            CommandInfo commandInfo = commandClass.getAnnotation(CommandInfo.class);
            if (commandInfo == null) {
                plugin.getLogger().warning("Класс " + commandClass.getName() + " не имеет аннотации @CommandInfo!");
                return;
            }

            String commandName = commandInfo.name(); // Используем имя команды из аннотации

            PluginCommand command = plugin.getCommand(commandName);
            if (command == null) {
                plugin.getLogger().warning("Команда '" + commandName + "' не зарегистрирована!");
                return;
            }

            TabCompleter tabCompleter = (sender, cmd, alias, args) -> {
                for (TabCompleterConfig annotation : annotations) {
                    String input = (args.length > 0 ? args[args.length - 1] : "").toLowerCase();

                    // Если указаны условия follow, проверяем их:
                    if (annotation.follow().length > 0) {
                        // Если в follow содержится "*", условие считается выполненным для любого значения
                        boolean wildcard = Arrays.asList(annotation.follow()).contains("*");
                        if (!wildcard) {
                            // Определяем предыдущий аргумент:
                            // Если ожидается аргумент 2, то предыдущий – это args[0]
                            // Для аргументов больше 2 – берем args[annotation.argument() - 2]
                            if (annotation.argument() == 2) {
                                if (args.length < 1) continue;
                                String previousArg = args[0].toLowerCase();
                                if (Arrays.stream(annotation.follow()).noneMatch(f -> f.equalsIgnoreCase(previousArg))) {
                                    continue;
                                }
                            } else {
                                if (args.length < annotation.argument()) continue;
                                String previousArg = args[annotation.argument() - 2].toLowerCase();
                                if (Arrays.stream(annotation.follow()).noneMatch(f -> f.equalsIgnoreCase(previousArg))) {
                                    continue;
                                }
                            }
                        }
                    }

                    if (args.length == annotation.argument()) {
                        if (annotation.type() == TabType.PLAYERS) {
                            return Bukkit.getOnlinePlayers().stream()
                                    .map(HumanEntity::getName)
                                    .filter(name -> name.toLowerCase().startsWith(input))
                                    .collect(Collectors.toList());
                        } else if (annotation.type() == TabType.CUSTOM) {
                            return Arrays.stream(annotation.args())
                                    .filter(val -> val.toLowerCase().startsWith(input))
                                    .collect(Collectors.toList());
                        } else if (annotation.type() == TabType.MATERIAL) {
                            return Arrays.stream(Material.values())
                                    .map(material -> material.getKey().toString()
                                            .replace(material.getKey().getNamespace() + ":", ""))
                                    .filter(id -> id.toLowerCase().startsWith(input))
                                    .collect(Collectors.toList());
                        }
                    }
                }
                return List.of();
            };

            // Назначаем TabCompleter для команды
            command.setTabCompleter(tabCompleter);
        } catch (Exception e) {
            plugin.getLogger().severe("Ошибка при регистрации TabCompleter для " + commandClass.getName() + ": " + e.getMessage());
        }
    }

}

Далее мы должны добавить в метод onEnable() нашего плагина вот это:
Код:
CommandTabCompleter.registerTabCompleters(this);

Как это работает в классе команд?

Код:
package com.example.package.commands;

import com.example.package.CommandInfo;
import com.example.package.PluginCommand;
import com.example.package.TabCompleter.TabCompleterConfig;
import com.example.package.TabCompleter.TabType;
import org.bukkit.entity.Player;

@TabCompleterConfig(type = TabType.PLAYERS, argument = 1)
@TabCompleterConfig(type = TabType.CUSTOM, args = {"hi", "test", "ky"}, argument = 2)
@TabCompleterConfig(type = TabType.MATERIAL, argument = 3)
@CommandInfo(name = "example", permission = "example.use", requirePlayer = true)

public class ExampleCommand extends PluginCommand {

    @Override
    public void execute(Player player, String[] args) {
        if (args.length == 1 && args[0].equals("hi")) {
            // code
        }
        //....
    }
}

В игре:

Снимок экрана 2025-02-03 163501.png Снимок экрана 2025-02-03 163510.png Снимок экрана 2025-02-03 163517.png ​
 
Последнее редактирование:
Изменения:
  • Добавил follow() для проверки предыдущего аргумента.
  • Если в follow() строка "*", то возвращает весь список
Пример:
Код:
@TabCompleterConfig(type = TabType.CUSTOM, argument = 1, args = {"give"})
@TabCompleterConfig(type = TabType.PLAYERS, argument = 2, follow = {"give"})
@TabCompleterConfig(type = TabType.MATERIAL, argument = 3, follow = {"*"})
​
 
Последнее редактирование:
Статус
Закрыто для дальнейших ответов.

Поделиться этой страницей

Назад
Верх