de.xaniox.heavyspleef.core.HeavySpleef.java Source code

Java tutorial

Introduction

Here is the source code for de.xaniox.heavyspleef.core.HeavySpleef.java

Source

/*
 * This file is part of HeavySpleef.
 * Copyright (c) 2014-2016 Matthias Werning
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package de.xaniox.heavyspleef.core;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.ListenableFuture;
import de.xaniox.heavyspleef.commands.base.CommandManager;
import de.xaniox.heavyspleef.commands.base.CommandManagerService;
import de.xaniox.heavyspleef.commands.base.DefaultCommandExecution;
import de.xaniox.heavyspleef.core.config.*;
import de.xaniox.heavyspleef.core.event.GlobalEventBus;
import de.xaniox.heavyspleef.core.extension.*;
import de.xaniox.heavyspleef.core.flag.*;
import de.xaniox.heavyspleef.core.game.Game;
import de.xaniox.heavyspleef.core.game.GameManager;
import de.xaniox.heavyspleef.core.game.JoinRequester;
import de.xaniox.heavyspleef.core.game.LoseCheckerTask;
import de.xaniox.heavyspleef.core.hook.HookManager;
import de.xaniox.heavyspleef.core.hook.HookReference;
import de.xaniox.heavyspleef.core.i18n.I18N;
import de.xaniox.heavyspleef.core.i18n.I18NBuilder;
import de.xaniox.heavyspleef.core.i18n.I18NManager;
import de.xaniox.heavyspleef.core.module.LoadPolicy;
import de.xaniox.heavyspleef.core.module.Module;
import de.xaniox.heavyspleef.core.module.ModuleManager;
import de.xaniox.heavyspleef.core.persistence.AsyncReadWriteHandler;
import de.xaniox.heavyspleef.core.player.PlayerManager;
import de.xaniox.heavyspleef.core.player.SpleefPlayer;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.java.JavaPlugin;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.mcstats.Metrics;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;

public final class HeavySpleef {

    private static final String I18N_CLASSPATH_FOLDER = "i18n/";

    private Map<ConfigType, ConfigurationObject> configurations;
    private ModuleManager moduleManager;
    private File localeDir;

    private final JavaPlugin plugin;
    private final Logger logger;

    private String spleefPrefix;
    private String vipPrefix;
    private FlagRegistry flagRegistry;
    private ExtensionRegistry extensionRegistry;
    private CommandManager commandManager;
    private AsyncReadWriteHandler databaseHandler;

    private HookManager hookManager;
    private GameManager gameManager;
    private PlayerManager playerManager;
    private BukkitListener bukkitListener;
    private RegionVisualizer regionVisualizer;
    private Updater updater;
    private PlayerPostActionHandler postActionHandler;
    private GlobalEventBus globalEventBus;
    private I18NManager i18NManager;
    private boolean gamesLoaded;
    private JoinRequester.PvPTimerManager pvpTimerManager;
    private Metrics metrics;
    private Set<GamesLoadCallback> gamesLoadCallbacks;

    public HeavySpleef(JavaPlugin plugin) {
        this.plugin = plugin;
        this.logger = plugin.getLogger();
        this.moduleManager = new ModuleManager(logger);
    }

    public void load() {
        File dataFolder = getDataFolder();

        localeDir = new File(dataFolder, "locale");
        localeDir.mkdirs();
        File layoutDir = new File(dataFolder, "layout");
        layoutDir.mkdirs();

        MinecraftVersion.initialize(logger);

        gamesLoadCallbacks = Sets.newLinkedHashSet();
        flagRegistry = new FlagRegistry(this);

        File configFile = new File(dataFolder, ConfigType.DEFAULT_CONFIG.getDestinationFileName());
        if (configFile.exists()) {
            Configuration prevLoadConfig = YamlConfiguration.loadConfiguration(configFile);
            checkConfigVersions(prevLoadConfig, dataFolder.toPath());
        }

        configurations = new EnumMap<ConfigType, ConfigurationObject>(ConfigType.class);
        loadConfigurations();

        DefaultConfig defaultConfig = getConfiguration(ConfigType.DEFAULT_CONFIG);
        Locale locale = defaultConfig.getLocalization().getLocale();
        I18NBuilder builder = I18NBuilder.builder().setLoadingMode(I18N.LoadingMode.FILE_SYSTEM).setLocale(locale)
                .setFileSystemFolder(localeDir).setClasspathFolder(I18N_CLASSPATH_FOLDER).setLogger(logger);

        I18NManager.setGlobalBuilder(builder);
        i18NManager = new I18NManager();

        playerManager = new PlayerManager(this);
        hookManager = new HookManager();

        hookManager.registerHook(HookReference.VAULT);
        hookManager.registerHook(HookReference.WORLDEDIT);
        hookManager.registerHook(HookReference.PROTOCOLLIB);

        postActionHandler = new PlayerPostActionHandler(this);
        regionVisualizer = new RegionVisualizer(getPlugin());
        globalEventBus = new GlobalEventBus(logger);

        extensionRegistry = new ExtensionRegistry(this);

        try {
            metrics = new Metrics(getPlugin());
        } catch (IOException e) {
            getLogger().log(Level.SEVERE, "Unable to create an instance of Metrics", e);
        }
    }

    public void enable() {
        gameManager = new GameManager(this);

        extensionRegistry.registerExtension(ExtensionLobbyWall.class);
        extensionRegistry.registerExtension(JoinSignExtension.class);
        extensionRegistry.registerExtension(LeaveSignExtension.class);
        extensionRegistry.registerExtension(StartSignExtension.class);

        flagRegistry.flushAndExecuteInitMethods();
        flagRegistry.setInitializationPolicy(FlagRegistry.InitializationPolicy.REGISTER);

        //Load all games
        databaseHandler.getGames(new FutureCallback<List<Game>>() {

            @Override
            public void onSuccess(List<Game> result) {
                for (Game game : result) {
                    gameManager.addGame(game, false);
                }

                gamesLoaded = true;
                for (GamesLoadCallback callback : gamesLoadCallbacks) {
                    callback.onGamesLoaded(result);
                }
            }

            @Override
            public void onFailure(Throwable t) {
                logger.log(Level.SEVERE, "Could not load games from database", t);
            }
        });

        bukkitListener = new BukkitListener(playerManager, gameManager, plugin);
        LoseCheckerTask loseCheckTask = new LoseCheckerTask(this);
        globalEventBus.registerListener(loseCheckTask);
        loseCheckTask.start();

        DefaultConfig config = getConfiguration(ConfigType.DEFAULT_CONFIG);
        GeneralSection generalSection = config.getGeneralSection();
        UpdateSection updateSection = config.getUpdateSection();

        CommandManagerService service = commandManager.getService();
        DefaultCommandExecution execution = service.getExecution();
        spleefPrefix = generalSection.getSpleefPrefix() + " ";
        execution.setPrefix(spleefPrefix);
        vipPrefix = generalSection.getVipPrefix();

        pvpTimerManager = new JoinRequester.PvPTimerManager(this);
        pvpTimerManager.setTicksNeeded(generalSection.getPvpTimer() * 20L);

        //Only check for updates when the user hasn't
        //disabled it in the configuration
        if (updateSection.isUpdateChecking()) {
            //Check for updates
            updater = new Updater(plugin);
            updater.check(new FutureCallback<Updater.CheckResult>() {

                @Override
                public void onSuccess(Updater.CheckResult result) {
                    if (result.isUpdateAvailable()) {
                        Updater.Version version = result.getVersion();

                        getLogger().log(Level.INFO, "Found a new update for HeavySpleef [v" + version + "]!");
                        getLogger().log(Level.INFO,
                                "Please remember to check for config & database compatibility issues which may occur when you update to the latest version");
                        getLogger().log(Level.INFO,
                                "Use '/spleef update' to update to the latest version of HeavySpleef");
                    }
                }

                @Override
                public void onFailure(Throwable t) {
                    getLogger().log(Level.WARNING, "Could not check for latest updates: " + t);
                }
            });
        }

        try {
            metrics.start();
        } catch (Exception e) {
            getLogger().log(Level.SEVERE, "Failed to start metrics: " + e);
        }
    }

    public void disable() {
        gameManager.shutdown();
        ListenableFuture<?> future = databaseHandler.saveGames(gameManager.getGames(), null);

        try {
            //Wait for the task to be completed, as Bukkit  
            //does not mind async thread and just kills them
            future.get();
        } catch (InterruptedException e) {
            logger.log(Level.SEVERE, "Server-Thread interrupted while saving games to database", e);
        } catch (ExecutionException e) {
            logger.log(Level.SEVERE, "Could not save games to database", e);
        }

        HandlerList.unregisterAll(plugin);
        moduleManager.disableModules();
    }

    private void loadConfigurations() {
        Map<ConfigType, Object[]> configArgs = new EnumMap<ConfigType, Object[]>(ConfigType.class);
        configArgs.put(ConfigType.DATABASE_CONFIG, new Object[] { getDataFolder() });

        prepareConfigurations(configArgs);
    }

    private void prepareConfigurations(Map<ConfigType, Object[]> args) {
        for (ConfigType type : ConfigType.values()) {
            File destinationFile = new File(getDataFolder(), type.getDestinationFileName());
            YamlConfiguration config;

            try {
                if (!destinationFile.exists()) {
                    URL resourceUrl = getClass().getResource(type.getClasspathResourceName());
                    copyResource(resourceUrl, destinationFile);
                }

                config = new YamlConfiguration();
                config.load(destinationFile);
            } catch (IOException | InvalidConfigurationException e) {
                logger.log(Level.SEVERE, "Could not load configuration \"" + type.getDestinationFileName() + "\"",
                        e);
                continue;
            }

            ConfigurationObject obj = configurations.get(type);
            if (obj == null) {
                try {
                    obj = type.newConfigInstance(config, args.get(type));
                } catch (ThrowingConfigurationObject.UnsafeException ex) {
                    Throwable cause = ex.getCause();

                    logger.log(Level.SEVERE, "Could not create config structure for " + destinationFile.getPath()
                            + ", except errors: ", cause);
                    continue;
                }

                configurations.put(type, obj);
            } else {
                obj.inflate(config, args.get(type));
            }

        }
    }

    public static void copyResource(URL resourceUrl, File destination) throws IOException {
        URLConnection connection = resourceUrl.openConnection();

        if (!destination.exists()) {
            destination.getParentFile().mkdirs();
        } else {
            destination.delete();
        }

        destination.createNewFile();

        final int bufferSize = 1024;

        try (InputStream inStream = connection.getInputStream();
                FileOutputStream outStream = new FileOutputStream(destination)) {
            byte[] buffer = new byte[bufferSize];

            int read;
            while ((read = inStream.read(buffer)) > 0) {
                outStream.write(buffer, 0, read);
            }
        }
    }

    private void checkConfigVersions(Configuration config, Path dataFolder) {
        if (config.getInt("config-version") < DefaultConfig.CURRENT_CONFIG_VERSION) {
            Path configSource = dataFolder.resolve(ConfigType.DEFAULT_CONFIG.getDestinationFileName());
            Path configTarget = dataFolder.resolve("config_old.yml");

            try {
                Files.move(configSource, configTarget, StandardCopyOption.REPLACE_EXISTING);
                URL configResource = getClass().getResource(ConfigType.DEFAULT_CONFIG.getClasspathResourceName());

                copyResource(configResource, configSource.toFile());

                ConsoleCommandSender sender = Bukkit.getConsoleSender();
                sender.sendMessage(
                        ChatColor.RED + "Due to a HeavySpleef update your old configuration has been renamed");
                sender.sendMessage(
                        ChatColor.RED + "to config_old.yml and a new one has been generated. Make sure to");
                sender.sendMessage(ChatColor.RED + "apply your old changes to the new config");
            } catch (IOException e) {
                getLogger().log(Level.SEVERE, "Could not create updated configuration due to an IOException", e);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public void reload() {
        loadConfigurations();
        DefaultConfig config = getConfiguration(ConfigType.DEFAULT_CONFIG);
        Locale locale = config.getLocalization().getLocale();

        i18NManager.reloadAll(locale);
        moduleManager.reloadModules();

        GeneralSection generalSection = config.getGeneralSection();
        CommandManagerService service = commandManager.getService();
        DefaultCommandExecution execution = service.getExecution();
        spleefPrefix = generalSection.getSpleefPrefix() + " ";
        vipPrefix = generalSection.getVipPrefix();

        execution.setPrefix(spleefPrefix);

        int pvpTimer = generalSection.getPvpTimer();
        pvpTimerManager.setTicksNeeded(pvpTimer * 20L);

        for (Game game : gameManager.getGames()) {
            JoinRequester requester = game.getJoinRequester();
            requester.setPvpTimerMode(pvpTimer > 0);

            List<AbstractFlag<?>> loadedFlags = Lists.newArrayList();
            Iterator<AbstractFlag<?>> iterator = game.getFlagManager().getFlags().iterator();
            while (iterator.hasNext()) {
                AbstractFlag<?> flag = iterator.next();

                if (flag instanceof UnloadedFlag) {
                    UnloadedFlag unloaded = (UnloadedFlag) flag;
                    if (!unloaded.validateLoad(flagRegistry)) {
                        //This is not ready for load yet
                        continue;
                    }

                    AbstractFlag<?> loaded = unloaded.loadFlag(flagRegistry);
                    loadedFlags.add(loaded);
                    game.removeFlag(unloaded.getClass());
                    game.addFlag(loaded, false);
                } else {
                    Class<? extends AbstractFlag<?>> clazz = (Class<? extends AbstractFlag<?>>) flag.getClass();
                    if (flagRegistry.isFlagPresent(clazz)) {
                        continue;
                    }

                    //This flag has been deactivated
                    game.removeFlag(clazz);

                    /* Generate a path */
                    StringBuilder pathBuilder = new StringBuilder();
                    Flag parentFlagData = clazz.getAnnotation(Flag.class);

                    do {
                        pathBuilder.insert(0, parentFlagData.name());

                        Class<? extends AbstractFlag<?>> parentFlagClass = parentFlagData.parent();
                        parentFlagData = parentFlagClass.getAnnotation(Flag.class);

                        if (parentFlagData != null && parentFlagClass != NullFlag.class) {
                            pathBuilder.insert(0, FlagRegistry.FLAG_PATH_SEPERATOR);
                        }
                    } while (parentFlagData != null);

                    String path = pathBuilder.toString();

                    Element element = DocumentHelper.createElement("flag");
                    element.addAttribute("name", path);
                    flag.marshal(element);

                    UnloadedFlag unloaded = new UnloadedFlag();
                    unloaded.setXmlElement(element);
                    game.addFlag(unloaded, false);
                }
            }

            for (AbstractFlag<?> loaded : loadedFlags) {
                loaded.onFlagAdd(game);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public <T extends ConfigurationObject> T getConfiguration(ConfigType type) {
        return (T) configurations.get(type);
    }

    public File getDataFolder() {
        return plugin.getDataFolder();
    }

    public SpleefPlayer getSpleefPlayer(Object base) {
        if (base instanceof Player) {
            return playerManager.getSpleefPlayer((Player) base);
        } else if (base instanceof String) {
            return playerManager.getSpleefPlayer((String) base);
        } else if (base instanceof UUID) {
            return playerManager.getSpleefPlayer((UUID) base);
        }

        throw new IllegalArgumentException("base must be an instance of Player, String or UUID but is '"
                + base.getClass().getCanonicalName() + "'");
    }

    public void registerModule(Module module) {
        moduleManager.registerModule(module);
    }

    public void enableModules(LoadPolicy.Lifecycle lifecycle) {
        moduleManager.enableModules(lifecycle);
    }

    public void addGamesLoadCallback(GamesLoadCallback callback) {
        gamesLoadCallbacks.add(callback);
    }

    public ExtensionRegistry getExtensionRegistry() {
        return extensionRegistry;
    }

    public void setExtensionRegistry(ExtensionRegistry extensionRegistry) {
        this.extensionRegistry = extensionRegistry;
    }

    public CommandManager getCommandManager() {
        return commandManager;
    }

    public void setCommandManager(CommandManager commandManager) {
        this.commandManager = commandManager;
    }

    public AsyncReadWriteHandler getDatabaseHandler() {
        return databaseHandler;
    }

    public void setDatabaseHandler(AsyncReadWriteHandler databaseHandler) {
        this.databaseHandler = databaseHandler;
    }

    public ModuleManager getModuleManager() {
        return moduleManager;
    }

    public JavaPlugin getPlugin() {
        return plugin;
    }

    public Logger getLogger() {
        return logger;
    }

    public String getSpleefPrefix() {
        return spleefPrefix;
    }

    public String getVipPrefix() {
        return vipPrefix;
    }

    public FlagRegistry getFlagRegistry() {
        return flagRegistry;
    }

    public HookManager getHookManager() {
        return hookManager;
    }

    public GameManager getGameManager() {
        return gameManager;
    }

    public PlayerManager getPlayerManager() {
        return playerManager;
    }

    public BukkitListener getBukkitListener() {
        return bukkitListener;
    }

    public RegionVisualizer getRegionVisualizer() {
        return regionVisualizer;
    }

    public Updater getUpdater() {
        return updater;
    }

    public PlayerPostActionHandler getPostActionHandler() {
        return postActionHandler;
    }

    public GlobalEventBus getGlobalEventBus() {
        return globalEventBus;
    }

    public I18NManager getI18NManager() {
        return i18NManager;
    }

    public boolean isGamesLoaded() {
        return gamesLoaded;
    }

    public JoinRequester.PvPTimerManager getPvpTimerManager() {
        return pvpTimerManager;
    }

    public Metrics getMetrics() {
        return metrics;
    }

    public interface GamesLoadCallback {

        public void onGamesLoaded(List<Game> games);

    }

}