Search in sources :

Example 66 with BlockType

use of com.sk89q.worldedit.world.block.BlockType 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

BlockType (com.sk89q.worldedit.world.block.BlockType)66 BlockState (com.sk89q.worldedit.world.block.BlockState)20 BlockVector3 (com.sk89q.worldedit.math.BlockVector3)19 Map (java.util.Map)12 HashMap (java.util.HashMap)10 BaseBlock (com.sk89q.worldedit.world.block.BaseBlock)9 World (com.sk89q.worldedit.world.World)8 ArrayList (java.util.ArrayList)8 TextureUtil (com.fastasyncworldedit.core.util.TextureUtil)7 List (java.util.List)7 EditSession (com.sk89q.worldedit.EditSession)6 IOException (java.io.IOException)6 Set (java.util.Set)6 CompoundTag (com.sk89q.jnbt.CompoundTag)5 Tag (com.sk89q.jnbt.Tag)5 Region (com.sk89q.worldedit.regions.Region)5 Property (com.sk89q.worldedit.registry.state.Property)5 Direction (com.sk89q.worldedit.util.Direction)5 Locale (java.util.Locale)5 MutableBlockVector3 (com.fastasyncworldedit.core.math.MutableBlockVector3)4