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;
}
Aggregations