Package com.onarandombox.MultiverseAdventure

Source Code of com.onarandombox.MultiverseAdventure.MVAdventureWorld

package com.onarandombox.MultiverseAdventure;

import buscript.multiverse.Buscript;
import buscript.multiverse.StringReplacer;
import com.onarandombox.MultiverseAdventure.api.AdventureWorld;
import com.onarandombox.MultiverseAdventure.event.MVAResetEvent;
import com.onarandombox.MultiverseAdventure.event.MVAResetFinishedEvent;
import com.onarandombox.MultiverseAdventure.listeners.MVAResetListener;
import com.onarandombox.MultiverseAdventure.listeners.MVAWorldListener;
import com.onarandombox.MultiverseCore.api.MultiverseWorld;
import com.onarandombox.MultiverseCore.utils.FileUtils;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;

import java.io.File;
import java.util.concurrent.Callable;
import java.util.logging.Level;

/**
* Provides support for "adventure"-worlds
*
* @author main()
*/
public final class MVAdventureWorld implements AdventureWorld {
    /**
     * Whether this AdventureWorld is "active" (contains players that have changed it)
     */
    private boolean active;
    private final MultiverseWorld world;
    private final MultiverseAdventure plugin;

    /**
     * The name of the template-folder for this AdventureWorld
     */
    private String template;
    private int activationdelay;
    private int resetdelay;
    private boolean resetOnRestart;
    private boolean resetWhenEmpty;
    private String cronResetSchedule;
    private String preResetScript;
    private String postResetScript;

    private int resetTaskId;
    private int activationTaskId;

    private JobKey quartzJobKey = null;

    public MVAdventureWorld(MultiverseWorld world, MultiverseAdventure plugin, String template, int activationdelay, int resetdelay) {
        this.world = world;
        this.plugin = plugin;
        active = false;

        this.setTemplate(template);
        this.setActivationDelay(activationdelay);
        this.setResetDelay(resetdelay);

        resetTaskId = -1;
        activationTaskId = -1;
        resetOnRestart = true;
        resetWhenEmpty = true;
        cronResetSchedule = "";
        preResetScript = "";
        postResetScript = "";

        this.plugin.log(Level.FINER, "A new MVAdventureWorld-Object was created!");
        this.initCronScheduler();
    }

    public MVAdventureWorld(MultiverseWorld world, MultiverseAdventure plugin, ConfigurationSection node) {
        this.world = world;
        this.plugin = plugin;
        active = false;

        this.setTemplate(node.getString("template", "NAME.template")); // "NAME" will be replaced with the world's name
        this.setActivationDelay(node.getInt("activationdelay", 10));
        this.setResetDelay(node.getInt("resetdelay", 10));
        this.setResetOnRestart(node.getBoolean("resetonrestart", true));
        this.setResetWhenEmpty(node.getBoolean("resetwhenempty", true));
        this.setCronResetSchedule(node.getString("cronresetschedule", ""));
        this.setPreResetScript(node.getString("preresetscript", ""));
        this.setPostResetScript(node.getString("postresetscript", ""));

        plugin.saveConfig();

        resetTaskId = -1;
        activationTaskId = -1;

        this.plugin.log(Level.FINER, "A new MVAdventureWorld-Object was created!");
    }

    private void initCronScheduler() {
        if (quartzJobKey == null) {
            quartzJobKey = new JobKey(getName() + "-reset", "resets");
        }
        // Cancel previous job if exists.
        try {
            if (plugin.getQuartzScheduler().deleteJob(quartzJobKey)) {
                plugin.log(Level.FINE, "Deleted previous cron job for " + getName());
            }
        } catch (SchedulerException e) {
            plugin.log(Level.SEVERE, "Could not delete existing cron job for " + getName() + ": " + e.getMessage());
            return;
        }

        // Return after job cancel if cron string is empty.
        if (getCronResetSchedule().isEmpty()) {
            return;
        }

        // Create a task for the cron job to run
        CronResetTask task = new CronResetTask(plugin, this.getName());
        // Create a job to stash in the scheduler
        JobDetail job = JobBuilder.newJob(CronResetJob.class)
                .withIdentity(quartzJobKey).build();
        job.getJobDataMap().put("cronResetTask", task);
        // Create the cron trigger for the job
        Trigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity(getName() + "-crontrigger", "resets")
                .startNow()
                .withSchedule(CronScheduleBuilder.cronSchedule("0 " + getCronResetSchedule()))
                .build();

        try {
            plugin.log(Level.FINE, "Scheduling reset cron-job for " + getName() + ": " + cronTrigger.toString());
            plugin.getQuartzScheduler().scheduleJob(job, cronTrigger);
        } catch (SchedulerException e) {
            plugin.log(Level.WARNING, "Could not schedule cron job for " + this.getName() + ": " + e.getMessage());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void saveTo(ConfigurationSection config) {
        config.set("enabled", true);
        config.set("template", this.template);
        config.set("activationdelay", activationdelay);
        config.set("resetdelay", resetdelay);
        config.set("resetonrestart", resetOnRestart);
        config.set("resetwhenempty", resetWhenEmpty);
        config.set("cronresetschedule", cronResetSchedule);
        config.set("preresetscript", preResetScript);
        config.set("postresetscript", postResetScript);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isActive() {
        return active;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isActivating() {
        if (isActive())
            return false;
        else
            return plugin.getServer().getScheduler().isQueued(activationTaskId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Deprecated
    public void setActive(boolean active) {
        this.active = active;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isResetting() {
        return plugin.getServer().getScheduler().isQueued(resetTaskId); // || plugin.getServer().getScheduler().isCurrentlyRunning(resetTaskId); We don't need to check this since the ResetPreparer runs in the main thread
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MultiverseWorld getMVWorld() {
        return world;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getTemplate() {
        return template.replaceAll("NAME", world.getName());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setTemplate(String template) {
        this.template = template;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getActivationDelay() {
        return activationdelay;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setActivationDelay(int activationDelay) {
        this.activationdelay = activationDelay;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getResetDelay() {
        return resetdelay;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setResetDelay(int resetDelay) {
        this.resetdelay = resetDelay;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean shouldResetOnRestart() {
        return resetOnRestart;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setResetOnRestart(boolean resetOnRestart) {
        this.resetOnRestart = resetOnRestart;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean shouldResetWhenEmpty() {
        return resetWhenEmpty;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setResetWhenEmpty(boolean resetWhenEmpty) {
        this.resetWhenEmpty = resetWhenEmpty;
    }

    @Override
    public String getCronResetSchedule() {
        return cronResetSchedule;
    }

    @Override
    public void setCronResetSchedule(String cronResetSchedule) {
        this.cronResetSchedule = cronResetSchedule;
        this.initCronScheduler();
    }

    @Override
    public String getPreResetScript() {
        return preResetScript;
    }

    @Override
    public void setPreResetScript(String preResetScript) {
        this.preResetScript = preResetScript;
    }

    @Override
    public String getPostResetScript() {
        return postResetScript;
    }

    @Override
    public void setPostResetScript(String postResetScript) {
        this.postResetScript = postResetScript;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean scheduleReset() {
        plugin.log(Level.FINER, "Scheduling reset of world '" + this.getName() + "' ...");

        resetTaskId = plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new ResetPreparer(this), getResetDelay() * 20);
        // resetdelay is in seconds and with 20 ticks per second we have to take this * 20

        return resetTaskId != -1;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void resetNow() {
        plugin.log(Level.FINER, "Resetting world '" + this.getName() + "' NOW!");

        plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new ResetPreparer(this));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void cancelReset() {
        if (isResetting()) {
            plugin.log(Level.FINER, "Cancelling reset of world '" + this.getName() + "' ...");

            plugin.getServer().getScheduler().cancelTask(resetTaskId);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void scheduleActivation() {
        plugin.log(Level.FINER, "Scheduling activation of world '" + this.getName() + "' ...");

        if (getActivationDelay() == 0) {
            active = true;
            return;
            // We don't need the scheduler here
        }
        activationTaskId = plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
            public void run() {
                active = true;
            }
        }, getActivationDelay() * 20);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void cancelActivation() {
        if (isActivating()) {
            plugin.log(Level.FINER, "Cancelling activation of world '" + this.getName() + "' ...");

            plugin.getServer().getScheduler().cancelTask(activationTaskId);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getName() {
        return world.getName();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean scheduleWriteTemplate() {
        return scheduleWriteTemplate(new TemplateWriter());
    }

    /**
     * Writes the current state of the world to the template. Useful for initializing.
     *
     * @param sender A CommandSender that receives a notification after the work is done.
     * @return True if success, false if failed.
     * @deprecated Use {@link #scheduleWriteTemplate(Runnable)} instead
     */
    public boolean scheduleWriteTemplate(final CommandSender sender) {
        return scheduleWriteTemplate(new Callable<Void>() {
            public Void call() throws Exception {
                sender.sendMessage("Finished.");
                return null;
            }
        });
    }

    /**
     * {@inheritDoc}
     *
     * @deprecated Use {@link #scheduleWriteTemplate(Callable<Void>,Callable<Void>)} instead
     */
    @Override
    public boolean scheduleWriteTemplate(Callable<Void> onFinish) {
        return scheduleWriteTemplate(onFinish, null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean scheduleWriteTemplate(Callable<Void> onFinish, Callable<Void> onFail) {
        return scheduleWriteTemplate(new TemplateWriter(onFinish, onFail));
    }

    private boolean scheduleWriteTemplate(TemplateWriter tw) {
        plugin.log(Level.FINER, "Scheduling a TemplateWriter for world '" + this.getName() + "' ...");

        if (tw == null)
            throw new IllegalArgumentException("tw can't be null!");

        int id = plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, tw);
        return id != -1;
    }

    public String toString() {
        return String.format("MVAdventureWorld@%h [world=%s, template=%s, activationdelay=%s, resetdelay=%s]",
                hashCode(), world, template, activationdelay, resetdelay);
    }

    private class ScriptWorldNameReplacer implements StringReplacer {
        String worldName;

        ScriptWorldNameReplacer(String name) {
            worldName = name;
        }

        @Override
        public String getRegexString() {
            return "%worldname%";
        }

        @Override
        public String getReplacement() {
            return worldName;
        }

        @Override
        public String getGlobalVarName() {
            return "worldname";
        }
    }

    /**
     * Prepares the AdventureWorld for the reset and then schedules the actual reset. (SYNC)
     */
    private class ResetPreparer implements Runnable {
        private AdventureWorld world;

        @Override
        public void run() {
            String name = world.getName();
            String template = world.getTemplate();

            // check
            if (!world.getMVWorld().getCBWorld().getPlayers().isEmpty()) {
                plugin.log(Level.FINE, "Abandoning reset of world '" + name + "' as it is not empty!");
                // What the...?
                return;
            }

            plugin.log(Level.INFO, "Beginning reset of world '" + name + "'...");

            // now call the event
            MVAResetEvent resetEvent = new MVAResetEvent(name);
            plugin.getServer().getPluginManager().callEvent(resetEvent);
            if (resetEvent.isCancelled()) {
                plugin.log(Level.INFO, "Reset of world '" + name + "' cancelled.");
                return;
            }

            if (!getPreResetScript().isEmpty()) {
                // Execute pre reset script, if there is one
                Buscript buscript = plugin.getCore().getScriptAPI();
                File preResetScript = new File(buscript.getScriptFolder(), getPreResetScript());
                if (preResetScript.exists()) {
                    buscript.getGlobalScope().put("world", buscript.getGlobalScope(), getMVWorld());
                    buscript.registerStringReplacer(new ScriptWorldNameReplacer(getName()));
                    buscript.executeScript(preResetScript);
                    buscript.getGlobalScope().put("world", buscript.getGlobalScope(), null);
                } else {
                    plugin.log(Level.WARNING, "preresetscript for " + getName() + " does not exist!");
                }
            }

            // everything is OK, let's start:
            // Mark this world as resetting
            MVAResetListener.addResettingWorld(getName());
            // 1. Unload it
            if (!plugin.getCore().getMVWorldManager().unloadWorld(name)) {
                plugin.log(Level.FINE, "Abandoning reset of world '" + name + "' as it failed to unload!");
                MVAResetListener.removeResettingWorld(getName());
                return;
            }

            // The Rest is done async
            int ret = plugin.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new ResetWorker(name, template));
            if (ret == -1) {// WTF? Scheduling failed???
                plugin.log(Level.SEVERE, "Couldn't schedule a ResetWorker!");
                MVAResetListener.removeResettingWorld(getName());
            }
        }

        public ResetPreparer(AdventureWorld world) {
            this.world = world;
        }
    }

    /**
     * Does the actual reset. (ASYNC)
     */
    private class ResetWorker implements Runnable {
        private String name;
        private String template;

        @Override
        public void run() {
            // 2. Remove it
            File serverFolder = plugin.getServer().getWorldContainer();
            File worldFile = new File(serverFolder, name);
            FileUtils.deleteFolderContents(worldFile);
            if (worldFile.list().length > 0) {
                // WTF? Couldn't delete it???
                plugin.log(Level.SEVERE, "Couldn't delete a world!");
                MVAResetListener.removeResettingWorld(getName());
                return; // failed...
            }

            // 3. Copy the new world
            File templateFile = new File(serverFolder, template);
            if (!FileUtils.copyFolder(templateFile, worldFile, plugin.getLogger())) {
                // Damn.
                plugin.log(Level.SEVERE, "Couldn't copy a world!");
                MVAResetListener.removeResettingWorld(getName());
                return; // failed...
            }

            // Now finish it in the main thread
            int ret = plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new ResetFinisher(name));
            if (ret == -1) {// WTF? Scheduling failed???
                plugin.log(Level.SEVERE, "Couldn't schedule a ResetFinisher!");
                MVAResetListener.removeResettingWorld(getName());
            }
        }

        public ResetWorker(String name, String template) {
            this.name = name;
            this.template = template;
        }
    }

    /**
     * Finishes the reset (SYNC)
     */
    private class ResetFinisher implements Runnable {
        private String name;

        @Override
        public void run() {
            // 4. Load the world
            MVAWorldListener.addPass(name);
            plugin.getCore().getMVWorldManager().loadWorld(name);
            MultiverseWorld world = plugin.getCore().getMVWorldManager().getMVWorld(name);

            plugin.log(Level.INFO, "Reset of world '" + name + "' finished.");

            // call the event
            plugin.getServer().getPluginManager().callEvent(new MVAResetFinishedEvent(name));

            if (!getPostResetScript().isEmpty()) {
                // Run post reset script, if exists
                Buscript buscript = plugin.getCore().getScriptAPI();
                File postResetScript = new File(buscript.getScriptFolder(), getPostResetScript());
                if (postResetScript.exists()) {
                    buscript.getGlobalScope().put("world", buscript.getGlobalScope(), world);
                    buscript.registerStringReplacer(new ScriptWorldNameReplacer(getName()));
                    buscript.executeScript(postResetScript);
                    buscript.getGlobalScope().put("world", buscript.getGlobalScope(), null);
                } else {
                    plugin.log(Level.WARNING, "postresetscript for " + getName() + " does not exist!");
                }
            }
        }

        public ResetFinisher(String name) {
            this.name = name;
        }
    }

    /**
     * Writes the current state of the world to the template. (ASYNC)
     */
    private class TemplateWriter implements Runnable {
        private final Callable<?> onFinish;
        private final Callable<Void> onFail;

        @Override
        public void run() {
            File serverFolder = plugin.getServer().getWorldContainer();
            File worldFile = new File(serverFolder, getName());
            File templateFile = new File(serverFolder, getTemplate());

            // 1. Unload
            plugin.getCore().getMVWorldManager().unloadWorld(getName());
            // 2. Remove template (if exists)
            FileUtils.deleteFolder(templateFile);
            if (templateFile.exists()) {
                // Damn.
                plugin.log(Level.SEVERE, "TemplateWriter: Couldn't delete the template!");
                onFail();
                return; // failed...
            }
            // 3. Copy
            if (!FileUtils.copyFolder(worldFile, templateFile, plugin.getLogger())) {
                // Damn.
                plugin.log(Level.SEVERE, "TemplateWriter: Couldn't copy the template!");
                onFail();
                return; // failed...
            }
            // 4. Load
            plugin.getCore().getMVWorldManager().loadWorld(getName());
            MVAWorldListener.addPass(getName());

            if (onFinish != null) {
                // 5. Notify
                plugin.getServer().getScheduler().callSyncMethod(plugin, onFinish);
            }
        }

        private void onFail() {
            plugin.log(Level.SEVERE, "TemplateWriter: Failed!");
            plugin.getServer().getScheduler().callSyncMethod(plugin, onFail);
        }

        /**
         * Create a new TemplateWriter.
         */
        public TemplateWriter() {
            this.onFinish = null;
            this.onFail = null;
        }

        /**
         * Create a new TemplateWriter that does something after the work is done.
         */
        public TemplateWriter(Callable<Void> onFinish, Callable<Void> onFail) {
            this.onFinish = onFinish;
            this.onFail = onFail;
        }
    }
}
TOP

Related Classes of com.onarandombox.MultiverseAdventure.MVAdventureWorld

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.