me.lucko.luckperms.bukkit.LPBukkitPlugin.java Source code

Java tutorial

Introduction

Here is the source code for me.lucko.luckperms.bukkit.LPBukkitPlugin.java

Source

/*
 * Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

package me.lucko.luckperms.bukkit;

import lombok.Getter;

import com.google.common.collect.ImmutableMap;

import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.bukkit.inject.Injector;
import me.lucko.luckperms.bukkit.messaging.BungeeMessagingService;
import me.lucko.luckperms.bukkit.messaging.LilyPadMessagingService;
import me.lucko.luckperms.bukkit.model.ChildPermissionProvider;
import me.lucko.luckperms.bukkit.model.DefaultsProvider;
import me.lucko.luckperms.bukkit.model.LPPermissible;
import me.lucko.luckperms.bukkit.vault.VaultHook;
import me.lucko.luckperms.common.api.ApiHandler;
import me.lucko.luckperms.common.api.ApiProvider;
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.commands.sender.Sender;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.config.LuckPermsConfiguration;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.contexts.ContextManager;
import me.lucko.luckperms.common.contexts.ServerCalculator;
import me.lucko.luckperms.common.core.UuidCache;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.data.Importer;
import me.lucko.luckperms.common.debug.DebugHandler;
import me.lucko.luckperms.common.dependencies.DependencyManager;
import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.locale.NoopLocaleManager;
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.managers.impl.GenericGroupManager;
import me.lucko.luckperms.common.managers.impl.GenericTrackManager;
import me.lucko.luckperms.common.managers.impl.GenericUserManager;
import me.lucko.luckperms.common.messaging.InternalMessagingService;
import me.lucko.luckperms.common.messaging.RedisMessaging;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.storage.Storage;
import me.lucko.luckperms.common.storage.StorageFactory;
import me.lucko.luckperms.common.storage.StorageType;
import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
import me.lucko.luckperms.common.tasks.UpdateTask;
import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.common.utils.LoggerImpl;
import me.lucko.luckperms.common.utils.PermissionCache;

import org.bukkit.World;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;

import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Getter
public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
    private Set<UUID> ignoringLogs;
    private LPBukkitScheduler scheduler;
    private VaultHook vaultHook = null;
    private LuckPermsConfiguration configuration;
    private UserManager userManager;
    private GroupManager groupManager;
    private TrackManager trackManager;
    private Storage storage;
    private InternalMessagingService messagingService = null;
    private UuidCache uuidCache;
    private BukkitListener listener;
    private ApiProvider apiProvider;
    private Logger log;
    private Importer importer;
    private DefaultsProvider defaultsProvider;
    private ChildPermissionProvider childPermissionProvider;
    private LocaleManager localeManager;
    private CachedStateManager cachedStateManager;
    private ContextManager<Player> contextManager;
    private WorldCalculator worldCalculator;
    private CalculatorFactory calculatorFactory;
    private BufferedRequest<Void> updateTaskBuffer;
    private boolean started = false;
    private DebugHandler debugHandler;
    private BukkitSenderFactory senderFactory;
    private PermissionCache permissionCache;

    @Override
    public void onEnable() {
        scheduler = new LPBukkitScheduler(this);

        localeManager = new NoopLocaleManager();
        senderFactory = new BukkitSenderFactory(this);
        log = new LoggerImpl(getConsoleSender());
        LuckPermsPlugin.sendStartupBanner(getConsoleSender(), this);

        ignoringLogs = ConcurrentHashMap.newKeySet();
        debugHandler = new DebugHandler(scheduler.getAsyncBukkitExecutor(), getVersion());
        permissionCache = new PermissionCache(scheduler.getAsyncBukkitExecutor());

        getLog().info("Loading configuration...");
        configuration = new BukkitConfig(this);
        configuration.init();
        configuration.loadAll();

        Set<StorageType> storageTypes = StorageFactory.getRequiredTypes(this, StorageType.H2);
        DependencyManager.loadDependencies(this, storageTypes);

        // setup the Bukkit defaults hook
        defaultsProvider = new DefaultsProvider();
        childPermissionProvider = new ChildPermissionProvider();

        // give all plugins a chance to load their defaults, then refresh.
        scheduler.doSyncLater(() -> {
            defaultsProvider.refresh();
            childPermissionProvider.setup();

            getServer().getScheduler().runTaskAsynchronously(this, () -> {
                for (Map.Entry<String, Boolean> e : defaultsProvider.getOpDefaults().entrySet()) {
                    permissionCache.offer(e.getKey());
                }

                for (Map.Entry<String, Boolean> e : defaultsProvider.getNonOpDefaults().entrySet()) {
                    permissionCache.offer(e.getKey());
                }

                ImmutableMap<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> permissions = childPermissionProvider
                        .getPermissions();
                for (Map.Entry<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> e : permissions
                        .entrySet()) {
                    permissionCache.offer(e.getKey().getKey());
                    for (Map.Entry<String, Boolean> e1 : e.getValue().entrySet()) {
                        permissionCache.offer(e1.getKey());
                    }
                }
            });

        }, 1L);

        // register events
        PluginManager pm = getServer().getPluginManager();
        listener = new BukkitListener(this);
        pm.registerEvents(listener, this);

        // initialise datastore
        storage = StorageFactory.getInstance(this, StorageType.H2);

        // initialise messaging
        String messagingType = getConfiguration().get(ConfigKeys.MESSAGING_SERVICE).toLowerCase();
        if (messagingType.equals("none") && getConfiguration().get(ConfigKeys.REDIS_ENABLED)) {
            messagingType = "redis";
        }
        if (messagingType.equals("redis")) {
            getLog().info("Loading redis...");
            if (getConfiguration().get(ConfigKeys.REDIS_ENABLED)) {
                RedisMessaging redis = new RedisMessaging(this);
                try {
                    redis.init(getConfiguration().get(ConfigKeys.REDIS_ADDRESS),
                            getConfiguration().get(ConfigKeys.REDIS_PASSWORD));
                    getLog().info("Loaded redis successfully...");

                    messagingService = redis;
                } catch (Exception e) {
                    getLog().warn("Couldn't load redis...");
                    e.printStackTrace();
                }
            } else {
                getLog().warn("Messaging Service was set to redis, but redis is not enabled!");
            }
        } else if (messagingType.equals("bungee")) {
            getLog().info("Loading bungee messaging service...");
            BungeeMessagingService bungeeMessaging = new BungeeMessagingService(this);
            bungeeMessaging.init();
            messagingService = bungeeMessaging;
        } else if (messagingType.equals("lilypad")) {
            getLog().info("Loading LilyPad messaging service...");
            if (getServer().getPluginManager().getPlugin("LilyPad-Connect") == null) {
                getLog().warn("LilyPad-Connect plugin not present.");
            } else {
                LilyPadMessagingService lilyPadMessaging = new LilyPadMessagingService(this);
                lilyPadMessaging.init();
                messagingService = lilyPadMessaging;
            }
        } else if (!messagingType.equals("none")) {
            getLog().warn("Messaging service '" + messagingType + "' not recognised.");
        }

        // setup the update task buffer
        updateTaskBuffer = new BufferedRequest<Void>(1000L, this::doAsync) {
            @Override
            protected Void perform() {
                new UpdateTask(LPBukkitPlugin.this).run();
                return null;
            }
        };

        // load locale
        localeManager = new SimpleLocaleManager();
        File locale = new File(getDataFolder(), "lang.yml");
        if (locale.exists()) {
            getLog().info("Found locale file. Attempting to load from it.");
            try {
                localeManager.loadFromFile(locale);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // register commands
        getLog().info("Registering commands...");
        BukkitCommand commandManager = new BukkitCommand(this);
        PluginCommand main = getServer().getPluginCommand("luckperms");
        main.setExecutor(commandManager);
        main.setTabCompleter(commandManager);
        main.setAliases(Arrays.asList("perms", "lp", "permissions", "perm"));

        // load internal managers
        getLog().info("Loading internal permission managers...");
        uuidCache = new UuidCache(this);
        userManager = new GenericUserManager(this);
        groupManager = new GenericGroupManager(this);
        trackManager = new GenericTrackManager();
        importer = new Importer(commandManager);
        calculatorFactory = new BukkitCalculatorFactory(this);
        cachedStateManager = new CachedStateManager(this);

        contextManager = new ContextManager<>();
        worldCalculator = new WorldCalculator(this);
        pm.registerEvents(worldCalculator, this);
        contextManager.registerCalculator(worldCalculator);
        contextManager.registerCalculator(new ServerCalculator<>(getConfiguration()));

        // Provide vault support
        tryVaultHook(false);

        // register with the LP API
        getLog().info("Registering API...");
        apiProvider = new ApiProvider(this);
        ApiHandler.registerProvider(apiProvider);
        getServer().getServicesManager().register(LuckPermsApi.class, apiProvider, this, ServicePriority.Normal);

        // schedule update tasks
        int mins = getConfiguration().get(ConfigKeys.SYNC_TIME);
        if (mins > 0) {
            long ticks = mins * 60 * 20;
            scheduler.doAsyncRepeating(() -> updateTaskBuffer.request(), ticks);
        }
        scheduler.doAsyncLater(() -> updateTaskBuffer.request(), 40L);

        // run an update instantly.
        updateTaskBuffer.requestDirectly();

        // register tasks
        scheduler.doAsyncRepeating(new ExpireTemporaryTask(this), 60L);
        scheduler.doAsyncRepeating(new CacheHousekeepingTask(this), 2400L);

        // register permissions
        registerPermissions(getConfiguration().get(ConfigKeys.COMMANDS_ALLOW_OP) ? PermissionDefault.OP
                : PermissionDefault.FALSE);
        if (!getConfiguration().get(ConfigKeys.OPS_ENABLED)) {
            scheduler.doSync(() -> getServer().getOperators().forEach(o -> o.setOp(false)));
        }

        // replace the temporary executor when the Bukkit one starts
        getServer().getScheduler().runTaskAsynchronously(this, () -> {
            scheduler.setUseBukkitAsync(true);
        });

        // Load any online users (in the case of a reload)
        for (Player player : getServer().getOnlinePlayers()) {
            scheduler.doAsync(() -> {
                listener.onAsyncLogin(player.getUniqueId(), player.getName());
                User user = getUserManager().get(getUuidCache().getUUID(player.getUniqueId()));
                if (user != null) {
                    scheduler.doSync(() -> {
                        try {
                            LPPermissible lpPermissible = new LPPermissible(player, user, this);
                            Injector.inject(player, lpPermissible);
                        } catch (Throwable t) {
                            t.printStackTrace();
                        }
                    });
                }
            });
        }

        started = true;
        getLog().info("Successfully loaded.");
    }

    @Override
    public void onDisable() {
        // Switch back to the LP executor, the bukkit one won't allow new tasks
        scheduler.setUseBukkitAsync(false);

        started = false;

        defaultsProvider.close();
        permissionCache.setShutdown(true);
        debugHandler.setShutdown(true);

        for (Player player : getServer().getOnlinePlayers()) {
            Injector.unInject(player, false, false);
            if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
                player.setOp(false);
            }

            final User user = getUserManager().get(getUuidCache().getUUID(player.getUniqueId()));
            if (user != null) {
                user.unregisterData();
                getUserManager().unload(user);
            }
        }

        getLog().info("Closing datastore...");
        storage.shutdown();

        if (messagingService != null) {
            getLog().info("Closing messaging service...");
            messagingService.close();
        }

        getLog().info("Unregistering API...");
        ApiHandler.unregisterProvider();
        getServer().getServicesManager().unregisterAll(this);

        if (vaultHook != null) {
            vaultHook.unhook(this);
        }

        scheduler.shutdown();

        // Bukkit will do this again when #onDisable completes, but we do it early to prevent NPEs elsewhere.
        getServer().getScheduler().cancelTasks(this);
        HandlerList.unregisterAll(this);

        // Null everything
        ignoringLogs = null;
        vaultHook = null;
        configuration = null;
        userManager = null;
        groupManager = null;
        trackManager = null;
        storage = null;
        messagingService = null;
        uuidCache = null;
        listener = null;
        apiProvider = null;
        log = null;
        importer = null;
        defaultsProvider = null;
        childPermissionProvider = null;
        localeManager = null;
        cachedStateManager = null;
        contextManager = null;
        worldCalculator = null;
        calculatorFactory = null;
        updateTaskBuffer = null;
        debugHandler = null;
        senderFactory = null;
        permissionCache = null;
    }

    public void tryVaultHook(boolean force) {
        if (vaultHook != null) {
            return;
        }

        getLog().info("Attempting to hook with Vault...");
        try {
            if (force || getServer().getPluginManager().isPluginEnabled("Vault")) {
                vaultHook = new VaultHook();
                vaultHook.hook(this);
                getLog().info("Registered Vault permission & chat hook.");
            } else {
                getLog().info("Vault not found.");
            }
        } catch (Exception e) {
            vaultHook = null;
            getLog().severe("Error occurred whilst hooking into Vault.");
            e.printStackTrace();
        }
    }

    @Override
    public void onUserRefresh(User user) {
        LPPermissible lpp = Injector.getPermissible(uuidCache.getExternalUUID(user.getUuid()));
        if (lpp != null) {
            lpp.updateSubscriptions();
        }
    }

    public void refreshAutoOp(Player player) {
        if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
            try {
                LPPermissible permissible = Injector.getPermissible(player.getUniqueId());
                if (permissible == null) {
                    return;
                }

                Map<String, Boolean> backing = permissible.getUser().getUserData()
                        .getPermissionData(permissible.calculateContexts()).getImmutableBacking();
                boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false);
                player.setOp(op);
            } catch (Exception ignored) {
            }
        }
    }

    @Override
    public String getVersion() {
        return getDescription().getVersion();
    }

    @Override
    public PlatformType getServerType() {
        return PlatformType.BUKKIT;
    }

    @Override
    public String getServerName() {
        return getServer().getName();
    }

    @Override
    public String getServerVersion() {
        return getServer().getVersion();
    }

    @Override
    public File getDataDirectory() {
        return super.getDataFolder();
    }

    @Override
    public InputStream getResourceStream(String path) {
        return getResource(path);
    }

    @Override
    public Player getPlayer(User user) {
        return getServer().getPlayer(uuidCache.getExternalUUID(user.getUuid()));
    }

    @Override
    public Contexts getContextForUser(User user) {
        Player player = getPlayer(user);
        if (player == null) {
            return null;
        }
        return new Contexts(getContextManager().getApplicableContext(player),
                getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
                getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS), true,
                getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
                getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS), player.isOp());
    }

    @Override
    public int getPlayerCount() {
        return getServer().getOnlinePlayers().size();
    }

    @Override
    public List<String> getPlayerList() {
        return getServer().getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
    }

    @Override
    public Set<UUID> getOnlinePlayers() {
        return getServer().getOnlinePlayers().stream().map(Player::getUniqueId).collect(Collectors.toSet());
    }

    @Override
    public boolean isPlayerOnline(UUID external) {
        return getServer().getPlayer(external) != null;
    }

    @Override
    public List<Sender> getOnlineSenders() {
        return getServer().getOnlinePlayers().stream().map(p -> getSenderFactory().wrap(p))
                .collect(Collectors.toList());
    }

    @Override
    public Sender getConsoleSender() {
        return getSenderFactory().wrap(getServer().getConsoleSender());
    }

    @Override
    public Set<Contexts> getPreProcessContexts(boolean op) {
        Set<ContextSet> c = new HashSet<>();
        c.add(ContextSet.empty());
        c.add(ContextSet.singleton("server", getConfiguration().get(ConfigKeys.SERVER)));

        // Pre process all worlds
        c.addAll(getServer().getWorlds().stream().map(World::getName).map(s -> {
            MutableContextSet set = MutableContextSet.create();
            set.add("server", getConfiguration().get(ConfigKeys.SERVER));
            set.add("world", s);
            return set.makeImmutable();
        }).collect(Collectors.toList()));

        // Pre process the separate Vault server, if any
        if (!getConfiguration().get(ConfigKeys.SERVER).equals(getConfiguration().get(ConfigKeys.VAULT_SERVER))) {
            c.add(ContextSet.singleton("server", getConfiguration().get(ConfigKeys.VAULT_SERVER)));
            c.addAll(getServer().getWorlds().stream().map(World::getName).map(s -> {
                MutableContextSet set = MutableContextSet.create();
                set.add("server", getConfiguration().get(ConfigKeys.VAULT_SERVER));
                set.add("world", s);
                return set.makeImmutable();
            }).collect(Collectors.toList()));
        }

        Set<Contexts> contexts = new HashSet<>();

        // Convert to full Contexts
        contexts.addAll(c.stream()
                .map(set -> new Contexts(set, getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
                        getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS), true,
                        getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
                        getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS), op))
                .collect(Collectors.toSet()));

        // Check for and include varying Vault config options
        boolean vaultDiff = getConfiguration().get(ConfigKeys.VAULT_INCLUDING_GLOBAL) != getConfiguration()
                .get(ConfigKeys.INCLUDING_GLOBAL_PERMS)
                || !getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS)
                || !getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS)
                || !getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS);

        if (vaultDiff) {
            contexts.addAll(c
                    .stream().map(map -> new Contexts(map,
                            getConfiguration().get(ConfigKeys.VAULT_INCLUDING_GLOBAL), true, true, true, true, op))
                    .collect(Collectors.toSet()));
        }

        return contexts;
    }

    @Override
    public LinkedHashMap<String, Object> getExtraInfo() {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        map.put("Vault Enabled", vaultHook != null);
        map.put("Vault Server", configuration.get(ConfigKeys.VAULT_SERVER));
        map.put("Bukkit Defaults count", defaultsProvider.size());
        map.put("Bukkit Child Permissions count", childPermissionProvider.getPermissions().size());
        map.put("Vault Including Global", configuration.get(ConfigKeys.VAULT_INCLUDING_GLOBAL));
        map.put("Vault Ignoring World", configuration.get(ConfigKeys.VAULT_IGNORE_WORLD));
        map.put("Vault Primary Group Overrides", configuration.get(ConfigKeys.VAULT_PRIMARY_GROUP_OVERRIDES));
        map.put("Vault Debug", configuration.get(ConfigKeys.VAULT_DEBUG));
        map.put("OPs Enabled", configuration.get(ConfigKeys.OPS_ENABLED));
        map.put("Auto OP", configuration.get(ConfigKeys.AUTO_OP));
        map.put("Commands Allow OPs", configuration.get(ConfigKeys.COMMANDS_ALLOW_OP));
        return map;
    }

    @SuppressWarnings("deprecation")
    @Override
    public UUID getUuidFromUsername(String playerName) {
        try {
            return getServer().getOfflinePlayer(playerName).getUniqueId();
        } catch (Exception e) {
            return null;
        }
    }

    private void registerPermissions(PermissionDefault def) {
        PluginManager pm = getServer().getPluginManager();

        for (Permission p : Permission.values()) {
            for (String node : p.getNodes()) {
                pm.addPermission(new org.bukkit.permissions.Permission(node, def));
            }
        }
    }
}