All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.alangomes.springspigot.command.CommandService Maven / Gradle / Ivy

There is a newer version: 0.20.7
Show newest version
package dev.alangomes.springspigot.command;

import dev.alangomes.springspigot.context.Context;
import dev.alangomes.springspigot.picocli.CommandLineDefinition;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.bukkit.command.Command;
import org.bukkit.command.CommandMap;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.SimplePluginManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import picocli.CommandLine;
import picocli.CommandLine.Model.CommandSpec;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_SINGLETON;

@Slf4j
@Component
@Scope(SCOPE_SINGLETON)
@ConditionalOnBean(annotation = CommandLine.Command.class)
class CommandService {

    private static final String DEFAULT_COMMAND_NAME = "
"; @Autowired private CommandLineDefinition commandLineDefinition; @Autowired private Context context; @Autowired private Plugin plugin; @Autowired private CommandExecutor commandExecutor; @Autowired private ResourceLoader resourceLoader; @Autowired private ConfigurableApplicationContext applicationContext; @Getter private boolean registered; private Class bukkitClass; @PostConstruct void init() { try { List commandSpecs = getCommands(); val packageName = plugin.getServer().getClass().getPackage().getName(); val version = packageName.substring(packageName.lastIndexOf('.') + 1); this.bukkitClass = Class.forName("org.bukkit.craftbukkit." + version + ".CraftServer", false, resourceLoader.getClassLoader()); commandSpecs.forEach(this::registerCommand); log.debug("Succesfully registered {} commands", commandSpecs.size()); registered = true; } catch (Throwable t) { log.debug("Failed to register commands natively, falling back to event listeners", t); } } @PreDestroy void destroy() { try { List commandSpecs = getCommands(); commandSpecs.forEach(this::unregisterCommand); } catch (Throwable t) { log.debug("Failed to unregister commands natively", t); } } @SneakyThrows public void registerCommand(CommandSpec commandSpec) { val commandMapField = bukkitClass.getDeclaredField("commandMap"); commandMapField.setAccessible(true); val commandMap = (SimpleCommandMap) commandMapField.get(plugin.getServer()); commandMap.register(plugin.getName().toLowerCase(), new WrappedCommand(commandSpec, context, commandExecutor)); } @SneakyThrows public void unregisterCommand(CommandSpec commandSpec) { val commandName = commandSpec.name(); val manager = (SimplePluginManager) plugin.getServer().getPluginManager(); val commandMapField = SimplePluginManager.class.getDeclaredField("commandMap"); commandMapField.setAccessible(true); val map = (CommandMap) commandMapField.get(manager); val knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands"); knownCommandsField.setAccessible(true); val knownCommands = (Map) knownCommandsField.get(map); val command = knownCommands.get(commandName); if (command != null) { command.unregister(map); knownCommands.remove(commandName); } } public List getCommands() { val commandLine = commandLineDefinition.build(applicationContext); val commandSpec = commandLine.getCommandSpec(); if (DEFAULT_COMMAND_NAME.equals(commandSpec.name())) { return commandSpec.subcommands().values().stream() .map(CommandLine::getCommandSpec) .filter(distinctByKey(CommandSpec::name)) .collect(Collectors.toList()); } return Collections.singletonList(commandSpec); } private static Predicate distinctByKey(Function keyExtractor) { Set seen = ConcurrentHashMap.newKeySet(); return t -> seen.add(keyExtractor.apply(t)); } }