1. Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.
    Скрыть объявление
Скрыть объявление
В преддверии глобального обновления, мы проводим исследования, которые помогут нам сделать опыт пользования форумом ещё удобнее. Помогите нам, примите участие!

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

Тема в разделе "Руководства, инструкции, утилиты", создана пользователем Zerkala8D4, 3 фев 2025.

Статус темы:
Закрыта.
  1. Автор темы
    Zerkala8D4

    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
     
    Последнее редактирование: 10 фев 2025
  2. Автор темы
    Zerkala8D4

    Zerkala8D4 Активный участник Пользователь

    Баллы:
    61
    Имя в Minecraft:
    AlohaDanse
    Изменения:
    • Добавил 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 = {"*"})
    
     
    Последнее редактирование: 3 фев 2025
Статус темы:
Закрыта.

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