CommandTabCompleteListener.java

package dev.aura.bungeechat.listener;

import dev.aura.bungeechat.api.utils.ChatUtils;
import dev.aura.bungeechat.command.BaseCommand;
import dev.aura.bungeechat.util.LoggerHelper;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.TabCompleteEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;

public class CommandTabCompleteListener implements Listener {
  private static boolean useModernMethods = true;

  private Map<String, BaseCommand> bungeeChatCommands = null;

  @EventHandler
  public void onTabComplete(TabCompleteEvent event) {
    final String message = event.getCursor();

    if (!ChatUtils.isCommand(message)) return;

    final String[] allArgs = event.getCursor().split(" ", -1);
    final String command = allArgs[0].substring(1);

    if (allArgs.length == 1) return;
    if (!bungeeChatCommands.containsKey(command)) return;

    final BaseCommand commandHandler = bungeeChatCommands.get(command);
    CommandSender sender = getCommandSenderFromEvent(event);

    if (!commandHandler.hasPermission(sender)) return;

    String[] args = Arrays.copyOfRange(allArgs, 1, allArgs.length);
    Collection<String> suggestions = null;

    try {
      suggestions = commandHandler.tabComplete(sender, args);
    } catch (RuntimeException e) {
      LoggerHelper.warning("Uncaught error during tabcomplete of /" + command, e);
    }

    if (suggestions != null) event.getSuggestions().addAll(suggestions);
  }

  public void updateBungeeChatCommands() {
    bungeeChatCommands = getBungeeChatCommands();
  }

  private static Map<String, BaseCommand> getBungeeChatCommands() {
    return ProxyServer.getInstance().getPluginManager().getCommands().stream()
        .filter(entry -> entry.getValue() instanceof BaseCommand)
        .collect(Collectors.toMap(Map.Entry::getKey, entry -> (BaseCommand) entry.getValue()));
  }

  @SuppressWarnings("deprecation") // For the backwards compatibility
  private static CommandSender getCommandSenderFromEvent(TabCompleteEvent event) {
    final ProxyServer server = ProxyServer.getInstance();
    Stream<ProxiedPlayer> players = server.getPlayers().stream();

    if (useModernMethods) {
      try {
        final SocketAddress senderSocketAddress = event.getSender().getSocketAddress();
        players = players.filter(player -> senderSocketAddress.equals(player.getSocketAddress()));

      } catch (NoSuchMethodError e) {
        // If it fails once, we need to use the old method always
        useModernMethods = false;
        return getCommandSenderFromEvent(event);
      }
    } else {
      final InetSocketAddress senderInetSocketAddress = event.getSender().getAddress();
      players = players.filter(player -> senderInetSocketAddress.equals(player.getAddress()));
    }

    return players.findAny().map(CommandSender.class::cast).orElseGet(server::getConsole);
  }
}