net.countercraft.movecraft.async.AsyncManager.java Source code

Java tutorial

Introduction

Here is the source code for net.countercraft.movecraft.async.AsyncManager.java

Source

/*
 * This file is part of Movecraft.
 *
 *     Movecraft 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.
 *
 *     Movecraft 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 Movecraft.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.countercraft.movecraft.async;

import net.countercraft.movecraft.Movecraft;
import net.countercraft.movecraft.async.detection.DetectionTask;
import net.countercraft.movecraft.async.detection.DetectionTaskData;
import net.countercraft.movecraft.async.rotation.RotationTask;
import net.countercraft.movecraft.async.translation.TranslationTask;
import net.countercraft.movecraft.config.Settings;
import net.countercraft.movecraft.craft.Craft;
import net.countercraft.movecraft.craft.CraftManager;
import net.countercraft.movecraft.listener.CommandListener;
import net.countercraft.movecraft.listener.WorldEditInteractListener;
import net.countercraft.movecraft.localisation.I18nSupport;
import net.countercraft.movecraft.utils.BlockUtils;
import net.countercraft.movecraft.utils.EntityUpdateCommand;
//import net.countercraft.movecraft.utils.FastBlockChanger;
import net.countercraft.movecraft.utils.MapUpdateCommand;
import net.countercraft.movecraft.utils.MapUpdateManager;
import net.countercraft.movecraft.utils.MathUtils;
import net.countercraft.movecraft.utils.MovecraftLocation;

import org.apache.commons.collections.ListUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.craftbukkit.v1_10_R1.block.CraftBlockState;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.projectiles.ProjectileSource;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import org.mozilla.javascript.JavaScriptException;

import at.pavlov.cannons.cannon.Cannon;

import com.palmergames.bukkit.towny.object.TownBlock;
import com.palmergames.bukkit.towny.object.TownyWorld;
import com.sk89q.worldguard.domains.DefaultDomain;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.flags.DefaultFlag;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.flags.StateFlag.State;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;

import net.countercraft.movecraft.utils.ItemDropUpdateCommand;
import net.countercraft.movecraft.utils.TownyUtils;
import net.countercraft.movecraft.utils.WGCustomFlagsUtils;

public class AsyncManager extends BukkitRunnable {
    private static final AsyncManager instance = new AsyncManager();
    private final HashMap<AsyncTask, Craft> ownershipMap = new HashMap<AsyncTask, Craft>();
    private final HashMap<org.bukkit.entity.TNTPrimed, Double> TNTTracking = new HashMap<org.bukkit.entity.TNTPrimed, Double>();
    private final HashMap<Craft, HashMap<Craft, Long>> recentContactTracking = new HashMap<Craft, HashMap<Craft, Long>>();
    private HashMap<org.bukkit.entity.SmallFireball, Long> FireballTracking = new HashMap<org.bukkit.entity.SmallFireball, Long>();
    private final BlockingQueue<AsyncTask> finishedAlgorithms = new LinkedBlockingQueue<AsyncTask>();
    private final HashSet<Craft> clearanceSet = new HashSet<Craft>();
    private long lastTracerUpdate = 0;
    private long lastFireballCheck = 0;
    private long lastTNTContactCheck = 0;
    private long lastFadeCheck = 0;
    private long lastContactCheck = 0;
    private long lastAssaultCheck = 0;
    private long lastSiegeNotification = 0;
    private long lastSiegePayout = 0;
    private HashSet<Material> transparent = null;

    public static AsyncManager getInstance() {
        return instance;
    }

    private AsyncManager() {
    }

    public void submitTask(AsyncTask task, Craft c) {
        if (c.isNotProcessing()) {
            c.setProcessing(true);
            ownershipMap.put(task, c);
            task.runTaskAsynchronously(Movecraft.getInstance());
        }
    }

    public void submitCompletedTask(AsyncTask task) {
        finishedAlgorithms.add(task);
    }

    void processAlgorithmQueue() {
        int runLength = 10;
        int queueLength = finishedAlgorithms.size();

        runLength = Math.min(runLength, queueLength);

        for (int i = 0; i < runLength; i++) {
            boolean sentMapUpdate = false;
            AsyncTask poll = finishedAlgorithms.poll();
            Craft c = ownershipMap.get(poll);

            if (poll instanceof DetectionTask) {
                // Process detection task

                DetectionTask task = (DetectionTask) poll;
                DetectionTaskData data = task.getData();

                Player p = data.getPlayer();
                Player notifyP = data.getNotificationPlayer();
                Craft pCraft = CraftManager.getInstance().getCraftByPlayer(p);

                if (pCraft != null && p != null) {
                    // Player is already controlling a craft
                    notifyP.sendMessage(String.format(I18nSupport
                            .getInternationalisedString("Detection - Failed - Already commanding a craft")));
                } else {
                    if (data.failed()) {
                        if (notifyP != null)
                            notifyP.sendMessage(data.getFailMessage());
                        else
                            Movecraft.getInstance().getLogger().log(Level.INFO,
                                    "NULL Player Craft Detection failed:" + data.getFailMessage());

                    } else {
                        Craft[] craftsInWorld = CraftManager.getInstance().getCraftsInWorld(c.getW());
                        boolean failed = false;
                        boolean isSubcraft = false;

                        if (craftsInWorld != null) {
                            for (Craft craft : craftsInWorld) {

                                if (BlockUtils.arrayContainsOverlap(craft.getBlockList(),
                                        data.getBlockList()) == true) {
                                    isSubcraft = true;
                                    if (c.getType().getCruiseOnPilot() || p != null) {
                                        if (craft.getType() == c.getType()
                                                || craft.getBlockList().length <= data.getBlockList().length) {
                                            notifyP.sendMessage(
                                                    String.format(I18nSupport.getInternationalisedString(
                                                            "Detection - Failed Craft is already being controlled")));
                                            failed = true;
                                        } else { // if this is a different type than
                                                 // the overlapping craft, and is
                                                 // smaller, this must be a child
                                                 // craft, like a fighter on a
                                                 // carrier
                                            if (craft.isNotProcessing() == false) {
                                                failed = true;
                                                notifyP.sendMessage(String.format(I18nSupport
                                                        .getInternationalisedString("Parent Craft is busy")));
                                            }

                                            // remove the new craft from the parent
                                            // craft
                                            List<MovecraftLocation> parentBlockList = ListUtils.subtract(
                                                    Arrays.asList(craft.getBlockList()),
                                                    Arrays.asList(data.getBlockList()));
                                            craft.setBlockList(parentBlockList.toArray(new MovecraftLocation[1]));
                                            craft.setOrigBlockCount(
                                                    craft.getOrigBlockCount() - data.getBlockList().length);

                                            // Rerun the polygonal bounding formula
                                            // for the parent craft
                                            Integer parentMaxX = null;
                                            Integer parentMaxZ = null;
                                            Integer parentMinX = null;
                                            Integer parentMinZ = null;
                                            for (MovecraftLocation l : parentBlockList) {
                                                if (parentMaxX == null || l.getX() > parentMaxX) {
                                                    parentMaxX = l.getX();
                                                }
                                                if (parentMaxZ == null || l.getZ() > parentMaxZ) {
                                                    parentMaxZ = l.getZ();
                                                }
                                                if (parentMinX == null || l.getX() < parentMinX) {
                                                    parentMinX = l.getX();
                                                }
                                                if (parentMinZ == null || l.getZ() < parentMinZ) {
                                                    parentMinZ = l.getZ();
                                                }
                                            }
                                            int parentSizeX, parentSizeZ;
                                            parentSizeX = (parentMaxX - parentMinX) + 1;
                                            parentSizeZ = (parentMaxZ - parentMinZ) + 1;
                                            int[][][] parentPolygonalBox = new int[parentSizeX][][];
                                            for (MovecraftLocation l : parentBlockList) {
                                                if (parentPolygonalBox[l.getX() - parentMinX] == null) {
                                                    parentPolygonalBox[l.getX()
                                                            - parentMinX] = new int[parentSizeZ][];
                                                }
                                                if (parentPolygonalBox[l.getX() - parentMinX][l.getZ()
                                                        - parentMinZ] == null) {
                                                    parentPolygonalBox[l.getX() - parentMinX][l.getZ()
                                                            - parentMinZ] = new int[2];
                                                    parentPolygonalBox[l.getX() - parentMinX][l.getZ()
                                                            - parentMinZ][0] = l.getY();
                                                    parentPolygonalBox[l.getX() - parentMinX][l.getZ()
                                                            - parentMinZ][1] = l.getY();
                                                } else {
                                                    int parentMinY = parentPolygonalBox[l.getX() - parentMinX][l
                                                            .getZ() - parentMinZ][0];
                                                    int parentMaxY = parentPolygonalBox[l.getX() - parentMinX][l
                                                            .getZ() - parentMinZ][1];

                                                    if (l.getY() < parentMinY) {
                                                        parentPolygonalBox[l.getX() - parentMinX][l.getZ()
                                                                - parentMinZ][0] = l.getY();
                                                    }
                                                    if (l.getY() > parentMaxY) {
                                                        parentPolygonalBox[l.getX() - parentMinX][l.getZ()
                                                                - parentMinZ][1] = l.getY();
                                                    }
                                                }
                                            }
                                            craft.setHitBox(parentPolygonalBox);
                                        }
                                    }
                                }

                            }
                        }
                        if (c.getType().getMustBeSubcraft() == true && isSubcraft == false) {
                            failed = true;
                            notifyP.sendMessage(String.format(
                                    I18nSupport.getInternationalisedString("Craft must be part of another craft")));
                        }
                        if (!failed) {
                            c.setBlockList(data.getBlockList());
                            c.setOrigBlockCount(data.getBlockList().length);
                            c.setHitBox(data.getHitBox());
                            c.setMinX(data.getMinX());
                            c.setMinZ(data.getMinZ());
                            c.setNotificationPlayer(notifyP);
                            if (c.getType().getDynamicFlyBlockSpeedFactor() != 0.0) {
                                c.setCurTickCooldown(c.getType().getCruiseTickCooldown());
                                c.setMaxSpeed(
                                        c.getCurSpeed() + (data.dynamicFlyBlockSpeedMultiplier * c.getCurSpeed()));
                            } else {
                                c.setCurTickCooldown(c.getType().getCruiseTickCooldown());
                                c.setMaxSpeed(c.getCurSpeed());
                            }

                            if (notifyP != null) {
                                notifyP.sendMessage(String
                                        .format(I18nSupport.getInternationalisedString(
                                                "Detection - Successfully piloted craft"))
                                        + " Size: " + c.getBlockList().length);
                                Movecraft.getInstance().getLogger().log(Level.INFO,
                                        String.format(
                                                I18nSupport.getInternationalisedString(
                                                        "Detection - Success - Log Output"),
                                                notifyP.getName(), c.getType().getCraftName(),
                                                c.getBlockList().length, c.getMinX(), c.getMinZ()));
                            } else {
                                Movecraft.getInstance().getLogger().log(Level.INFO,
                                        String.format(
                                                I18nSupport.getInternationalisedString(
                                                        "Detection - Success - Log Output"),
                                                "NULL PLAYER", c.getType().getCraftName(), c.getBlockList().length,
                                                c.getMinX(), c.getMinZ()));
                            }
                            CraftManager.getInstance().addCraft(c, p);
                        }
                    }
                }

            } else if (poll instanceof TranslationTask) {
                // Process translation task

                TranslationTask task = (TranslationTask) poll;
                Player p = CraftManager.getInstance().getPlayerFromCraft(c);
                Player notifyP = c.getNotificationPlayer();

                // Check that the craft hasn't been sneakily unpiloted
                // if ( p != null ) { cruiseOnPilot crafts don't have player
                // pilots

                if (task.getData().failed()) {
                    // The craft translation failed
                    if (notifyP != null && !c.getSinking())
                        notifyP.sendMessage(task.getData().getFailMessage());

                    if (task.getData().collisionExplosion()) {
                        MapUpdateCommand[] updates = task.getData().getUpdates();
                        c.setBlockList(task.getData().getBlockList());
                        c.setScheduledBlockChanges(task.getData().getScheduledBlockChanges());
                        boolean failed = MapUpdateManager.getInstance().addWorldUpdate(c.getW(), updates, null,
                                null);

                        if (failed) {
                            Movecraft.getInstance().getLogger().log(Level.SEVERE, String.format(
                                    I18nSupport.getInternationalisedString("Translation - Craft collision")));
                        } else {
                            sentMapUpdate = true;
                        }
                    }
                } else {
                    // The craft is clear to move, perform the block updates

                    MapUpdateCommand[] updates = task.getData().getUpdates();
                    EntityUpdateCommand[] eUpdates = task.getData().getEntityUpdates();
                    ItemDropUpdateCommand[] iUpdates = task.getData().getItemDropUpdateCommands();
                    // get list of cannons before sending map updates, to avoid
                    // conflicts
                    HashSet<Cannon> shipCannons = null;
                    if (Movecraft.getInstance().getCannonsPlugin() != null && c.getNotificationPlayer() != null) {
                        // convert blocklist to location list
                        List<Location> shipLocations = new ArrayList<Location>();
                        for (MovecraftLocation loc : c.getBlockList()) {
                            Location tloc = new Location(c.getW(), loc.getX(), loc.getY(), loc.getZ());
                            shipLocations.add(tloc);
                        }
                        shipCannons = Movecraft.getInstance().getCannonsPlugin().getCannonsAPI()
                                .getCannons(shipLocations, c.getNotificationPlayer().getUniqueId(), true);
                    }
                    boolean failed = MapUpdateManager.getInstance().addWorldUpdate(c.getW(), updates, eUpdates,
                            iUpdates);

                    if (!failed) {
                        sentMapUpdate = true;
                        c.setBlockList(task.getData().getBlockList());
                        c.setScheduledBlockChanges(task.getData().getScheduledBlockChanges());
                        c.setMinX(task.getData().getMinX());
                        c.setMinZ(task.getData().getMinZ());
                        c.setHitBox(task.getData().getHitbox());

                        // move any cannons that were present
                        if (Movecraft.getInstance().getCannonsPlugin() != null && shipCannons != null) {
                            for (Cannon can : shipCannons) {
                                can.move(new Vector(task.getData().getDx(), task.getData().getDy(),
                                        task.getData().getDz()));
                            }
                        }

                    } else {

                        Movecraft.getInstance().getLogger().log(Level.SEVERE, String
                                .format(I18nSupport.getInternationalisedString("Translation - Craft collision")));

                    }
                }

            } else if (poll instanceof RotationTask) {
                // Process rotation task
                RotationTask task = (RotationTask) poll;
                Player p = CraftManager.getInstance().getPlayerFromCraft(c);
                Player notifyP = c.getNotificationPlayer();

                // Check that the craft hasn't been sneakily unpiloted
                if (notifyP != null || task.getIsSubCraft()) {

                    if (task.isFailed()) {
                        // The craft translation failed, don't try to notify
                        // them if there is no pilot
                        if (notifyP != null)
                            notifyP.sendMessage(task.getFailMessage());
                        else
                            Movecraft.getInstance().getLogger().log(Level.INFO,
                                    "NULL Player Rotation Failed: " + task.getFailMessage());
                    } else {
                        MapUpdateCommand[] updates = task.getUpdates();
                        EntityUpdateCommand[] eUpdates = task.getEntityUpdates();

                        // get list of cannons before sending map updates, to
                        // avoid conflicts
                        HashSet<Cannon> shipCannons = null;
                        if (Movecraft.getInstance().getCannonsPlugin() != null
                                && c.getNotificationPlayer() != null) {
                            // convert blocklist to location list
                            List<Location> shipLocations = new ArrayList<Location>();
                            for (MovecraftLocation loc : c.getBlockList()) {
                                Location tloc = new Location(c.getW(), loc.getX(), loc.getY(), loc.getZ());
                                shipLocations.add(tloc);
                            }
                            shipCannons = Movecraft.getInstance().getCannonsPlugin().getCannonsAPI()
                                    .getCannons(shipLocations, c.getNotificationPlayer().getUniqueId(), true);
                        }

                        boolean failed = MapUpdateManager.getInstance().addWorldUpdate(c.getW(), updates, eUpdates,
                                null);

                        if (!failed) {
                            sentMapUpdate = true;

                            c.setBlockList(task.getBlockList());
                            c.setScheduledBlockChanges(task.getScheduledBlockChanges());
                            c.setMinX(task.getMinX());
                            c.setMinZ(task.getMinZ());
                            c.setHitBox(task.getHitbox());

                            // rotate any cannons that were present
                            if (Movecraft.getInstance().getCannonsPlugin() != null && shipCannons != null) {
                                Location tloc = new Location(task.getCraft().getW(), task.getOriginPoint().getX(),
                                        task.getOriginPoint().getY(), task.getOriginPoint().getZ());
                                for (Cannon can : shipCannons) {
                                    if (task.getRotation() == net.countercraft.movecraft.utils.Rotation.CLOCKWISE)
                                        can.rotateRight(tloc.toVector());
                                    if (task.getRotation() == net.countercraft.movecraft.utils.Rotation.ANTICLOCKWISE)
                                        can.rotateLeft(tloc.toVector());
                                }
                            }
                        } else {

                            Movecraft.getInstance().getLogger().log(Level.SEVERE, String
                                    .format(I18nSupport.getInternationalisedString("Rotation - Craft Collision")));

                        }
                    }
                }
            }

            ownershipMap.remove(poll);

            // only mark the craft as having finished updating if you didn't
            // send any updates to the map updater. Otherwise the map updater
            // will mark the crafts once it is done with them.
            if (!sentMapUpdate) {
                clear(c);
            }
        }
    }

    public void processCruise() {
        for (World w : Bukkit.getWorlds()) {
            if (w != null && CraftManager.getInstance().getCraftsInWorld(w) != null) {
                for (Craft pcraft : CraftManager.getInstance().getCraftsInWorld(w)) {
                    if ((pcraft != null) && pcraft.isNotProcessing()) {
                        if (pcraft.getCruising()) {
                            long ticksElapsed = (System.currentTimeMillis() - pcraft.getLastCruiseUpdate()) / 50;

                            // if the craft should go slower underwater, make
                            // time pass more slowly there
                            if (pcraft.getType().getHalfSpeedUnderwater() && pcraft.getMinY() < w.getSeaLevel())
                                ticksElapsed = ticksElapsed >> 1;
                            if (pcraft.getType().getDynamicLagSpeedFactor() > 0.0) { // craft speed should depend on how much lag it generates (IE: how many updates)
                                if (MapUpdateManager.getInstance().blockUpdatesPerCraft != null) {
                                    Integer numUpdates = MapUpdateManager.getInstance().blockUpdatesPerCraft
                                            .get(pcraft);
                                    if (numUpdates != null && numUpdates != 0) { // this will only be true right after a move for this craft, so this doesn't get executed TOO often
                                        double speedMultiplier = ((double) pcraft.getBlockList().length)
                                                / numUpdates;
                                        speedMultiplier = Math.sqrt(speedMultiplier);
                                        speedMultiplier = speedMultiplier
                                                * pcraft.getType().getDynamicLagSpeedFactor();
                                        double newMaxSpeed = 20.0 / pcraft.getType().getCruiseTickCooldown(); // get current base speed in bps
                                        newMaxSpeed = newMaxSpeed * speedMultiplier;
                                        pcraft.setMaxSpeed(newMaxSpeed);
                                        if (pcraft.getCurSpeed() > newMaxSpeed) // if craft is going too fast, slow it down. This usually happens after a turn or manuevre
                                            pcraft.setCurSpeed(newMaxSpeed);
                                    }
                                }
                            }
                            // check direct controls to modify movement
                            boolean bankLeft = false;
                            boolean bankRight = false;
                            boolean dive = false;
                            if (pcraft.getPilotLocked()) {
                                if (pcraft.getNotificationPlayer().isSneaking())
                                    dive = true;
                                if (pcraft.getNotificationPlayer().getInventory().getHeldItemSlot() == 3)
                                    bankLeft = true;
                                if (pcraft.getNotificationPlayer().getInventory().getHeldItemSlot() == 5)
                                    bankRight = true;
                            }

                            if (Math.abs(ticksElapsed) >= pcraft.getCurTickCooldown()) {
                                int dx = 0;
                                int dz = 0;
                                int dy = 0;

                                // ascend
                                if (pcraft.getCruiseDirection() == 0x42) {
                                    dy = 0 + 1 + pcraft.getType().getVertCruiseSkipBlocks();
                                }
                                // descend
                                if (pcraft.getCruiseDirection() == 0x43) {
                                    dy = 0 - 1 - pcraft.getType().getVertCruiseSkipBlocks();
                                    if (pcraft.getMinY() <= w.getSeaLevel())
                                        dy = -1;
                                } else {
                                    if (dive) {
                                        dy = 0 - ((pcraft.getType().getCruiseSkipBlocks() + 1) >> 1);
                                        if (pcraft.getMinY() <= w.getSeaLevel())
                                            dy = -1;
                                    }
                                }
                                // ship faces west
                                if (pcraft.getCruiseDirection() == 0x5) {
                                    dx = 0 - 1 - pcraft.getType().getCruiseSkipBlocks();
                                    if (bankRight) {
                                        dz = (0 - 1 - pcraft.getType().getCruiseSkipBlocks()) >> 1;
                                    }
                                    if (bankLeft) {
                                        dz = (1 + pcraft.getType().getCruiseSkipBlocks()) >> 1;
                                    }
                                }
                                // ship faces east
                                if (pcraft.getCruiseDirection() == 0x4) {
                                    dx = 1 + pcraft.getType().getCruiseSkipBlocks();
                                    if (bankLeft) {
                                        dz = (0 - 1 - pcraft.getType().getCruiseSkipBlocks()) >> 1;
                                    }
                                    if (bankRight) {
                                        dz = (1 + pcraft.getType().getCruiseSkipBlocks()) >> 1;
                                    }
                                }
                                // ship faces north
                                if (pcraft.getCruiseDirection() == 0x2) {
                                    dz = 1 + pcraft.getType().getCruiseSkipBlocks();
                                    if (bankRight) {
                                        dx = (0 - 1 - pcraft.getType().getCruiseSkipBlocks()) >> 1;
                                    }
                                    if (bankLeft) {
                                        dx = (1 + pcraft.getType().getCruiseSkipBlocks()) >> 1;
                                    }
                                }
                                // ship faces south
                                if (pcraft.getCruiseDirection() == 0x3) {
                                    dz = 0 - 1 - pcraft.getType().getCruiseSkipBlocks();
                                    if (bankLeft) {
                                        dx = (0 - 1 - pcraft.getType().getCruiseSkipBlocks()) >> 1;
                                    }
                                    if (bankRight) {
                                        dx = (1 + pcraft.getType().getCruiseSkipBlocks()) >> 1;
                                    }
                                }
                                if (pcraft.getType().getCruiseOnPilot())
                                    dy = pcraft.getType().getCruiseOnPilotVertMove();
                                pcraft.translate(dx, dy, dz);
                                pcraft.setLastDX(dx);
                                pcraft.setLastDZ(dz);
                                if (pcraft.getLastCruiseUpdate() != -1) {
                                    pcraft.setLastCruisUpdate(System.currentTimeMillis());
                                } else {
                                    pcraft.setLastCruisUpdate(System.currentTimeMillis() - 30000);
                                }

                                if (pcraft.getCurSpeed() < pcraft.getMaxSpeed()) { // increase velocity of cruising craft and logarithmically approach maxspeed
                                    double difference = pcraft.getMaxSpeed() - pcraft.getCurSpeed();
                                    double newSpeed = pcraft.getCurSpeed();
                                    if (difference / 10.0 < 0.01)
                                        newSpeed += 0.01;
                                    else
                                        newSpeed += difference / 10.0;
                                    pcraft.setCurSpeed(newSpeed);
                                }
                            }

                        } /* else {
                              
                            if (pcraft.getPilotLocked() == true && pcraft.isNotProcessing()) {
                              
                               Player p = CraftManager.getInstance().getPlayerFromCraft(pcraft);
                               if (p != null)
                                  if (MathUtils.playerIsWithinBoundingPolygon(pcraft.getHitBox(), pcraft.getMinX(),
                               pcraft.getMinZ(), MathUtils.bukkit2MovecraftLoc(p.getLocation()))) {
                            double movedX = p.getLocation().getX() - pcraft.getPilotLockedX();
                            double movedZ = p.getLocation().getZ() - pcraft.getPilotLockedZ();
                            int dX = 0;
                            int dZ = 0;
                            if (movedX > 0.15)
                               dX = 1;
                            else if (movedX < -0.15)
                               dX = -1;
                            if (movedZ > 0.15)
                               dZ = 1;
                            else if (movedZ < -0.15)
                               dZ = -1;
                            if (dX != 0 || dZ != 0) {
                               long timeSinceLastMoveCommand = System.currentTimeMillis()
                                     - pcraft.getLastRightClick();
                               // wait before accepting new move
                               // commands to help with bouncing.
                               // Also ignore extreme movement
                               if (Math.abs(movedX) < 0.2 && Math.abs(movedZ) < 0.2
                                     && timeSinceLastMoveCommand > 300) {
                              
                                  pcraft.setLastRightClick(System.currentTimeMillis());
                                  long ticksElapsed = (System.currentTimeMillis()
                                        - pcraft.getLastCruiseUpdate()) / 50;
                              
                                  // if the craft should go slower
                                  // underwater, make time pass
                                  // more slowly there
                                  if (pcraft.getType().getHalfSpeedUnderwater()
                                        && pcraft.getMinY() < w.getSeaLevel())
                                     ticksElapsed = ticksElapsed >> 1;
                              
                                  if (Math.abs(ticksElapsed) >= pcraft.getType().getTickCooldown()) {
                                     pcraft.translate(dX, 0, dZ);
                                     pcraft.setLastCruisUpdate(System.currentTimeMillis());
                                  }
                                  pcraft.setLastDX(dX);
                                  pcraft.setLastDY(0);
                                  pcraft.setLastDZ(dZ);
                                  pcraft.setKeepMoving(true);
                               } else {
                                  Location loc = p.getLocation();
                                  loc.setX(pcraft.getPilotLockedX());
                                  loc.setY(pcraft.getPilotLockedY());
                                  loc.setZ(pcraft.getPilotLockedZ());
                                  Vector pVel = new Vector(0.0, 0.0, 0.0);
                                  p.teleport(loc);
                                  p.setVelocity(pVel);
                               }
                            }
                                  }
                            }
                          }*/
                    }
                }
            }
        }

    }

    private boolean isRegionBlockedPVP(MovecraftLocation loc, World w) {
        if (Movecraft.getInstance().getWorldGuardPlugin() == null)
            return false;
        if (Settings.WorldGuardBlockSinkOnPVPPerm == false)
            return false;

        Location nativeLoc = new Location(w, loc.getX(), loc.getY(), loc.getZ());
        ApplicableRegionSet set = Movecraft.getInstance().getWorldGuardPlugin().getRegionManager(w)
                .getApplicableRegions(nativeLoc);
        if (set.allows(DefaultFlag.PVP) == false) {
            return true;
        }
        return false;
    }

    private boolean isRegionFlagSinkAllowed(MovecraftLocation loc, World w) {
        if (Movecraft.getInstance().getWorldGuardPlugin() != null
                && Movecraft.getInstance().getWGCustomFlagsPlugin() != null && Settings.WGCustomFlagsUseSinkFlag) {
            Location nativeLoc = new Location(w, loc.getX(), loc.getY(), loc.getZ());
            WGCustomFlagsUtils WGCFU = new WGCustomFlagsUtils();
            return WGCFU.validateFlag(nativeLoc, Movecraft.FLAG_SINK);
        } else {
            return true;
        }
    }

    private Location isTownyPlotPVPEnabled(MovecraftLocation loc, World w, Set<TownBlock> townBlockSet) {
        Location plugLoc = new Location(w, loc.getX(), loc.getY(), loc.getZ());
        TownBlock townBlock = TownyUtils.getTownBlock(plugLoc);
        if (townBlock != null && !townBlockSet.contains(townBlock)) {
            if (TownyUtils.validatePVP(townBlock)) {
                townBlockSet.add(townBlock);
                return null;
            } else {
                return plugLoc;
            }
        } else {
            return null;
        }
    }

    public void processSinking() {
        for (World w : Bukkit.getWorlds()) {
            if (w != null && CraftManager.getInstance().getCraftsInWorld(w) != null) {
                TownyWorld townyWorld = null;
                boolean townyEnabled = false;
                if (Movecraft.getInstance().getTownyPlugin() != null && Settings.TownyBlockSinkOnNoPVP) {
                    townyWorld = TownyUtils.getTownyWorld(w);
                    if (townyWorld != null) {
                        townyEnabled = townyWorld.isUsingTowny();
                    }
                }
                // check every few seconds for every craft to see if it should
                // be sinking or disabled
                for (Craft pcraft : CraftManager.getInstance().getCraftsInWorld(w)) {
                    Set<TownBlock> townBlockSet = new HashSet<TownBlock>();
                    if (pcraft != null && pcraft.getSinking() == false) {
                        if (pcraft.getType().getSinkPercent() != 0.0 && pcraft.isNotProcessing()) {
                            long ticksElapsed = (System.currentTimeMillis() - pcraft.getLastBlockCheck()) / 50;

                            if (ticksElapsed > Settings.SinkCheckTicks) {
                                int totalNonAirBlocks = 0;
                                int totalNonAirWaterBlocks = 0;
                                HashMap<ArrayList<Integer>, Integer> foundFlyBlocks = new HashMap<ArrayList<Integer>, Integer>();
                                HashMap<ArrayList<Integer>, Integer> foundMoveBlocks = new HashMap<ArrayList<Integer>, Integer>();
                                boolean regionPVPBlocked = false;
                                boolean sinkingForbiddenByFlag = false;
                                boolean sinkingForbiddenByTowny = false;
                                // go through each block in the blocklist, and
                                // if its in the FlyBlocks, total up the number
                                // of them
                                Location townyLoc = null;
                                for (MovecraftLocation l : pcraft.getBlockList()) {
                                    if (isRegionBlockedPVP(l, w))
                                        regionPVPBlocked = true;
                                    if (!isRegionFlagSinkAllowed(l, w))
                                        sinkingForbiddenByFlag = true;
                                    if (townyLoc == null && townyEnabled && Settings.TownyBlockSinkOnNoPVP) {
                                        townyLoc = isTownyPlotPVPEnabled(l, w, townBlockSet);
                                        if (townyLoc != null) {
                                            sinkingForbiddenByTowny = true;
                                        }
                                    }
                                    Integer blockID = w.getBlockAt(l.getX(), l.getY(), l.getZ()).getTypeId();
                                    Integer dataID = (int) w.getBlockAt(l.getX(), l.getY(), l.getZ()).getData();
                                    Integer shiftedID = (blockID << 4) + dataID + 10000;
                                    for (ArrayList<Integer> flyBlockDef : pcraft.getType().getFlyBlocks()
                                            .keySet()) {
                                        if (flyBlockDef.contains(blockID) || flyBlockDef.contains(shiftedID)) {
                                            Integer count = foundFlyBlocks.get(flyBlockDef);
                                            if (count == null) {
                                                foundFlyBlocks.put(flyBlockDef, 1);
                                            } else {
                                                foundFlyBlocks.put(flyBlockDef, count + 1);
                                            }
                                        }
                                    }
                                    if (pcraft.getType().getMoveBlocks() != null) {
                                        for (ArrayList<Integer> moveBlockDef : pcraft.getType().getMoveBlocks()
                                                .keySet()) {
                                            if (moveBlockDef.contains(blockID)
                                                    || moveBlockDef.contains(shiftedID)) {
                                                Integer count = foundMoveBlocks.get(moveBlockDef);
                                                if (count == null) {
                                                    foundMoveBlocks.put(moveBlockDef, 1);
                                                } else {
                                                    foundMoveBlocks.put(moveBlockDef, count + 1);
                                                }
                                            }
                                        }
                                    }

                                    if (blockID != 0) {
                                        totalNonAirBlocks++;
                                    }
                                    if (blockID != 0 && blockID != 8 && blockID != 9) {
                                        totalNonAirWaterBlocks++;
                                    }
                                }

                                // now see if any of the resulting percentages
                                // are below the threshold specified in
                                // SinkPercent
                                boolean isSinking = false;

                                for (ArrayList<Integer> i : pcraft.getType().getFlyBlocks().keySet()) {
                                    int numfound = 0;
                                    if (foundFlyBlocks.get(i) != null) {
                                        numfound = foundFlyBlocks.get(i);
                                    }
                                    double percent = ((double) numfound / (double) totalNonAirBlocks) * 100.0;
                                    double flyPercent = pcraft.getType().getFlyBlocks().get(i).get(0);
                                    double sinkPercent = flyPercent * pcraft.getType().getSinkPercent() / 100.0;
                                    if (percent < sinkPercent) {
                                        isSinking = true;
                                    }

                                }
                                if (pcraft.getType().getMoveBlocks() != null) {
                                    for (ArrayList<Integer> i : pcraft.getType().getMoveBlocks().keySet()) {
                                        int numfound = 0;
                                        if (foundMoveBlocks.get(i) != null) {
                                            numfound = foundMoveBlocks.get(i);
                                        }
                                        double percent = ((double) numfound / (double) totalNonAirBlocks) * 100.0;
                                        double movePercent = pcraft.getType().getMoveBlocks().get(i).get(0);
                                        double disablePercent = movePercent * pcraft.getType().getSinkPercent()
                                                / 100.0;
                                        if (percent < disablePercent && pcraft.getDisabled() == false
                                                && pcraft.isNotProcessing()) {
                                            pcraft.setDisabled(true);
                                            if (pcraft.getNotificationPlayer() != null) {
                                                Location loc = pcraft.getNotificationPlayer().getLocation();
                                                pcraft.getW().playSound(loc, Sound.ENTITY_IRONGOLEM_DEATH, 5.0f,
                                                        5.0f);
                                            }
                                        }
                                    }

                                }

                                // And check the overallsinkpercent
                                if (pcraft.getType().getOverallSinkPercent() != 0.0) {
                                    double percent;
                                    if (pcraft.getType().blockedByWater()) {
                                        percent = (double) totalNonAirBlocks / (double) pcraft.getOrigBlockCount();
                                    } else {
                                        percent = (double) totalNonAirWaterBlocks
                                                / (double) pcraft.getOrigBlockCount();
                                    }
                                    if (percent * 100.0 < pcraft.getType().getOverallSinkPercent()) {
                                        isSinking = true;
                                    }
                                }

                                if (totalNonAirBlocks == 0) {
                                    isSinking = true;
                                }

                                if (isSinking
                                        && (regionPVPBlocked || sinkingForbiddenByFlag || sinkingForbiddenByTowny)
                                        && pcraft.isNotProcessing()) {
                                    Player p = CraftManager.getInstance().getPlayerFromCraft(pcraft);
                                    Player notifyP = pcraft.getNotificationPlayer();
                                    if (notifyP != null)
                                        if (regionPVPBlocked) {
                                            notifyP.sendMessage(
                                                    String.format(I18nSupport.getInternationalisedString(
                                                            "Player- Craft should sink but PVP is not allowed in this WorldGuard region")));
                                        } else if (sinkingForbiddenByFlag) {
                                            notifyP.sendMessage(
                                                    String.format(I18nSupport.getInternationalisedString(
                                                            "WGCustomFlags - Sinking a craft is not allowed in this WorldGuard region")));
                                        } else {
                                            if (townyLoc != null) {
                                                notifyP.sendMessage(String.format(
                                                        I18nSupport.getInternationalisedString(
                                                                "Towny - Sinking a craft is not allowed in this town plot")
                                                                + " @ %d,%d,%d",
                                                        townyLoc.getBlockX(), townyLoc.getBlockY(),
                                                        townyLoc.getBlockZ()));
                                            } else {
                                                notifyP.sendMessage(
                                                        String.format(I18nSupport.getInternationalisedString(
                                                                "Towny - Sinking a craft is not allowed in this town plot")));
                                            }

                                        }
                                    pcraft.setCruising(false);
                                    CraftManager.getInstance().removeCraft(pcraft);
                                } else {
                                    // if the craft is sinking, let the player
                                    // know and release the craft. Otherwise
                                    // update the time for the next check
                                    if (isSinking && pcraft.isNotProcessing()) {
                                        Player p = CraftManager.getInstance().getPlayerFromCraft(pcraft);
                                        Player notifyP = pcraft.getNotificationPlayer();
                                        if (notifyP != null)
                                            notifyP.sendMessage(String.format(I18nSupport
                                                    .getInternationalisedString("Player- Craft is sinking")));
                                        pcraft.setCruising(false);
                                        pcraft.setSinking(true);
                                        CraftManager.getInstance().removePlayerFromCraft(pcraft);
                                        final Craft releaseCraft = pcraft;
                                        BukkitTask releaseTask = new BukkitRunnable() {
                                            @Override
                                            public void run() {
                                                CraftManager.getInstance().removeCraft(releaseCraft);
                                            }
                                        }.runTaskLater(Movecraft.getInstance(), (20 * 600));
                                    } else {
                                        pcraft.setLastBlockCheck(System.currentTimeMillis());
                                    }
                                }
                            }
                        }
                    }
                }

                // sink all the sinking ships
                if (CraftManager.getInstance().getCraftsInWorld(w) != null)
                    for (Craft pcraft : CraftManager.getInstance().getCraftsInWorld(w)) {
                        if (pcraft != null && pcraft.getSinking() == true) {
                            if (pcraft.getBlockList().length == 0) {
                                CraftManager.getInstance().removeCraft(pcraft);
                            }
                            if (pcraft.getMinY() < 5) {
                                CraftManager.getInstance().removeCraft(pcraft);
                            }
                            long ticksElapsed = (System.currentTimeMillis() - pcraft.getLastCruiseUpdate()) / 50;
                            if (Math.abs(ticksElapsed) >= pcraft.getType().getSinkRateTicks()) {
                                int dx = 0;
                                int dz = 0;
                                if (pcraft.getType().getKeepMovingOnSink()) {
                                    dx = pcraft.getLastDX();
                                    dz = pcraft.getLastDZ();
                                }
                                pcraft.translate(dx, -1, dz);
                                pcraft.setScheduledBlockChanges(null); // don't bother restoring color blocks for a sinking ship
                                if (pcraft.getLastCruiseUpdate() != -1) {
                                    pcraft.setLastCruisUpdate(System.currentTimeMillis());
                                } else {
                                    pcraft.setLastCruisUpdate(System.currentTimeMillis() - 30000);
                                }
                            }
                        }
                    }
            }
        }
    }

    public void processTracers() {
        if (Settings.TracerRateTicks == 0)
            return;
        long ticksElapsed = (System.currentTimeMillis() - lastTracerUpdate) / 50;
        if (ticksElapsed > Settings.TracerRateTicks) {
            for (World w : Bukkit.getWorlds()) {
                if (w != null) {
                    for (org.bukkit.entity.TNTPrimed tnt : w
                            .getEntitiesByClass(org.bukkit.entity.TNTPrimed.class)) {
                        if (tnt.getVelocity().lengthSquared() > 0.25) {
                            for (Player p : w.getPlayers()) {
                                // is the TNT within the view distance (rendered
                                // world) of the player?
                                long maxDistSquared = Bukkit.getServer().getViewDistance() * 16;
                                maxDistSquared = maxDistSquared - 16;
                                maxDistSquared = maxDistSquared * maxDistSquared;

                                if (p.getLocation().distanceSquared(tnt.getLocation()) < maxDistSquared) { // we
                                    // use
                                    // squared
                                    // because
                                    // its
                                    // faster
                                    final Location loc = tnt.getLocation();
                                    final Player fp = p;
                                    final World fw = w;
                                    // then make a cobweb to look like smoke,
                                    // place it a little later so it isn't right
                                    // in the middle of the volley
                                    BukkitTask placeCobweb = new BukkitRunnable() {
                                        @Override
                                        public void run() {
                                            fp.sendBlockChange(loc, 30, (byte) 0);
                                        }
                                    }.runTaskLater(Movecraft.getInstance(), 5);
                                    // then remove it
                                    BukkitTask removeCobweb = new BukkitRunnable() {
                                        @Override
                                        public void run() {
                                            // fp.sendBlockChange(loc,
                                            // fw.getBlockAt(loc).getType(),
                                            // fw.getBlockAt(loc).getData());
                                            fp.sendBlockChange(loc, 0, (byte) 0);
                                        }
                                    }.runTaskLater(Movecraft.getInstance(), 160);
                                }
                            }
                        }
                    }
                }
            }
            lastTracerUpdate = System.currentTimeMillis();
        }
    }

    public void processFireballs() {
        long ticksElapsed = (System.currentTimeMillis() - lastFireballCheck) / 50;

        if (ticksElapsed > 3) {
            for (World w : Bukkit.getWorlds()) {
                if (w != null) {
                    for (org.bukkit.entity.SmallFireball fireball : w
                            .getEntitiesByClass(org.bukkit.entity.SmallFireball.class)) {
                        if (!(fireball.getShooter() instanceof org.bukkit.entity.LivingEntity)) { // means
                            // it
                            // was
                            // launched
                            // by
                            // a
                            // dispenser
                            if (w.getPlayers().size() > 0) {
                                Player p = w.getPlayers().get(0);

                                final org.bukkit.entity.SmallFireball ffb = fireball;
                                if (!FireballTracking.containsKey(fireball)) {
                                    Craft c = fastNearestCraftToLoc(ffb.getLocation());
                                    if (c != null) {
                                        int distX = c.getMinX() + c.getMaxX();
                                        distX = distX >> 1;
                                        distX = Math.abs(distX - ffb.getLocation().getBlockX());
                                        int distY = c.getMinY() + c.getMaxY();
                                        distY = distY >> 1;
                                        distY = Math.abs(distY - ffb.getLocation().getBlockY());
                                        int distZ = c.getMinZ() + c.getMaxZ();
                                        distZ = distZ >> 1;
                                        distZ = Math.abs(distZ - ffb.getLocation().getBlockZ());
                                        boolean inRange = (distX < 50) && (distY < 50) && (distZ < 50);
                                        if ((c.getAADirector() != null) && inRange) {
                                            p = c.getAADirector();
                                            if (p.getItemInHand().getTypeId() == Settings.PilotTool) {
                                                Vector fv = ffb.getVelocity();
                                                double speed = fv.length(); // store the speed to add it back in later, since all the values we will be using are "normalized", IE: have a speed of 1
                                                fv = fv.normalize(); // you normalize it for comparison with the new direction to see if we are trying to steer too far
                                                Block targetBlock = p.getTargetBlock(transparent, 120);
                                                Vector targetVector;
                                                if (targetBlock == null) { // the player is looking at nothing, shoot in that general direction
                                                    targetVector = p.getLocation().getDirection();
                                                } else { // shoot directly at the block the player is looking at (IE: with convergence)
                                                    targetVector = targetBlock.getLocation().toVector()
                                                            .subtract(ffb.getLocation().toVector());
                                                    targetVector = targetVector.normalize();
                                                }
                                                if (targetVector.getX() - fv.getX() > 0.5) {
                                                    fv.setX(fv.getX() + 0.5);
                                                } else if (targetVector.getX() - fv.getX() < -0.5) {
                                                    fv.setX(fv.getX() - 0.5);
                                                } else {
                                                    fv.setX(targetVector.getX());
                                                }
                                                if (targetVector.getY() - fv.getY() > 0.5) {
                                                    fv.setY(fv.getY() + 0.5);
                                                } else if (targetVector.getY() - fv.getY() < -0.5) {
                                                    fv.setY(fv.getY() - 0.5);
                                                } else {
                                                    fv.setY(targetVector.getY());
                                                }
                                                if (targetVector.getZ() - fv.getZ() > 0.5) {
                                                    fv.setZ(fv.getZ() + 0.5);
                                                } else if (targetVector.getZ() - fv.getZ() < -0.5) {
                                                    fv.setZ(fv.getZ() - 0.5);
                                                } else {
                                                    fv.setZ(targetVector.getZ());
                                                }
                                                fv = fv.multiply(speed); // put the original speed back in, but now along a different trajectory                                 
                                                fireball.setVelocity(fv);
                                                fireball.setDirection(fv);
                                            }
                                        } else {
                                            p = c.getNotificationPlayer();
                                        }
                                    }
                                    // give it a living shooter, then set the
                                    // fireball to be deleted
                                    fireball.setShooter(p);
                                    FireballTracking.put(fireball, System.currentTimeMillis());
                                }
                            }
                        }
                    }
                }
            }

            int timelimit = 20 * Settings.FireballLifespan * 50;
            // then, removed any exploded TNT from tracking
            Iterator<org.bukkit.entity.SmallFireball> fireballI = FireballTracking.keySet().iterator();
            while (fireballI.hasNext()) {
                org.bukkit.entity.SmallFireball fireball = fireballI.next();
                if (fireball != null)
                    if (System.currentTimeMillis() - FireballTracking.get(fireball) > timelimit) {
                        fireball.remove();
                        fireballI.remove();
                    }
            }

            lastFireballCheck = System.currentTimeMillis();
        }
    }

    private Craft fastNearestCraftToLoc(Location loc) {
        Craft ret = null;
        long closestDistSquared = 1000000000l;
        Craft[] craftsList = CraftManager.getInstance().getCraftsInWorld(loc.getWorld());
        if (craftsList != null) {
            for (Craft i : craftsList) {
                int midX = (i.getMaxX() + i.getMinX()) >> 1;
                //            int midY=(i.getMaxY()+i.getMinY())>>1; don't check Y because it is slow
                int midZ = (i.getMaxZ() + i.getMinZ()) >> 1;
                long distSquared = Math.abs(midX - (int) loc.getX());
                //            distSquared+=Math.abs(midY-(int)loc.getY());
                distSquared += Math.abs(midZ - (int) loc.getZ());
                if (distSquared < closestDistSquared) {
                    closestDistSquared = distSquared;
                    ret = i;
                }
            }
        }
        return ret;
    }

    public void processTNTContactExplosives() {
        long ticksElapsed = (System.currentTimeMillis() - lastTNTContactCheck) / 50;
        if (ticksElapsed > 0) {
            // see if there is any new rapid moving TNT in the worlds
            for (World w : Bukkit.getWorlds()) {
                if (w != null) {
                    for (org.bukkit.entity.TNTPrimed tnt : w
                            .getEntitiesByClass(org.bukkit.entity.TNTPrimed.class)) {
                        if ((tnt.getVelocity().lengthSquared() > 0.35)) {
                            if (!TNTTracking.containsKey(tnt)) {
                                Craft c = fastNearestCraftToLoc(tnt.getLocation());
                                if (c != null) {
                                    int distX = c.getMinX() + c.getMaxX();
                                    distX = distX >> 1;
                                    distX = Math.abs(distX - tnt.getLocation().getBlockX());
                                    int distY = c.getMinY() + c.getMaxY();
                                    distY = distY >> 1;
                                    distY = Math.abs(distY - tnt.getLocation().getBlockY());
                                    int distZ = c.getMinZ() + c.getMaxZ();
                                    distZ = distZ >> 1;
                                    distZ = Math.abs(distZ - tnt.getLocation().getBlockZ());
                                    boolean inRange = (distX < 100) && (distY < 100) && (distZ < 100);
                                    if ((c.getCannonDirector() != null) && inRange) {
                                        Player p = c.getCannonDirector();
                                        if (p.getItemInHand().getTypeId() == Settings.PilotTool) {
                                            Vector tv = tnt.getVelocity();
                                            double speed = tv.length(); // store the speed to add it back in later, since all the values we will be using are "normalized", IE: have a speed of 1
                                            tv = tv.normalize(); // you normalize it for comparison with the new direction to see if we are trying to steer too far
                                            Block targetBlock = p.getTargetBlock(transparent, 120);
                                            Vector targetVector;
                                            if (targetBlock == null) { // the player is looking at nothing, shoot in that general direction
                                                targetVector = p.getLocation().getDirection();
                                            } else { // shoot directly at the block the player is looking at (IE: with convergence)
                                                targetVector = targetBlock.getLocation().toVector()
                                                        .subtract(tnt.getLocation().toVector());
                                                targetVector = targetVector.normalize();
                                            }
                                            if (targetVector.getX() - tv.getX() > 0.7) {
                                                tv.setX(tv.getX() + 0.7);
                                            } else if (targetVector.getX() - tv.getX() < -0.7) {
                                                tv.setX(tv.getX() - 0.7);
                                            } else {
                                                tv.setX(targetVector.getX());
                                            }
                                            if (targetVector.getZ() - tv.getZ() > 0.7) {
                                                tv.setZ(tv.getZ() + 0.7);
                                            } else if (targetVector.getZ() - tv.getZ() < -0.7) {
                                                tv.setZ(tv.getZ() - 0.7);
                                            } else {
                                                tv.setZ(targetVector.getZ());
                                            }
                                            tv = tv.multiply(speed); // put the original speed back in, but now along a different trajectory                                 
                                            tv.setY(tnt.getVelocity().getY()); // you leave the original Y (or vertical axis) trajectory as it was
                                            tnt.setVelocity(tv);
                                        }
                                    }
                                }
                                TNTTracking.put(tnt, tnt.getVelocity().lengthSquared());
                            }
                        }
                    }
                }
            }

            // then, removed any exploded TNT from tracking
            Iterator<org.bukkit.entity.TNTPrimed> tntI = TNTTracking.keySet().iterator();
            while (tntI.hasNext()) {
                org.bukkit.entity.TNTPrimed tnt = tntI.next();
                if (tnt.getFuseTicks() <= 0) {
                    tntI.remove();
                }
            }

            // now check to see if any has abruptly changed velocity, and should
            // explode
            for (org.bukkit.entity.TNTPrimed tnt : TNTTracking.keySet()) {
                double vel = tnt.getVelocity().lengthSquared();
                if (vel < TNTTracking.get(tnt) / 10.0) {
                    tnt.setFuseTicks(0);
                } else {
                    // update the tracking with the new velocity so gradual
                    // changes do not make TNT explode
                    TNTTracking.put(tnt, vel);
                }
            }

            lastTNTContactCheck = System.currentTimeMillis();
        }
    }

    public void processFadingBlocks() {
        if (Settings.FadeWrecksAfter == 0)
            return;
        long ticksElapsed = (System.currentTimeMillis() - lastFadeCheck) / 50;
        if (ticksElapsed > 20) {
            for (World w : Bukkit.getWorlds()) {
                if (w != null) {
                    ArrayList<MapUpdateCommand> updateCommands = new ArrayList<MapUpdateCommand>();
                    CopyOnWriteArrayList<MovecraftLocation> locations = null;

                    // I know this is horrible, but I honestly don't see another
                    // way to do this...
                    int numTries = 0;
                    while ((locations == null) && (numTries < 100)) {
                        try {
                            locations = new CopyOnWriteArrayList<MovecraftLocation>(
                                    Movecraft.getInstance().blockFadeTimeMap.keySet());
                        } catch (java.util.ConcurrentModificationException e) {
                            numTries++;
                        } catch (java.lang.NegativeArraySizeException e) {
                            Movecraft.getInstance().blockFadeTimeMap = new HashMap<MovecraftLocation, Long>();
                            Movecraft.getInstance().blockFadeTypeMap = new HashMap<MovecraftLocation, Integer>();
                            Movecraft.getInstance().blockFadeWaterMap = new HashMap<MovecraftLocation, Boolean>();
                            Movecraft.getInstance().blockFadeWorldMap = new HashMap<MovecraftLocation, World>();
                            locations = new CopyOnWriteArrayList<MovecraftLocation>(
                                    Movecraft.getInstance().blockFadeTimeMap.keySet());
                        }
                    }

                    for (MovecraftLocation loc : locations) {
                        if (Movecraft.getInstance().blockFadeWorldMap.get(loc) == w) {
                            Long time = Movecraft.getInstance().blockFadeTimeMap.get(loc);
                            Integer type = Movecraft.getInstance().blockFadeTypeMap.get(loc);
                            Boolean water = Movecraft.getInstance().blockFadeWaterMap.get(loc);
                            if (time != null && type != null && water != null) {
                                long secsElapsed = (System.currentTimeMillis()
                                        - Movecraft.getInstance().blockFadeTimeMap.get(loc)) / 1000;
                                // has enough time passed to fade the block?
                                boolean timeElapsed = false;
                                // make containers take longer to fade so their loot can be recovered
                                if (w.getBlockTypeIdAt(loc.getX(), loc.getY(), loc.getZ()) == 54) {
                                    timeElapsed = secsElapsed > Settings.FadeWrecksAfter * 5;
                                } else if (w.getBlockTypeIdAt(loc.getX(), loc.getY(), loc.getZ()) == 146) {
                                    timeElapsed = secsElapsed > Settings.FadeWrecksAfter * 5;
                                } else if (w.getBlockTypeIdAt(loc.getX(), loc.getY(), loc.getZ()) == 158) {
                                    timeElapsed = secsElapsed > Settings.FadeWrecksAfter * 5;
                                } else if (w.getBlockTypeIdAt(loc.getX(), loc.getY(), loc.getZ()) == 23) {
                                    timeElapsed = secsElapsed > Settings.FadeWrecksAfter * 5;
                                } else {
                                    timeElapsed = secsElapsed > Settings.FadeWrecksAfter;
                                }
                                if (timeElapsed) {
                                    // load the chunk if it hasn't been already
                                    int cx = loc.getX() >> 4;
                                    int cz = loc.getZ() >> 4;
                                    if (w.isChunkLoaded(cx, cz) == false) {
                                        w.loadChunk(cx, cz);
                                    }
                                    // check to see if the block type has
                                    // changed, if so don't fade it
                                    if (w.getBlockTypeIdAt(loc.getX(), loc.getY(),
                                            loc.getZ()) == Movecraft.getInstance().blockFadeTypeMap.get(loc)) {
                                        // should it become water? if not, then
                                        // air
                                        if (Movecraft.getInstance().blockFadeWaterMap.get(loc) == true) {
                                            MapUpdateCommand updateCom = new MapUpdateCommand(loc, 9, (byte) 0,
                                                    null);
                                            updateCommands.add(updateCom);
                                        } else {
                                            MapUpdateCommand updateCom = new MapUpdateCommand(loc, 0, (byte) 0,
                                                    null);
                                            updateCommands.add(updateCom);
                                        }
                                    }
                                    Movecraft.getInstance().blockFadeTimeMap.remove(loc);
                                    Movecraft.getInstance().blockFadeTypeMap.remove(loc);
                                    Movecraft.getInstance().blockFadeWorldMap.remove(loc);
                                    Movecraft.getInstance().blockFadeWaterMap.remove(loc);
                                }
                            }
                        }
                    }
                    if (updateCommands.size() > 0) {
                        MapUpdateManager.getInstance().addWorldUpdate(w,
                                updateCommands.toArray(new MapUpdateCommand[1]), null, null);
                    }
                }
            }

            lastFadeCheck = System.currentTimeMillis();
        }
    }

    public void processDetection() {
        long ticksElapsed = (System.currentTimeMillis() - lastContactCheck) / 50;
        if (ticksElapsed > 21) {
            for (World w : Bukkit.getWorlds()) {
                if (w != null && CraftManager.getInstance().getCraftsInWorld(w) != null) {
                    for (Craft ccraft : CraftManager.getInstance().getCraftsInWorld(w)) {
                        if (CraftManager.getInstance().getPlayerFromCraft(ccraft) != null) {
                            if (!recentContactTracking.containsKey(ccraft)) {
                                recentContactTracking.put(ccraft, new HashMap<Craft, Long>());
                            }
                            for (Craft tcraft : CraftManager.getInstance().getCraftsInWorld(w)) {
                                long cposx = ccraft.getMaxX() + ccraft.getMinX();
                                long cposy = ccraft.getMaxY() + ccraft.getMinY();
                                long cposz = ccraft.getMaxZ() + ccraft.getMinZ();
                                cposx = cposx >> 1;
                                cposy = cposy >> 1;
                                cposz = cposz >> 1;
                                long tposx = tcraft.getMaxX() + tcraft.getMinX();
                                long tposy = tcraft.getMaxY() + tcraft.getMinY();
                                long tposz = tcraft.getMaxZ() + tcraft.getMinZ();
                                tposx = tposx >> 1;
                                tposy = tposy >> 1;
                                tposz = tposz >> 1;
                                long diffx = cposx - tposx;
                                long diffy = cposy - tposy;
                                long diffz = cposz - tposz;
                                long distsquared = Math.abs(diffx) * Math.abs(diffx);
                                distsquared += Math.abs(diffy) * Math.abs(diffy);
                                distsquared += Math.abs(diffz) * Math.abs(diffz);
                                long detectionRange = 0;
                                if (tposy > 65) {
                                    detectionRange = (long) (Math.sqrt(tcraft.getOrigBlockCount())
                                            * tcraft.getType().getDetectionMultiplier());
                                } else {
                                    detectionRange = (long) (Math.sqrt(tcraft.getOrigBlockCount())
                                            * tcraft.getType().getUnderwaterDetectionMultiplier());
                                }
                                if (distsquared < detectionRange * detectionRange
                                        && tcraft.getNotificationPlayer() != ccraft.getNotificationPlayer()) {
                                    // craft has been detected

                                    // has the craft not been seen in the last
                                    // minute, or is completely new?
                                    if (recentContactTracking.get(ccraft).get(tcraft) == null
                                            || System.currentTimeMillis()
                                                    - recentContactTracking.get(ccraft).get(tcraft) > 60000) {
                                        String notification = "New contact: ";
                                        notification += tcraft.getType().getCraftName();
                                        notification += " commanded by ";
                                        if (tcraft.getNotificationPlayer() != null) {
                                            notification += tcraft.getNotificationPlayer().getDisplayName();
                                        } else {
                                            notification += "NULL";
                                        }
                                        notification += ", size: ";
                                        notification += tcraft.getOrigBlockCount();
                                        notification += ", range: ";
                                        notification += (int) Math.sqrt(distsquared);
                                        notification += " to the";
                                        if (Math.abs(diffx) > Math.abs(diffz))
                                            if (diffx < 0)
                                                notification += " east.";
                                            else
                                                notification += " west.";
                                        else if (diffz < 0)
                                            notification += " south.";
                                        else
                                            notification += " north.";

                                        ccraft.getNotificationPlayer().sendMessage(notification);
                                        w.playSound(ccraft.getNotificationPlayer().getLocation(),
                                                Sound.BLOCK_ANVIL_LAND, 1.0f, 2.0f);
                                        /*                              final World sw = w; 
                                                                      final Player sp = ccraft.getNotificationPlayer();
                                                                      BukkitTask replaysound = new BukkitRunnable() {
                                                                         @Override
                                                                         public void run() {
                                                                            sw.playSound(sp.getLocation(), Sound.BLOCK_ANVIL_LAND, 10.0f, 2.0f);
                                                                         }
                                                                      }.runTaskLater(Movecraft.getInstance(), (5));*/

                                    }

                                    long timestamp = System.currentTimeMillis();
                                    recentContactTracking.get(ccraft).put(tcraft, timestamp);
                                }
                            }
                        }
                    }
                }
            }

            lastContactCheck = System.currentTimeMillis();
        }
    }

    public void processSiege() {
        if (Movecraft.getInstance().currentSiegeName != null) {
            long secsElapsed = (System.currentTimeMillis() - lastSiegeNotification) / 1000;
            if (secsElapsed > 180) {
                lastSiegeNotification = System.currentTimeMillis();
                boolean siegeLeaderPilotingShip = false;
                Player siegeLeader = Movecraft.getInstance().getServer()
                        .getPlayer(Movecraft.getInstance().currentSiegePlayer);
                if (siegeLeader != null) {
                    if (CraftManager.getInstance().getCraftByPlayer(siegeLeader) != null) {
                        for (String tTypeName : Settings.SiegeCraftsToWin
                                .get(Movecraft.getInstance().currentSiegeName)) {
                            if (tTypeName.equalsIgnoreCase(CraftManager.getInstance().getCraftByPlayer(siegeLeader)
                                    .getType().getCraftName())) {
                                siegeLeaderPilotingShip = true;
                            }
                        }
                    }
                }
                boolean siegeLeaderShipInRegion = false;
                int midX = 0;
                int midY = 0;
                int midZ = 0;
                if (siegeLeaderPilotingShip == true) {
                    midX = (CraftManager.getInstance().getCraftByPlayer(siegeLeader).getMaxX()
                            + CraftManager.getInstance().getCraftByPlayer(siegeLeader).getMinX()) / 2;
                    midY = (CraftManager.getInstance().getCraftByPlayer(siegeLeader).getMaxY()
                            + CraftManager.getInstance().getCraftByPlayer(siegeLeader).getMinY()) / 2;
                    midZ = (CraftManager.getInstance().getCraftByPlayer(siegeLeader).getMaxZ()
                            + CraftManager.getInstance().getCraftByPlayer(siegeLeader).getMinZ()) / 2;
                    if (Movecraft.getInstance().getWorldGuardPlugin().getRegionManager(siegeLeader.getWorld())
                            .getRegion(Settings.SiegeRegion.get(Movecraft.getInstance().currentSiegeName))
                            .contains(midX, midY, midZ)) {
                        siegeLeaderShipInRegion = true;
                    }
                }
                long timeLapsed = (System.currentTimeMillis() - Movecraft.getInstance().currentSiegeStartTime)
                        / 1000;
                int timeLeft = (int) (Settings.SiegeDuration.get(Movecraft.getInstance().currentSiegeName)
                        - timeLapsed);
                if (timeLeft > 10) {
                    if (siegeLeaderShipInRegion == true) {
                        Bukkit.getServer().broadcastMessage(String.format(
                                "The Siege of %s is under way. The Siege Flagship is a %s of size %d under the command of %s at %d, %d, %d. Siege will end in %d minutes",
                                Movecraft.getInstance().currentSiegeName,
                                CraftManager.getInstance().getCraftByPlayer(siegeLeader).getType().getCraftName(),
                                CraftManager.getInstance().getCraftByPlayer(siegeLeader).getOrigBlockCount(),
                                siegeLeader.getDisplayName(), midX, midY, midZ, timeLeft / 60));
                    } else {
                        Bukkit.getServer().broadcastMessage(String.format(
                                "The Siege of %s is under way. The Siege Leader, %s, is not in command of a Flagship within the Siege Region! If they are still not when the duration expires, the siege will fail! Siege will end in %d minutes",
                                Movecraft.getInstance().currentSiegeName, siegeLeader.getDisplayName(),
                                timeLeft / 60));
                    }
                } else {
                    if (siegeLeaderShipInRegion == true) {
                        Bukkit.getServer()
                                .broadcastMessage(String.format(
                                        "The Siege of %s has succeeded! The forces of %s have been victorious!",
                                        Movecraft.getInstance().currentSiegeName, siegeLeader.getDisplayName()));
                        ProtectedRegion controlRegion = Movecraft.getInstance().getWorldGuardPlugin()
                                .getRegionManager(siegeLeader.getWorld()).getRegion(
                                        Settings.SiegeControlRegion.get(Movecraft.getInstance().currentSiegeName));
                        DefaultDomain newOwner = new DefaultDomain();
                        newOwner.addPlayer(Movecraft.getInstance().currentSiegePlayer);
                        controlRegion.setOwners(newOwner);
                        DefaultDomain newMember = new DefaultDomain();
                        newOwner.addPlayer(Movecraft.getInstance().currentSiegePlayer);
                        controlRegion.setMembers(newMember);
                        if (Settings.SiegeCommandsOnWin.get(Movecraft.getInstance().currentSiegeName) != null)
                            for (String command : Settings.SiegeCommandsOnWin
                                    .get(Movecraft.getInstance().currentSiegeName)) {
                                command = command
                                        .replaceAll("%r",
                                                Settings.SiegeRegion.get(Movecraft.getInstance().currentSiegeName))
                                        .replaceAll("%c", Settings.SiegeCost
                                                .get(Movecraft.getInstance().currentSiegeName).toString())
                                        .replaceAll("%w", siegeLeader.getName());
                                Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), command);
                            }
                    } else {
                        Bukkit.getServer()
                                .broadcastMessage(String.format(
                                        "The Siege of %s has failed! The forces of %s have been crushed!",
                                        Movecraft.getInstance().currentSiegeName, siegeLeader.getDisplayName()));
                        if (Settings.SiegeCommandsOnLose.get(Movecraft.getInstance().currentSiegeName) != null)
                            for (String command : Settings.SiegeCommandsOnLose
                                    .get(Movecraft.getInstance().currentSiegeName)) {
                                command = command
                                        .replaceAll("%r",
                                                Settings.SiegeRegion.get(Movecraft.getInstance().currentSiegeName))
                                        .replaceAll("%c", Settings.SiegeCost
                                                .get(Movecraft.getInstance().currentSiegeName).toString())
                                        .replaceAll("%l", siegeLeader.getName());
                                Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), command);
                            }
                    }
                    Movecraft.getInstance().currentSiegeName = null;
                    Movecraft.getInstance().siegeInProgress = false;
                }
            }
        }
        if (Settings.SiegeName != null) {
            // and now process payments every morning at 1:01 AM, as long as it has
            // been 23 hours after the last payout
            long secsElapsed = (System.currentTimeMillis() - lastSiegePayout) / 1000;
            if (secsElapsed > 23 * 60 * 60) {
                Calendar rightNow = Calendar.getInstance();
                int hour = rightNow.get(Calendar.HOUR_OF_DAY);
                int minute = rightNow.get(Calendar.MINUTE);
                if ((hour == 1) && (minute == 1)) {
                    lastSiegePayout = System.currentTimeMillis();
                    for (String tSiegeName : Settings.SiegeName) {
                        int payment = Settings.SiegeIncome.get(tSiegeName);
                        for (World tW : Movecraft.getInstance().getServer().getWorlds()) {
                            ProtectedRegion tRegion = Movecraft.getInstance().getWorldGuardPlugin()
                                    .getRegionManager(tW).getRegion(Settings.SiegeControlRegion.get(tSiegeName));
                            if (tRegion != null) {
                                int totalSize = tRegion.getOwners().getPlayers().size()
                                        + tRegion.getOwners().getUniqueIds().size();
                                for (String tPlayerName : tRegion.getOwners().getPlayers()) {
                                    int share = payment / totalSize;
                                    Movecraft.getInstance().getEconomy().depositPlayer(tPlayerName, share);
                                    Movecraft.getInstance().getLogger().log(Level.INFO,
                                            String.format("Player %s paid %d for their ownership in %s",
                                                    tPlayerName, share, tSiegeName));
                                }
                                for (UUID tPlayerUUID : tRegion.getOwners().getUniqueIds()) {
                                    int share = payment / totalSize;
                                    Movecraft.getInstance().getEconomy()
                                            .depositPlayer(Bukkit.getPlayer(tPlayerUUID), share);
                                    Movecraft.getInstance().getLogger().log(Level.INFO,
                                            String.format("Player UUID %s paid %d for their ownership in %s",
                                                    tPlayerUUID.toString(), share, tSiegeName));
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public void processAssault() {
        long ticksElapsed = (System.currentTimeMillis() - lastAssaultCheck) / 50;
        if (ticksElapsed > 19) {
            Iterator assaultI = Movecraft.getInstance().assaultsRunning.iterator();
            while (assaultI.hasNext()) {
                String assault = (String) assaultI.next();
                String assaultStarter = Movecraft.getInstance().assaultStarter.get(assault);
                World w = Movecraft.getInstance().assaultWorlds.get(assault);
                if (Movecraft.getInstance().assaultDamages.get(assault) >= Movecraft.getInstance().assaultMaxDamages
                        .get(assault)) {
                    // assault was successful 
                    Bukkit.getServer()
                            .broadcastMessage(String.format("The assault of %s was successful!", assault));
                    ProtectedRegion tRegion = Movecraft.getInstance().getWorldGuardPlugin().getRegionManager(w)
                            .getRegion(assault);
                    tRegion.setFlag(DefaultFlag.TNT, State.DENY);

                    //first, find a position for the repair beacon
                    int beaconX = Movecraft.getInstance().assaultDamagablePartMin.get(assault).getBlockX();
                    int beaconZ = Movecraft.getInstance().assaultDamagablePartMin.get(assault).getBlockZ();
                    int beaconY = w.getHighestBlockAt(beaconX, beaconZ).getY();
                    int x, y, z;
                    for (x = beaconX; x < beaconX + 5; x++)
                        for (z = beaconZ; z < beaconZ + 5; z++)
                            if (!w.isChunkLoaded(x >> 4, z >> 4))
                                w.loadChunk(x >> 4, z >> 4);
                    boolean empty = false;
                    while (!empty && beaconY < 250) {
                        empty = true;
                        beaconY++;
                        for (x = beaconX; x < beaconX + 5; x++) {
                            for (y = beaconY; y < beaconY + 4; y++) {
                                for (z = beaconZ; z < beaconZ + 5; z++) {
                                    if (!w.getBlockAt(x, y, z).isEmpty())
                                        empty = false;
                                }
                            }
                        }
                    }

                    //now make the beacon
                    y = beaconY;
                    for (x = beaconX + 1; x < beaconX + 4; x++)
                        for (z = beaconZ + 1; z < beaconZ + 4; z++)
                            w.getBlockAt(x, y, z).setType(Material.BEDROCK);
                    y = beaconY + 1;
                    for (x = beaconX; x < beaconX + 5; x++)
                        for (z = beaconZ; z < beaconZ + 5; z++)
                            if (x == beaconX || z == beaconZ || x == beaconX + 4 || z == beaconZ + 4)
                                w.getBlockAt(x, y, z).setType(Material.BEDROCK);
                            else
                                w.getBlockAt(x, y, z).setType(Material.IRON_BLOCK);
                    y = beaconY + 2;
                    for (x = beaconX + 1; x < beaconX + 4; x++)
                        for (z = beaconZ + 1; z < beaconZ + 4; z++)
                            w.getBlockAt(x, y, z).setType(Material.BEDROCK);
                    w.getBlockAt(beaconX + 2, beaconY + 2, beaconZ + 2).setType(Material.BEACON);
                    w.getBlockAt(beaconX + 2, beaconY + 3, beaconZ + 2).setType(Material.BEDROCK);
                    // finally the sign on the beacon
                    w.getBlockAt(beaconX + 2, beaconY + 3, beaconZ + 1).setType(Material.WALL_SIGN);
                    Sign s = (Sign) w.getBlockAt(beaconX + 2, beaconY + 3, beaconZ + 1).getState();
                    s.setLine(0, ChatColor.RED + "REGION DAMAGED!");
                    CommandListener executor = new CommandListener();
                    s.setLine(1, "Region:" + assault);
                    s.setLine(2, "Damage:" + Movecraft.getInstance().assaultDamages.get(assault));
                    Calendar rightNow = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
                    s.setLine(3, "Owner:" + executor.getRegionOwnerList(tRegion));
                    ((CraftBlockState) s).update(false, false);
                    ProtectedRegion aRegion = Movecraft.getInstance().getWorldGuardPlugin().getRegionManager(w)
                            .getRegion(assault);
                    tRegion.getOwners().clear();
                    assaultI.remove();
                } else {
                    // assault was not successful
                    long elapsed = System.currentTimeMillis()
                            - Movecraft.getInstance().assaultStartTime.get(assault);
                    if (elapsed > Settings.AssaultDuration * 1000) {
                        // assault has failed to reach damage cap within required time
                        Bukkit.getServer()
                                .broadcastMessage(String.format("The assault of %s has failed!", assault));
                        ProtectedRegion tRegion = Movecraft.getInstance().getWorldGuardPlugin().getRegionManager(w)
                                .getRegion(assault);
                        tRegion.setFlag(DefaultFlag.TNT, State.DENY);
                        // repair the damages that have occurred so far
                        WorldEditInteractListener executor = new WorldEditInteractListener();
                        if (executor.repairRegion(w, assault) == false) {
                            Bukkit.getServer().broadcastMessage(
                                    String.format("REPAIR OF %s FAILED, CONTACT AN ADMIN", assault));
                        }
                        assaultI.remove();
                    }
                }

            }
            lastAssaultCheck = System.currentTimeMillis();
        }

    }

    private void processScheduledBlockChanges() {
        for (World w : Bukkit.getWorlds()) {
            if (w != null && CraftManager.getInstance().getCraftsInWorld(w) != null) {
                ArrayList<MapUpdateCommand> updateCommands = new ArrayList<MapUpdateCommand>();
                for (Craft pcraft : CraftManager.getInstance().getCraftsInWorld(w)) {
                    HashMap<MapUpdateCommand, Long> scheduledBlockChanges = pcraft.getScheduledBlockChanges();
                    if (scheduledBlockChanges != null) {
                        Iterator<MapUpdateCommand> mucI = scheduledBlockChanges.keySet().iterator();
                        boolean madeChanges = false;
                        while (mucI.hasNext()) {
                            MapUpdateCommand muc = mucI.next();
                            if ((pcraft.getScheduledBlockChanges().get(muc) < System.currentTimeMillis())
                                    && (pcraft.isNotProcessing())) {
                                int cx = muc.getNewBlockLocation().getX() >> 4;
                                int cz = muc.getNewBlockLocation().getZ() >> 4;
                                if (w.isChunkLoaded(cx, cz) == false) {
                                    w.loadChunk(cx, cz);
                                }
                                if (w.getBlockAt(muc.getNewBlockLocation().getX(), muc.getNewBlockLocation().getY(),
                                        muc.getNewBlockLocation().getZ()).getTypeId() == muc.getTypeID()) {
                                    // if the block you will be updating later has changed type, something went horribly wrong: it burned away, was flooded, or was destroyed. Don't update it
                                    updateCommands.add(muc);
                                }
                                mucI.remove();
                                madeChanges = true;
                            }
                        }
                        if (madeChanges) {
                            pcraft.setScheduledBlockChanges(scheduledBlockChanges);
                        }
                    }
                }
                if (updateCommands.size() > 0) {
                    MapUpdateManager.getInstance().addWorldUpdate(w,
                            updateCommands.toArray(new MapUpdateCommand[1]), null, null);
                }
            }
        }
    }

    public void run() {
        clearAll();
        if (transparent == null) {
            transparent = new HashSet<Material>();
            transparent.add(Material.AIR);
            transparent.add(Material.GLASS);
            transparent.add(Material.THIN_GLASS);
            transparent.add(Material.STAINED_GLASS);
            transparent.add(Material.STAINED_GLASS_PANE);
            transparent.add(Material.IRON_FENCE);
            transparent.add(Material.REDSTONE_WIRE);
            transparent.add(Material.IRON_TRAPDOOR);
            transparent.add(Material.TRAP_DOOR);
            transparent.add(Material.NETHER_BRICK_STAIRS);
            transparent.add(Material.LEVER);
            transparent.add(Material.STONE_BUTTON);
            transparent.add(Material.WOOD_BUTTON);
            transparent.add(Material.STEP);
            transparent.add(Material.SMOOTH_STAIRS);
            transparent.add(Material.SIGN);
            transparent.add(Material.SIGN_POST);
            transparent.add(Material.WALL_SIGN);
        }

        processCruise();
        processSinking();
        processTracers();
        processFireballs();
        processTNTContactExplosives();
        processFadingBlocks();
        processDetection();
        processSiege();
        processAssault();
        processAlgorithmQueue();
        processScheduledBlockChanges();
        //      if(Settings.CompatibilityMode==false)
        //         FastBlockChanger.getInstance().run(); 

        // now cleanup craft that are bugged and have not moved in the past 60 seconds, but have no pilot or are still processing
        for (World w : Bukkit.getWorlds()) {
            if (w != null && CraftManager.getInstance().getCraftsInWorld(w) != null) {
                for (Craft pcraft : CraftManager.getInstance().getCraftsInWorld(w)) {
                    if (CraftManager.getInstance().getPlayerFromCraft(pcraft) == null) {
                        if (pcraft.getLastCruiseUpdate() < System.currentTimeMillis() - 60000) {
                            CraftManager.getInstance().forceRemoveCraft(pcraft);
                        }
                    }
                    if (!pcraft.isNotProcessing()) {
                        if (pcraft.getCruising()) {
                            if (pcraft.getLastCruiseUpdate() < System.currentTimeMillis() - 5000) {
                                pcraft.setProcessing(false);
                            }
                        }
                    }
                }
            }
        }
    }

    private void clear(Craft c) {
        clearanceSet.add(c);
    }

    private void clearAll() {
        for (Craft c : clearanceSet) {
            c.setProcessing(false);
        }

        clearanceSet.clear();
    }
}