Search in sources :

Example 1 with ScheduleArguments

use of com.bergerkiller.bukkit.lightcleaner.lighting.LightingService.ScheduleArguments in project monumenta-structure-management by TeamMonumenta.

the class StructuresAPI method pasteStructure.

/**
 * Pastes a structure at the given location, ignoring structure void similarly to vanilla structure blocks.
 *
 * Must be called from main thread, will return immediately and do its work on an async thread
 */
public static CompletableFuture<Void> pasteStructure(@Nonnull BlockArrayClipboard clipboard, @Nonnull Location loc, boolean includeEntities) {
    /*
		 * This is the future given to the caller - when it's completed it should be safe to interact with the pasted area
		 * In particular there is a 6s delay after pasting before this is marked as completed
		 */
    CompletableFuture<Void> future = new CompletableFuture<>();
    Plugin plugin = StructuresPlugin.getInstance();
    if (plugin == null) {
        future.completeExceptionally(new Exception("MonumentaStructureManagement plugin isn't loaded"));
        return future;
    }
    /* Clone the input variable to make sure the caller doesn't change it while we're stil loading */
    Location pasteLoc = loc.clone();
    /*
		 * This future is used to signal the pasting system that this schematic has completed and the next one can start pasting
		 * This does not have the 6s delay returned to the user
		 */
    CompletableFuture<Void> signal = new CompletableFuture<>();
    PENDING_TASKS.add(new PendingTask(signal, () -> {
        // <-- START
        final long initialTime = System.currentTimeMillis();
        final Region sourceRegion = clipboard.getRegion();
        final BlockVector3 size = sourceRegion.getMaximumPoint().subtract(sourceRegion.getMinimumPoint());
        final org.bukkit.World world = pasteLoc.getWorld();
        final BlockVector3 to = BlockVector3.at(pasteLoc.getBlockX(), pasteLoc.getBlockY(), pasteLoc.getBlockZ());
        final Vector pos1 = new Vector((double) to.getX(), (double) to.getY(), (double) to.getZ());
        final Vector pos2 = pos1.clone().add(new Vector(size.getX() + 1, size.getY() + 1, size.getZ() + 1));
        final BoundingBox box = new BoundingBox(pos1.getX(), pos1.getY(), pos1.getZ(), pos2.getX(), pos2.getY(), pos2.getZ());
        /*
			 * Clipboards seem to be offset at their original save location now, rather than at 0 0 0
			 * This offset can be added to a relative position to get the correct location within the clipboard
			 */
        final Region shiftedRegion = clipboard.getRegion().clone();
        final BlockVector3 clipboardAddOffset = shiftedRegion.getMinimumPoint();
        final Vector clipboardAddOffsetVec = new Vector(clipboardAddOffset.getX(), clipboardAddOffset.getY(), clipboardAddOffset.getZ());
        shiftedRegion.shift(clipboardAddOffset.multiply(-1, -1, -1));
        shiftedRegion.shift(to);
        /* Set of positions (relative to the clipboard / origin) that should not be overwritten when pasting */
        final Set<Long> noLoadPositions = new HashSet<>();
        /* This chunk consumer removes entities and sets spawners/brewstands/furnaces to air */
        final Consumer<Chunk> chunkConsumer = (final Chunk chunk) -> {
            for (final BlockState state : chunk.getTileEntities(true)) {
                if (state instanceof CreatureSpawner || state instanceof BrewingStand || state instanceof Furnace || state instanceof Chest || state instanceof ShulkerBox) {
                    final Location sLoc = state.getLocation();
                    final BlockVector3 relPos = BlockVector3.at(sLoc.getBlockX(), sLoc.getBlockY(), sLoc.getBlockZ()).subtract(to).add(clipboardAddOffset);
                    if (box.contains(sLoc.toVector()) && !clipboard.getBlock(relPos).getBlockType().equals(BlockTypes.STRUCTURE_VOID)) {
                        if (state instanceof CreatureSpawner || state instanceof BrewingStand || state instanceof Furnace) {
                            // TODO: Work around a bug in FAWE that corrupts these blocks if they're not removed first
                            final Block block = state.getBlock();
                            /* Set block to air and then dirt... which works around somehow the tile entity data being left behind */
                            if (state instanceof BrewingStand || state instanceof Furnace) {
                                Inventory inv;
                                if (state instanceof BrewingStand) {
                                    inv = ((BrewingStand) state).getInventory();
                                } else {
                                    inv = ((Furnace) state).getInventory();
                                }
                                for (int i = 0; i < inv.getSize(); i++) {
                                    inv.setItem(i, new ItemStack(Material.AIR));
                                }
                            }
                            block.setType(Material.AIR);
                            block.setType(Material.DIRT);
                        } else if (state instanceof ShulkerBox) {
                            /* Never overwrite shulker boxes */
                            final int relx = state.getX() - to.getX();
                            final int rely = state.getY() - to.getY();
                            final int relz = state.getZ() - to.getZ();
                            noLoadPositions.add(compressToLong(relx, rely, relz));
                        }
                    }
                }
            }
            if (includeEntities) {
                for (final Entity entity : chunk.getEntities()) {
                    if (box.contains(entity.getLocation().toVector()) && entityShouldBeRemoved(entity)) {
                        final Vector relPos = entity.getLocation().toVector().subtract(pos1).add(clipboardAddOffsetVec);
                        if (!clipboard.getBlock(BlockVector3.at(relPos.getBlockX(), relPos.getBlockY(), relPos.getBlockZ())).getBlockType().equals(BlockTypes.STRUCTURE_VOID)) {
                            entity.remove();
                        }
                    }
                }
            }
        };
        markAndLoadChunks(world, shiftedRegion, chunkConsumer).whenComplete((unused, ex) -> {
            if (ex != null) {
                signal.completeExceptionally(ex);
                future.completeExceptionally(ex);
            } else {
                /* Actually load the structure asynchronously now that all the chunks have been processed for entities / blocks that shouldn't be replaced */
                Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
                    // STOP -->
                    MSLog.finer(() -> "Initial processing took " + Long.toString(System.currentTimeMillis() - initialTime) + " milliseconds (mostly async)");
                    // <-- START
                    final long pasteTime = System.currentTimeMillis();
                    try (EditSession extent = WorldEdit.getInstance().newEditSessionBuilder().world(new BukkitWorld(world)).fastMode(true).combineStages(true).changeSetNull().checkMemory(false).allowedRegionsEverywhere().limitUnlimited().build()) {
                        /*
							 * Filter function to skip some blocks from overwriting what exists in the world
							 * If this function returns true, this location will be overwritten
							 */
                        final RegionFunction filterFunction = position -> {
                            final BlockType newBlockType = clipboard.getBlock(position).getBlockType();
                            if (newBlockType == null || !newBlockType.equals(BlockTypes.STRUCTURE_VOID)) {
                                // This position is not in structure void in the clipboard
                                if (!noLoadPositions.contains(compressToLong(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) {
                                    // This position is not in the list of blocks that should not be overwritten
                                    return true;
                                }
                            }
                            // Don't overwrite by default
                            return false;
                        };
                        final ForwardExtentCopy copy = new ForwardExtentCopy(clipboard, clipboard.getRegion(), clipboard.getOrigin(), extent, to);
                        copy.setCopyingBiomes(false);
                        copy.setFilterFunction(filterFunction);
                        copy.setCopyingEntities(includeEntities);
                        Operations.completeBlindly(copy);
                    }
                    // STOP -->
                    MSLog.finer(() -> "Loading structure took " + Long.toString(System.currentTimeMillis() - pasteTime) + " milliseconds (async)");
                    /* Allow the next structure load task to start at this point */
                    signal.complete(null);
                    /* 6s later, signal caller that loading is complete */
                    Bukkit.getScheduler().runTaskLater(plugin, () -> {
                        future.complete(null);
                    }, 120);
                    /* Schedule light cleaning on the main thread so it can safely check plugin enabled status */
                    Bukkit.getScheduler().runTask(plugin, () -> {
                        if (!Bukkit.getPluginManager().isPluginEnabled("LightCleaner")) {
                            return;
                        }
                        // <-- START
                        final long lightTime = System.currentTimeMillis();
                        /* Relight an area 16 blocks bigger than the respawned area */
                        final Set<BlockVector2> lightingChunks = new CuboidRegion(to.subtract(16, 16, 16), to.add(size).add(16, 16, 16)).getChunks();
                        final LongHashSet lightCleanerChunks = new LongHashSet(lightingChunks.size());
                        for (final BlockVector2 chunk : lightingChunks) {
                            lightCleanerChunks.add(chunk.getX(), chunk.getZ());
                        }
                        ScheduleArguments args = new ScheduleArguments();
                        args.setWorld(world);
                        args.setChunks(lightCleanerChunks);
                        args.setLoadedChunksOnly(true);
                        LightingService.schedule(args);
                        // STOP -->
                        MSLog.finer(() -> "scheduleLighting took " + Long.toString(System.currentTimeMillis() - lightTime) + " milliseconds (main thread)");
                        /* 10s later, unmark all chunks as force loaded */
                        Bukkit.getScheduler().runTaskLater(plugin, () -> {
                            unmarkChunksAsync(world, shiftedRegion);
                        }, 200);
                    });
                });
            }
        });
    }));
    ensureTask(plugin);
    return future;
}
Also used : Plugin(org.bukkit.plugin.Plugin) EditSession(com.sk89q.worldedit.EditSession) BlockVector2(com.sk89q.worldedit.math.BlockVector2) BlockTypes(com.sk89q.worldedit.world.block.BlockTypes) BlockVector3(com.sk89q.worldedit.math.BlockVector3) CuboidRegion(com.sk89q.worldedit.regions.CuboidRegion) World(com.sk89q.worldedit.world.World) Inventory(org.bukkit.inventory.Inventory) ScheduleArguments(com.bergerkiller.bukkit.lightcleaner.lighting.LightingService.ScheduleArguments) LightingService(com.bergerkiller.bukkit.lightcleaner.lighting.LightingService) Block(org.bukkit.block.Block) ForwardExtentCopy(com.sk89q.worldedit.function.operation.ForwardExtentCopy) Location(org.bukkit.Location) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Chunk(org.bukkit.Chunk) Operations(com.sk89q.worldedit.function.operation.Operations) DiskOptimizedClipboard(com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard) EnumSet(java.util.EnumSet) Material(org.bukkit.Material) Bukkit(org.bukkit.Bukkit) Clipboard(com.sk89q.worldedit.extent.clipboard.Clipboard) BlockType(com.sk89q.worldedit.world.block.BlockType) Entity(org.bukkit.entity.Entity) Set(java.util.Set) UUID(java.util.UUID) EntityType(org.bukkit.entity.EntityType) BukkitWorld(com.sk89q.worldedit.bukkit.BukkitWorld) ItemStack(org.bukkit.inventory.ItemStack) ClipboardFormat(com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat) BlockArrayClipboard(com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard) Closer(com.sk89q.worldedit.util.io.Closer) MSLog(com.playmonumenta.structures.utils.MSLog) CommandUtils(com.playmonumenta.structures.utils.CommandUtils) RegionFunction(com.sk89q.worldedit.function.RegionFunction) BoundingBox(org.bukkit.util.BoundingBox) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) ShulkerBox(org.bukkit.block.ShulkerBox) LongHashSet(com.bergerkiller.bukkit.common.wrappers.LongHashSet) Deque(java.util.Deque) BufferedOutputStream(java.io.BufferedOutputStream) HashSet(java.util.HashSet) Furnace(org.bukkit.block.Furnace) Chest(org.bukkit.block.Chest) Nonnull(javax.annotation.Nonnull) WorldEdit(com.sk89q.worldedit.WorldEdit) Region(com.sk89q.worldedit.regions.Region) CreatureSpawner(org.bukkit.block.CreatureSpawner) BukkitRunnable(org.bukkit.scheduler.BukkitRunnable) FileOutputStream(java.io.FileOutputStream) BlockState(org.bukkit.block.BlockState) File(java.io.File) ConcurrentLinkedDeque(java.util.concurrent.ConcurrentLinkedDeque) ClipboardWriter(com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter) TimeUnit(java.util.concurrent.TimeUnit) Consumer(java.util.function.Consumer) Vector(org.bukkit.util.Vector) ArmorStand(org.bukkit.entity.ArmorStand) ClipboardFormats(com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats) Tameable(org.bukkit.entity.Tameable) BrewingStand(org.bukkit.block.BrewingStand) Chest(org.bukkit.block.Chest) Entity(org.bukkit.entity.Entity) EnumSet(java.util.EnumSet) Set(java.util.Set) LongHashSet(com.bergerkiller.bukkit.common.wrappers.LongHashSet) HashSet(java.util.HashSet) CuboidRegion(com.sk89q.worldedit.regions.CuboidRegion) World(com.sk89q.worldedit.world.World) BukkitWorld(com.sk89q.worldedit.bukkit.BukkitWorld) ScheduleArguments(com.bergerkiller.bukkit.lightcleaner.lighting.LightingService.ScheduleArguments) CreatureSpawner(org.bukkit.block.CreatureSpawner) ForwardExtentCopy(com.sk89q.worldedit.function.operation.ForwardExtentCopy) CompletableFuture(java.util.concurrent.CompletableFuture) Consumer(java.util.function.Consumer) BoundingBox(org.bukkit.util.BoundingBox) Vector(org.bukkit.util.Vector) ShulkerBox(org.bukkit.block.ShulkerBox) RegionFunction(com.sk89q.worldedit.function.RegionFunction) BukkitWorld(com.sk89q.worldedit.bukkit.BukkitWorld) BlockVector3(com.sk89q.worldedit.math.BlockVector3) Chunk(org.bukkit.Chunk) Furnace(org.bukkit.block.Furnace) BlockVector2(com.sk89q.worldedit.math.BlockVector2) LongHashSet(com.bergerkiller.bukkit.common.wrappers.LongHashSet) BlockState(org.bukkit.block.BlockState) BlockType(com.sk89q.worldedit.world.block.BlockType) BrewingStand(org.bukkit.block.BrewingStand) CuboidRegion(com.sk89q.worldedit.regions.CuboidRegion) Region(com.sk89q.worldedit.regions.Region) Block(org.bukkit.block.Block) EditSession(com.sk89q.worldedit.EditSession) ItemStack(org.bukkit.inventory.ItemStack) Inventory(org.bukkit.inventory.Inventory) Plugin(org.bukkit.plugin.Plugin) Location(org.bukkit.Location)

Aggregations

LongHashSet (com.bergerkiller.bukkit.common.wrappers.LongHashSet)1 LightingService (com.bergerkiller.bukkit.lightcleaner.lighting.LightingService)1 ScheduleArguments (com.bergerkiller.bukkit.lightcleaner.lighting.LightingService.ScheduleArguments)1 DiskOptimizedClipboard (com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard)1 CommandUtils (com.playmonumenta.structures.utils.CommandUtils)1 MSLog (com.playmonumenta.structures.utils.MSLog)1 EditSession (com.sk89q.worldedit.EditSession)1 WorldEdit (com.sk89q.worldedit.WorldEdit)1 BukkitWorld (com.sk89q.worldedit.bukkit.BukkitWorld)1 BlockArrayClipboard (com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard)1 Clipboard (com.sk89q.worldedit.extent.clipboard.Clipboard)1 ClipboardFormat (com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat)1 ClipboardFormats (com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats)1 ClipboardWriter (com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter)1 RegionFunction (com.sk89q.worldedit.function.RegionFunction)1 ForwardExtentCopy (com.sk89q.worldedit.function.operation.ForwardExtentCopy)1 Operations (com.sk89q.worldedit.function.operation.Operations)1 BlockVector2 (com.sk89q.worldedit.math.BlockVector2)1 BlockVector3 (com.sk89q.worldedit.math.BlockVector3)1 CuboidRegion (com.sk89q.worldedit.regions.CuboidRegion)1