/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java
Java | 1327 lines | 644 code | 136 blank | 547 comment | 37 complexity | ce6a6d7c6c9dc69b404229b496fa5289 MD5 | raw file
- /*
- * WorldEdit, a Minecraft world manipulation toolkit
- * Copyright (C) sk89q <http://www.sk89q.com>
- * Copyright (C) WorldEdit team and contributors
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- package com.sk89q.worldedit;
- import com.sk89q.worldedit.blocks.BaseBlock;
- import com.sk89q.worldedit.blocks.BlockID;
- import com.sk89q.worldedit.blocks.BlockType;
- import com.sk89q.worldedit.entity.BaseEntity;
- import com.sk89q.worldedit.entity.Entity;
- import com.sk89q.worldedit.event.extent.EditSessionEvent;
- import com.sk89q.worldedit.extent.ChangeSetExtent;
- import com.sk89q.worldedit.extent.Extent;
- import com.sk89q.worldedit.extent.MaskingExtent;
- import com.sk89q.worldedit.extent.NullExtent;
- import com.sk89q.worldedit.extent.buffer.ForgetfulExtentBuffer;
- import com.sk89q.worldedit.extent.cache.LastAccessExtentCache;
- import com.sk89q.worldedit.extent.inventory.BlockBag;
- import com.sk89q.worldedit.extent.inventory.BlockBagExtent;
- import com.sk89q.worldedit.extent.reorder.MultiStageReorder;
- import com.sk89q.worldedit.extent.validation.BlockChangeLimiter;
- import com.sk89q.worldedit.extent.validation.DataValidatorExtent;
- import com.sk89q.worldedit.extent.world.BlockQuirkExtent;
- import com.sk89q.worldedit.extent.world.ChunkLoadingExtent;
- import com.sk89q.worldedit.extent.world.FastModeExtent;
- import com.sk89q.worldedit.extent.world.SurvivalModeExtent;
- import com.sk89q.worldedit.function.GroundFunction;
- import com.sk89q.worldedit.function.RegionMaskingFilter;
- import com.sk89q.worldedit.function.block.BlockReplace;
- import com.sk89q.worldedit.function.block.Counter;
- import com.sk89q.worldedit.function.block.Naturalizer;
- import com.sk89q.worldedit.function.generator.GardenPatchGenerator;
- import com.sk89q.worldedit.function.mask.*;
- import com.sk89q.worldedit.function.operation.*;
- import com.sk89q.worldedit.function.pattern.BlockPattern;
- import com.sk89q.worldedit.function.pattern.Patterns;
- import com.sk89q.worldedit.function.util.RegionOffset;
- import com.sk89q.worldedit.function.visitor.*;
- import com.sk89q.worldedit.history.UndoContext;
- import com.sk89q.worldedit.history.change.BlockChange;
- import com.sk89q.worldedit.history.changeset.BlockOptimizedHistory;
- import com.sk89q.worldedit.history.changeset.ChangeSet;
- import com.sk89q.worldedit.internal.expression.Expression;
- import com.sk89q.worldedit.internal.expression.ExpressionException;
- import com.sk89q.worldedit.internal.expression.runtime.RValue;
- import com.sk89q.worldedit.math.interpolation.Interpolation;
- import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
- import com.sk89q.worldedit.math.interpolation.Node;
- import com.sk89q.worldedit.math.noise.RandomNoise;
- import com.sk89q.worldedit.math.transform.AffineTransform;
- import com.sk89q.worldedit.patterns.Pattern;
- import com.sk89q.worldedit.patterns.SingleBlockPattern;
- import com.sk89q.worldedit.regions.*;
- import com.sk89q.worldedit.regions.shape.ArbitraryBiomeShape;
- import com.sk89q.worldedit.regions.shape.ArbitraryShape;
- import com.sk89q.worldedit.regions.shape.RegionShape;
- import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
- import com.sk89q.worldedit.util.*;
- import com.sk89q.worldedit.util.collection.DoubleArrayList;
- import com.sk89q.worldedit.util.eventbus.EventBus;
- import com.sk89q.worldedit.world.NullWorld;
- import com.sk89q.worldedit.world.World;
- import com.sk89q.worldedit.world.biome.BaseBiome;
- import javax.annotation.Nullable;
- import java.util.*;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import static com.google.common.base.Preconditions.checkArgument;
- import static com.google.common.base.Preconditions.checkNotNull;
- import static com.sk89q.worldedit.regions.Regions.*;
- /**
- * An {@link Extent} that handles history, {@link BlockBag}s, change limits,
- * block re-ordering, and much more. Most operations in WorldEdit use this class.
- *
- * <p>Most of the actual functionality is implemented with a number of other
- * {@link Extent}s that are chained together. For example, history is logged
- * using the {@link ChangeSetExtent}.</p>
- */
- @SuppressWarnings({"FieldCanBeLocal", "deprecation"})
- public class EditSession implements Extent {
- private static final Logger log = Logger.getLogger(EditSession.class.getCanonicalName());
- /**
- * Used by {@link #setBlock(Vector, BaseBlock, Stage)} to
- * determine which {@link Extent}s should be bypassed.
- */
- public enum Stage {
- BEFORE_HISTORY,
- BEFORE_REORDER,
- BEFORE_CHANGE
- }
- @SuppressWarnings("ProtectedField")
- protected final World world;
- private final ChangeSet changeSet = new BlockOptimizedHistory();
- private @Nullable FastModeExtent fastModeExtent;
- private final SurvivalModeExtent survivalExtent;
- private @Nullable ChunkLoadingExtent chunkLoadingExtent;
- private @Nullable LastAccessExtentCache cacheExtent;
- private @Nullable BlockQuirkExtent quirkExtent;
- private @Nullable DataValidatorExtent validator;
- private final BlockBagExtent blockBagExtent;
- private final MultiStageReorder reorderExtent;
- private @Nullable ChangeSetExtent changeSetExtent;
- private final MaskingExtent maskingExtent;
- private final BlockChangeLimiter changeLimiter;
- private final Extent bypassReorderHistory;
- private final Extent bypassHistory;
- private final Extent bypassNone;
- @SuppressWarnings("deprecation")
- private Mask oldMask;
- /**
- * Create a new instance.
- *
- * @param world a world
- * @param maxBlocks the maximum number of blocks that can be changed, or -1 to use no limit
- * @deprecated use {@link WorldEdit#getEditSessionFactory()} to create {@link EditSession}s
- */
- @SuppressWarnings("deprecation")
- @Deprecated
- public EditSession(LocalWorld world, int maxBlocks) {
- this(world, maxBlocks, null);
- }
- /**
- * Create a new instance.
- *
- * @param world a world
- * @param maxBlocks the maximum number of blocks that can be changed, or -1 to use no limit
- * @param blockBag the block bag to set, or null to use none
- * @deprecated use {@link WorldEdit#getEditSessionFactory()} to create {@link EditSession}s
- */
- @Deprecated
- public EditSession(LocalWorld world, int maxBlocks, @Nullable BlockBag blockBag) {
- this(WorldEdit.getInstance().getEventBus(), world, maxBlocks, blockBag, new EditSessionEvent(world, null, maxBlocks, null));
- }
- /**
- * Construct the object with a maximum number of blocks and a block bag.
- *
- * @param eventBus the event bus
- * @param world the world
- * @param maxBlocks the maximum number of blocks that can be changed, or -1 to use no limit
- * @param blockBag an optional {@link BlockBag} to use, otherwise null
- * @param event the event to call with the extent
- */
- EditSession(EventBus eventBus, World world, int maxBlocks, @Nullable BlockBag blockBag, EditSessionEvent event) {
- checkNotNull(eventBus);
- checkArgument(maxBlocks >= -1, "maxBlocks >= -1 required");
- checkNotNull(event);
- this.world = world;
- if (world != null) {
- Extent extent;
- // These extents are ALWAYS used
- extent = fastModeExtent = new FastModeExtent(world, false);
- extent = survivalExtent = new SurvivalModeExtent(extent, world);
- extent = quirkExtent = new BlockQuirkExtent(extent, world);
- extent = chunkLoadingExtent = new ChunkLoadingExtent(extent, world);
- extent = cacheExtent = new LastAccessExtentCache(extent);
- extent = wrapExtent(extent, eventBus, event, Stage.BEFORE_CHANGE);
- extent = validator = new DataValidatorExtent(extent, world);
- extent = blockBagExtent = new BlockBagExtent(extent, blockBag);
- // This extent can be skipped by calling rawSetBlock()
- extent = reorderExtent = new MultiStageReorder(extent, false);
- extent = wrapExtent(extent, eventBus, event, Stage.BEFORE_REORDER);
- // These extents can be skipped by calling smartSetBlock()
- extent = changeSetExtent = new ChangeSetExtent(extent, changeSet);
- extent = maskingExtent = new MaskingExtent(extent, Masks.alwaysTrue());
- extent = changeLimiter = new BlockChangeLimiter(extent, maxBlocks);
- extent = wrapExtent(extent, eventBus, event, Stage.BEFORE_HISTORY);
- this.bypassReorderHistory = blockBagExtent;
- this.bypassHistory = reorderExtent;
- this.bypassNone = extent;
- } else {
- Extent extent = new NullExtent();
- extent = survivalExtent = new SurvivalModeExtent(extent, NullWorld.getInstance());
- extent = blockBagExtent = new BlockBagExtent(extent, blockBag);
- extent = reorderExtent = new MultiStageReorder(extent, false);
- extent = maskingExtent = new MaskingExtent(extent, Masks.alwaysTrue());
- extent = changeLimiter = new BlockChangeLimiter(extent, maxBlocks);
- this.bypassReorderHistory = extent;
- this.bypassHistory = extent;
- this.bypassNone = extent;
- }
- }
- private Extent wrapExtent(Extent extent, EventBus eventBus, EditSessionEvent event, Stage stage) {
- event = event.clone(stage);
- event.setExtent(extent);
- eventBus.post(event);
- return event.getExtent();
- }
- /**
- * Get the world.
- *
- * @return the world
- */
- public World getWorld() {
- return world;
- }
- /**
- * Get the underlying {@link ChangeSet}.
- *
- * @return the change set
- */
- public ChangeSet getChangeSet() {
- return changeSet;
- }
- /**
- * Get the maximum number of blocks that can be changed. -1 will be returned
- * if it the limit disabled.
- *
- * @return the limit (>= 0) or -1 for no limit
- */
- public int getBlockChangeLimit() {
- return changeLimiter.getLimit();
- }
- /**
- * Set the maximum number of blocks that can be changed.
- *
- * @param limit the limit (>= 0) or -1 for no limit
- */
- public void setBlockChangeLimit(int limit) {
- changeLimiter.setLimit(limit);
- }
- /**
- * Returns queue status.
- *
- * @return whether the queue is enabled
- */
- public boolean isQueueEnabled() {
- return reorderExtent.isEnabled();
- }
- /**
- * Queue certain types of block for better reproduction of those blocks.
- */
- public void enableQueue() {
- reorderExtent.setEnabled(true);
- }
- /**
- * Disable the queue. This will flush the queue.
- */
- public void disableQueue() {
- if (isQueueEnabled()) {
- flushQueue();
- }
- reorderExtent.setEnabled(true);
- }
- /**
- * Get the mask.
- *
- * @return mask, may be null
- */
- public Mask getMask() {
- return oldMask;
- }
- /**
- * Set a mask.
- *
- * @param mask mask or null
- */
- public void setMask(Mask mask) {
- this.oldMask = mask;
- if (mask == null) {
- maskingExtent.setMask(Masks.alwaysTrue());
- } else {
- maskingExtent.setMask(mask);
- }
- }
- /**
- * Set the mask.
- *
- * @param mask the mask
- * @deprecated Use {@link #setMask(Mask)}
- */
- @Deprecated
- public void setMask(com.sk89q.worldedit.masks.Mask mask) {
- if (mask == null) {
- setMask((Mask) null);
- } else {
- setMask(Masks.wrap(mask));
- }
- }
- /**
- * Get the {@link SurvivalModeExtent}.
- *
- * @return the survival simulation extent
- */
- public SurvivalModeExtent getSurvivalExtent() {
- return survivalExtent;
- }
- /**
- * Set whether fast mode is enabled.
- *
- * <p>Fast mode may skip lighting checks or adjacent block
- * notification.</p>
- *
- * @param enabled true to enable
- */
- public void setFastMode(boolean enabled) {
- if (fastModeExtent != null) {
- fastModeExtent.setEnabled(enabled);
- }
- }
- /**
- * Return fast mode status.
- *
- * <p>Fast mode may skip lighting checks or adjacent block
- * notification.</p>
- *
- * @return true if enabled
- */
- public boolean hasFastMode() {
- return fastModeExtent != null && fastModeExtent.isEnabled();
- }
- /**
- * Get the {@link BlockBag} is used.
- *
- * @return a block bag or null
- */
- public BlockBag getBlockBag() {
- return blockBagExtent.getBlockBag();
- }
- /**
- * Set a {@link BlockBag} to use.
- *
- * @param blockBag the block bag to set, or null to use none
- */
- public void setBlockBag(BlockBag blockBag) {
- blockBagExtent.setBlockBag(blockBag);
- }
- /**
- * Gets the list of missing blocks and clears the list for the next
- * operation.
- *
- * @return a map of missing blocks
- */
- public Map<Integer, Integer> popMissingBlocks() {
- return blockBagExtent.popMissing();
- }
- /**
- * Get the number of blocks changed, including repeated block changes.
- *
- * <p>This number may not be accurate.</p>
- *
- * @return the number of block changes
- */
- public int getBlockChangeCount() {
- return changeSet.size();
- }
- @Override
- public BaseBiome getBiome(Vector2D position) {
- return bypassNone.getBiome(position);
- }
- @Override
- public boolean setBiome(Vector2D position, BaseBiome biome) {
- return bypassNone.setBiome(position, biome);
- }
- @Override
- public BaseBlock getLazyBlock(Vector position) {
- return world.getLazyBlock(position);
- }
- @Override
- public BaseBlock getBlock(Vector position) {
- return world.getBlock(position);
- }
- /**
- * Get a block type at the given position.
- *
- * @param position the position
- * @return the block type
- * @deprecated Use {@link #getLazyBlock(Vector)} or {@link #getBlock(Vector)}
- */
- @Deprecated
- public int getBlockType(Vector position) {
- return world.getBlockType(position);
- }
- /**
- * Get a block data at the given position.
- *
- * @param position the position
- * @return the block data
- * @deprecated Use {@link #getLazyBlock(Vector)} or {@link #getBlock(Vector)}
- */
- @Deprecated
- public int getBlockData(Vector position) {
- return world.getBlockData(position);
- }
- /**
- * Gets the block type at a position.
- *
- * @param position the position
- * @return a block
- * @deprecated Use {@link #getBlock(Vector)}
- */
- @Deprecated
- public BaseBlock rawGetBlock(Vector position) {
- return getBlock(position);
- }
- /**
- * Returns the highest solid 'terrain' block which can occur naturally.
- *
- * @param x the X coordinate
- * @param z the Z cooridnate
- * @param minY minimal height
- * @param maxY maximal height
- * @return height of highest block found or 'minY'
- */
- public int getHighestTerrainBlock(int x, int z, int minY, int maxY) {
- return getHighestTerrainBlock(x, z, minY, maxY, false);
- }
- /**
- * Returns the highest solid 'terrain' block which can occur naturally.
- *
- * @param x the X coordinate
- * @param z the Z coordinate
- * @param minY minimal height
- * @param maxY maximal height
- * @param naturalOnly look at natural blocks or all blocks
- * @return height of highest block found or 'minY'
- */
- public int getHighestTerrainBlock(int x, int z, int minY, int maxY, boolean naturalOnly) {
- for (int y = maxY; y >= minY; --y) {
- Vector pt = new Vector(x, y, z);
- int id = getBlockType(pt);
- int data = getBlockData(pt);
- if (naturalOnly ? BlockType.isNaturalTerrainBlock(id, data) : !BlockType.canPassThrough(id, data)) {
- return y;
- }
- }
- return minY;
- }
- /**
- * Set a block, bypassing both history and block re-ordering.
- *
- * @param position the position to set the block at
- * @param block the block
- * @param stage the level
- * @return whether the block changed
- * @throws WorldEditException thrown on a set error
- */
- public boolean setBlock(Vector position, BaseBlock block, Stage stage) throws WorldEditException {
- switch (stage) {
- case BEFORE_HISTORY:
- return bypassNone.setBlock(position, block);
- case BEFORE_CHANGE:
- return bypassHistory.setBlock(position, block);
- case BEFORE_REORDER:
- return bypassReorderHistory.setBlock(position, block);
- }
- throw new RuntimeException("New enum entry added that is unhandled here");
- }
- /**
- * Set a block, bypassing both history and block re-ordering.
- *
- * @param position the position to set the block at
- * @param block the block
- * @return whether the block changed
- */
- public boolean rawSetBlock(Vector position, BaseBlock block) {
- try {
- return setBlock(position, block, Stage.BEFORE_CHANGE);
- } catch (WorldEditException e) {
- throw new RuntimeException("Unexpected exception", e);
- }
- }
- /**
- * Set a block, bypassing history but still utilizing block re-ordering.
- *
- * @param position the position to set the block at
- * @param block the block
- * @return whether the block changed
- */
- public boolean smartSetBlock(Vector position, BaseBlock block) {
- try {
- return setBlock(position, block, Stage.BEFORE_REORDER);
- } catch (WorldEditException e) {
- throw new RuntimeException("Unexpected exception", e);
- }
- }
- @Override
- public boolean setBlock(Vector position, BaseBlock block) throws MaxChangedBlocksException {
- try {
- return setBlock(position, block, Stage.BEFORE_HISTORY);
- } catch (MaxChangedBlocksException e) {
- throw e;
- } catch (WorldEditException e) {
- throw new RuntimeException("Unexpected exception", e);
- }
- }
- /**
- * Sets the block at a position, subject to both history and block re-ordering.
- *
- * @param position the position
- * @param pattern a pattern to use
- * @return Whether the block changed -- not entirely dependable
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public boolean setBlock(Vector position, Pattern pattern) throws MaxChangedBlocksException {
- return setBlock(position, pattern.next(position));
- }
- /**
- * Set blocks that are in a set of positions and return the number of times
- * that the block set calls returned true.
- *
- * @param vset a set of positions
- * @param pattern the pattern
- * @return the number of changed blocks
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- private int setBlocks(Set<Vector> vset, Pattern pattern) throws MaxChangedBlocksException {
- int affected = 0;
- for (Vector v : vset) {
- affected += setBlock(v, pattern) ? 1 : 0;
- }
- return affected;
- }
- /**
- * Set a block (only if a previous block was not there) if {@link Math#random()}
- * returns a number less than the given probability.
- *
- * @param position the position
- * @param block the block
- * @param probability a probability between 0 and 1, inclusive
- * @return whether a block was changed
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public boolean setChanceBlockIfAir(Vector position, BaseBlock block, double probability)
- throws MaxChangedBlocksException {
- return Math.random() <= probability && setBlockIfAir(position, block);
- }
- /**
- * Set a block only if there's no block already there.
- *
- * @param position the position
- * @param block the block to set
- * @return if block was changed
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- * @deprecated Use your own method
- */
- @Deprecated
- public boolean setBlockIfAir(Vector position, BaseBlock block) throws MaxChangedBlocksException {
- return getBlock(position).isAir() && setBlock(position, block);
- }
- @Override
- @Nullable
- public Entity createEntity(com.sk89q.worldedit.util.Location location, BaseEntity entity) {
- return bypassNone.createEntity(location, entity);
- }
- /**
- * Insert a contrived block change into the history.
- *
- * @param position the position
- * @param existing the previous block at that position
- * @param block the new block
- * @deprecated Get the change set with {@link #getChangeSet()} and add the change with that
- */
- @Deprecated
- public void rememberChange(Vector position, BaseBlock existing, BaseBlock block) {
- changeSet.add(new BlockChange(position.toBlockVector(), existing, block));
- }
- /**
- * Restores all blocks to their initial state.
- *
- * @param editSession a new {@link EditSession} to perform the undo in
- */
- public void undo(EditSession editSession) {
- UndoContext context = new UndoContext();
- context.setExtent(editSession.bypassHistory);
- Operations.completeBlindly(ChangeSetExecutor.createUndo(changeSet, context));
- editSession.flushQueue();
- }
- /**
- * Sets to new state.
- *
- * @param editSession a new {@link EditSession} to perform the redo in
- */
- public void redo(EditSession editSession) {
- UndoContext context = new UndoContext();
- context.setExtent(editSession.bypassHistory);
- Operations.completeBlindly(ChangeSetExecutor.createRedo(changeSet, context));
- editSession.flushQueue();
- }
- /**
- * Get the number of changed blocks.
- *
- * @return the number of changes
- */
- public int size() {
- return getBlockChangeCount();
- }
- @Override
- public Vector getMinimumPoint() {
- return getWorld().getMinimumPoint();
- }
- @Override
- public Vector getMaximumPoint() {
- return getWorld().getMaximumPoint();
- }
- @Override
- public List<? extends Entity> getEntities(Region region) {
- return bypassNone.getEntities(region);
- }
- @Override
- public List<? extends Entity> getEntities() {
- return bypassNone.getEntities();
- }
- /**
- * Finish off the queue.
- */
- public void flushQueue() {
- Operations.completeBlindly(commit());
- }
- @Override
- public @Nullable Operation commit() {
- return bypassNone.commit();
- }
- /**
- * Count the number of blocks of a given list of types in a region.
- *
- * @param region the region
- * @param searchIDs a list of IDs to search
- * @return the number of found blocks
- */
- public int countBlock(Region region, Set<Integer> searchIDs) {
- Set<BaseBlock> passOn = new HashSet<BaseBlock>();
- for (Integer i : searchIDs) {
- passOn.add(new BaseBlock(i, -1));
- }
- return countBlocks(region, passOn);
- }
- /**
- * Count the number of blocks of a list of types in a region.
- *
- * @param region the region
- * @param searchBlocks the list of blocks to search
- * @return the number of blocks that matched the pattern
- */
- public int countBlocks(Region region, Set<BaseBlock> searchBlocks) {
- FuzzyBlockMask mask = new FuzzyBlockMask(this, searchBlocks);
- Counter count = new Counter();
- RegionMaskingFilter filter = new RegionMaskingFilter(mask, count);
- RegionVisitor visitor = new RegionVisitor(region, filter);
- Operations.completeBlindly(visitor); // We can't throw exceptions, nor do we expect any
- return count.getCount();
- }
- /**
- * Fills an area recursively in the X/Z directions.
- *
- * @param origin the location to start from
- * @param block the block to fill with
- * @param radius the radius of the spherical area to fill
- * @param depth the maximum depth, starting from the origin
- * @param recursive whether a breadth-first search should be performed
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int fillXZ(Vector origin, BaseBlock block, double radius, int depth, boolean recursive)
- throws MaxChangedBlocksException {
- return fillXZ(origin, new SingleBlockPattern(block), radius, depth, recursive);
- }
- /**
- * Fills an area recursively in the X/Z directions.
- *
- * @param origin the origin to start the fill from
- * @param pattern the pattern to fill with
- * @param radius the radius of the spherical area to fill, with 0 as the smallest radius
- * @param depth the maximum depth, starting from the origin, with 1 as the smallest depth
- * @param recursive whether a breadth-first search should be performed
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int fillXZ(Vector origin, Pattern pattern, double radius, int depth, boolean recursive) throws MaxChangedBlocksException {
- checkNotNull(origin);
- checkNotNull(pattern);
- checkArgument(radius >= 0, "radius >= 0");
- checkArgument(depth >= 1, "depth >= 1");
- MaskIntersection mask = new MaskIntersection(
- new RegionMask(new EllipsoidRegion(null, origin, new Vector(radius, radius, radius))),
- new BoundedHeightMask(
- Math.max(origin.getBlockY() - depth + 1, 0),
- Math.min(getWorld().getMaxY(), origin.getBlockY())),
- Masks.negate(new ExistingBlockMask(this)));
- // Want to replace blocks
- BlockReplace replace = new BlockReplace(this, Patterns.wrap(pattern));
- // Pick how we're going to visit blocks
- RecursiveVisitor visitor;
- if (recursive) {
- visitor = new RecursiveVisitor(mask, replace);
- } else {
- visitor = new DownwardVisitor(mask, replace, origin.getBlockY());
- }
- // Start at the origin
- visitor.visit(origin);
- // Execute
- Operations.completeLegacy(visitor);
- return visitor.getAffected();
- }
- /**
- * Remove a cuboid above the given position with a given apothem and a given height.
- *
- * @param position base position
- * @param apothem an apothem of the cuboid (on the XZ plane), where the minimum is 1
- * @param height the height of the cuboid, where the minimum is 1
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int removeAbove(Vector position, int apothem, int height) throws MaxChangedBlocksException {
- checkNotNull(position);
- checkArgument(apothem >= 1, "apothem >= 1");
- checkArgument(height >= 1, "height >= 1");
- Region region = new CuboidRegion(
- getWorld(), // Causes clamping of Y range
- position.add(-apothem + 1, 0, -apothem + 1),
- position.add(apothem - 1, height - 1, apothem - 1));
- Pattern pattern = new SingleBlockPattern(new BaseBlock(BlockID.AIR));
- return setBlocks(region, pattern);
- }
- /**
- * Remove a cuboid below the given position with a given apothem and a given height.
- *
- * @param position base position
- * @param apothem an apothem of the cuboid (on the XZ plane), where the minimum is 1
- * @param height the height of the cuboid, where the minimum is 1
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int removeBelow(Vector position, int apothem, int height) throws MaxChangedBlocksException {
- checkNotNull(position);
- checkArgument(apothem >= 1, "apothem >= 1");
- checkArgument(height >= 1, "height >= 1");
- Region region = new CuboidRegion(
- getWorld(), // Causes clamping of Y range
- position.add(-apothem + 1, 0, -apothem + 1),
- position.add(apothem - 1, -height + 1, apothem - 1));
- Pattern pattern = new SingleBlockPattern(new BaseBlock(BlockID.AIR));
- return setBlocks(region, pattern);
- }
- /**
- * Remove blocks of a certain type nearby a given position.
- *
- * @param position center position of cuboid
- * @param blockType the block type to match
- * @param apothem an apothem of the cuboid, where the minimum is 1
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int removeNear(Vector position, int blockType, int apothem) throws MaxChangedBlocksException {
- checkNotNull(position);
- checkArgument(apothem >= 1, "apothem >= 1");
- Mask mask = new FuzzyBlockMask(this, new BaseBlock(blockType, -1));
- Vector adjustment = new Vector(1, 1, 1).multiply(apothem - 1);
- Region region = new CuboidRegion(
- getWorld(), // Causes clamping of Y range
- position.add(adjustment.multiply(-1)),
- position.add(adjustment));
- Pattern pattern = new SingleBlockPattern(new BaseBlock(BlockID.AIR));
- return replaceBlocks(region, mask, pattern);
- }
- /**
- * Sets all the blocks inside a region to a given block type.
- *
- * @param region the region
- * @param block the block
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int setBlocks(Region region, BaseBlock block) throws MaxChangedBlocksException {
- return setBlocks(region, new SingleBlockPattern(block));
- }
- /**
- * Sets all the blocks inside a region to a given pattern.
- *
- * @param region the region
- * @param pattern the pattern that provides the replacement block
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException {
- checkNotNull(region);
- checkNotNull(pattern);
- BlockReplace replace = new BlockReplace(this, Patterns.wrap(pattern));
- RegionVisitor visitor = new RegionVisitor(region, replace);
- Operations.completeLegacy(visitor);
- return visitor.getAffected();
- }
- /**
- * Replaces all the blocks matching a given filter, within a given region, to a block
- * returned by a given pattern.
- *
- * @param region the region to replace the blocks within
- * @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.masks.ExistingBlockMask}
- * @param replacement the replacement block
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int replaceBlocks(Region region, Set<BaseBlock> filter, BaseBlock replacement) throws MaxChangedBlocksException {
- return replaceBlocks(region, filter, new SingleBlockPattern(replacement));
- }
- /**
- * Replaces all the blocks matching a given filter, within a given region, to a block
- * returned by a given pattern.
- *
- * @param region the region to replace the blocks within
- * @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.masks.ExistingBlockMask}
- * @param pattern the pattern that provides the new blocks
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int replaceBlocks(Region region, Set<BaseBlock> filter, Pattern pattern) throws MaxChangedBlocksException {
- Mask mask = filter == null ? new ExistingBlockMask(this) : new FuzzyBlockMask(this, filter);
- return replaceBlocks(region, mask, pattern);
- }
- /**
- * Replaces all the blocks matching a given mask, within a given region, to a block
- * returned by a given pattern.
- *
- * @param region the region to replace the blocks within
- * @param mask the mask that blocks must match
- * @param pattern the pattern that provides the new blocks
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int replaceBlocks(Region region, Mask mask, Pattern pattern) throws MaxChangedBlocksException {
- checkNotNull(region);
- checkNotNull(mask);
- checkNotNull(pattern);
- BlockReplace replace = new BlockReplace(this, Patterns.wrap(pattern));
- RegionMaskingFilter filter = new RegionMaskingFilter(mask, replace);
- RegionVisitor visitor = new RegionVisitor(region, filter);
- Operations.completeLegacy(visitor);
- return visitor.getAffected();
- }
- /**
- * Sets the blocks at the center of the given region to the given pattern.
- * If the center sits between two blocks on a certain axis, then two blocks
- * will be placed to mark the center.
- *
- * @param region the region to find the center of
- * @param pattern the replacement pattern
- * @return the number of blocks placed
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int center(Region region, Pattern pattern) throws MaxChangedBlocksException {
- checkNotNull(region);
- checkNotNull(pattern);
- Vector center = region.getCenter();
- Region centerRegion = new CuboidRegion(
- getWorld(), // Causes clamping of Y range
- new Vector((int) center.getX(), (int) center.getY(), (int) center.getZ()),
- center.toBlockVector());
- return setBlocks(centerRegion, pattern);
- }
- /**
- * Make the faces of the given region as if it was a {@link CuboidRegion}.
- *
- * @param region the region
- * @param block the block to place
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int makeCuboidFaces(Region region, BaseBlock block) throws MaxChangedBlocksException {
- return makeCuboidFaces(region, new SingleBlockPattern(block));
- }
- /**
- * Make the faces of the given region as if it was a {@link CuboidRegion}.
- *
- * @param region the region
- * @param pattern the pattern to place
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int makeCuboidFaces(Region region, Pattern pattern) throws MaxChangedBlocksException {
- checkNotNull(region);
- checkNotNull(pattern);
- CuboidRegion cuboid = CuboidRegion.makeCuboid(region);
- Region faces = cuboid.getFaces();
- return setBlocks(faces, pattern);
- }
- /**
- * Make the faces of the given region. The method by which the faces are found
- * may be inefficient, because there may not be an efficient implementation supported
- * for that specific shape.
- *
- * @param region the region
- * @param pattern the pattern to place
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int makeFaces(final Region region, Pattern pattern) throws MaxChangedBlocksException {
- checkNotNull(region);
- checkNotNull(pattern);
- if (region instanceof CuboidRegion) {
- return makeCuboidFaces(region, pattern);
- } else {
- return new RegionShape(region).generate(this, pattern, true);
- }
- }
- /**
- * Make the walls (all faces but those parallel to the X-Z plane) of the given region
- * as if it was a {@link CuboidRegion}.
- *
- * @param region the region
- * @param block the block to place
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int makeCuboidWalls(Region region, BaseBlock block) throws MaxChangedBlocksException {
- return makeCuboidWalls(region, new SingleBlockPattern(block));
- }
- /**
- * Make the walls (all faces but those parallel to the X-Z plane) of the given region
- * as if it was a {@link CuboidRegion}.
- *
- * @param region the region
- * @param pattern the pattern to place
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int makeCuboidWalls(Region region, Pattern pattern) throws MaxChangedBlocksException {
- checkNotNull(region);
- checkNotNull(pattern);
- CuboidRegion cuboid = CuboidRegion.makeCuboid(region);
- Region faces = cuboid.getWalls();
- return setBlocks(faces, pattern);
- }
- /**
- * Make the walls of the given region. The method by which the walls are found
- * may be inefficient, because there may not be an efficient implementation supported
- * for that specific shape.
- *
- * @param region the region
- * @param pattern the pattern to place
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int makeWalls(final Region region, Pattern pattern) throws MaxChangedBlocksException {
- checkNotNull(region);
- checkNotNull(pattern);
- if (region instanceof CuboidRegion) {
- return makeCuboidWalls(region, pattern);
- } else {
- final int minY = region.getMinimumPoint().getBlockY();
- final int maxY = region.getMaximumPoint().getBlockY();
- final ArbitraryShape shape = new RegionShape(region) {
- @Override
- protected BaseBlock getMaterial(int x, int y, int z, BaseBlock defaultMaterial) {
- if (y > maxY || y < minY) {
- // Put holes into the floor and ceiling by telling ArbitraryShape that the shape goes on outside the region
- return defaultMaterial;
- }
- return super.getMaterial(x, y, z, defaultMaterial);
- }
- };
- return shape.generate(this, pattern, true);
- }
- }
- /**
- * Places a layer of blocks on top of ground blocks in the given region
- * (as if it were a cuboid).
- *
- * @param region the region
- * @param block the placed block
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int overlayCuboidBlocks(Region region, BaseBlock block) throws MaxChangedBlocksException {
- checkNotNull(block);
- return overlayCuboidBlocks(region, new SingleBlockPattern(block));
- }
- /**
- * Places a layer of blocks on top of ground blocks in the given region
- * (as if it were a cuboid).
- *
- * @param region the region
- * @param pattern the placed block pattern
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- @SuppressWarnings("deprecation")
- public int overlayCuboidBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException {
- checkNotNull(region);
- checkNotNull(pattern);
- BlockReplace replace = new BlockReplace(this, Patterns.wrap(pattern));
- RegionOffset offset = new RegionOffset(new Vector(0, 1, 0), replace);
- GroundFunction ground = new GroundFunction(new ExistingBlockMask(this), offset);
- LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground);
- Operations.completeLegacy(visitor);
- return ground.getAffected();
- }
- /**
- * Turns the first 3 layers into dirt/grass and the bottom layers
- * into rock, like a natural Minecraft mountain.
- *
- * @param region the region to affect
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- public int naturalizeCuboidBlocks(Region region) throws MaxChangedBlocksException {
- checkNotNull(region);
- Naturalizer naturalizer = new Naturalizer(this);
- FlatRegion flatRegion = Regions.asFlatRegion(region);
- LayerVisitor visitor = new LayerVisitor(flatRegion, minimumBlockY(region), maximumBlockY(region), naturalizer);
- Operations.completeLegacy(visitor);
- return naturalizer.getAffected();
- }
- /**
- * Stack a cuboid region.
- *
- * @param region the region to stack
- * @param dir the direction to stack
- * @param count the number of times to stack
- * @param copyAir true to also copy air blocks
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- public int stackCuboidRegion(Region region, Vector dir, int count, boolean copyAir) throws MaxChangedBlocksException {
- checkNotNull(region);
- checkNotNull(dir);
- checkArgument(count >= 1, "count >= 1 required");
- Vector size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1);
- Vector to = region.getMinimumPoint();
- ForwardExtentCopy copy = new ForwardExtentCopy(this, region, this, to);
- copy.setRepetitions(count);
- copy.setTransform(new AffineTransform().translate(dir.multiply(size)));
- if (!copyAir) {
- copy.setSourceMask(new ExistingBlockMask(this));
- }
- Operations.completeLegacy(copy);
- return copy.getAffected();
- }
- /**
- * Move the blocks in a region a certain direction.
- *
- * @param region the region to move
- * @param dir the direction
- * @param distance the distance to move
- * @param copyAir true to copy air blocks
- * @param replacement the replacement block to fill in after moving, or null to use air
- * @return number of blocks moved
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- public int moveRegion(Region region, Vector dir, int distance, boolean copyAir, BaseBlock replacement) throws MaxChangedBlocksException {
- checkNotNull(region);
- checkNotNull(dir);
- checkArgument(distance >= 1, "distance >= 1 required");
- Vector to = region.getMinimumPoint();
- // Remove the original blocks
- com.sk89q.worldedit.function.pattern.Pattern pattern = replacement != null ?
- new BlockPattern(replacement) :
- new BlockPattern(new BaseBlock(BlockID.AIR));
- BlockReplace remove = new BlockReplace(this, pattern);
- // Copy to a buffer so we don't destroy our original before we can copy all the blocks from it
- ForgetfulExtentBuffer buffer = new ForgetfulExtentBuffer(this, new RegionMask(region));
- ForwardExtentCopy copy = new ForwardExtentCopy(this, region, buffer, to);
- copy.setTransform(new AffineTransform().translate(dir.multiply(distance)));
- copy.setSourceFunction(remove); // Remove
- copy.setRemovingEntities(true);
- if (!copyAir) {
- copy.setSourceMask(new ExistingBlockMask(this));
- }
- // Then we need to copy the buffer to the world
- BlockReplace replace = new BlockReplace(this, buffer);
- RegionVisitor visitor = new RegionVisitor(buffer.asRegion(), replace);
- OperationQueue operation = new OperationQueue(copy, visitor);
- Operations.completeLegacy(operation);
- return copy.getAffected();
- }
- /**
- * Move the blocks in a region a certain direction.
- *
- * @param region the region to move
- * @param dir the direction
- * @param distance the distance to move
- * @param copyAir true to copy air blocks
- * @param replacement the replacement block to fill in after moving, or null to use air
- * @return number of blocks moved
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- public int moveCuboidRegion(Region region, Vector dir, int distance, boolean copyAir, BaseBlock replacement) throws MaxChangedBlocksException {
- return moveRegion(region, dir, distance, copyAir, replacement);
- }
- /**
- * Drain nearby pools of water or lava.
- *
- * @param origin the origin to drain from, which will search a 3x3 area
- * @param radius the radius of the removal, where a value should be 0 or greater
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- public int drainArea(Vector origin, double radius) throws MaxChangedBlocksException {
- checkNotNull(origin);
- checkArgument(radius >= 0, "radius >= 0 required");
- MaskIntersection mask = new MaskIntersection(
- new BoundedHeightMask(0, getWorld().getMaxY()),
- new RegionMask(new EllipsoidRegion(null, origin, new Vector(radius, radius, radius))),
- getWorld().createLiquidMask());
- BlockReplace replace = new BlockReplace(this, new BlockPattern(new BaseBlock(BlockID.AIR)));
- RecursiveVisitor visitor = new RecursiveVisitor(mask, replace);
- // Around the origin in a 3x3 block
- for (BlockVector position : CuboidRegion.fromCenter(origin, 1)) {
- if (mask.test(position)) {
- visitor.visit(position);
- }
- }
- Operations.completeLegacy(visitor);
- return visitor.getAffected();
- }
- /**
- * Fix liquids so that they turn into stationary blocks and extend outward.
- *
- * @param origin the original position
- * @param radius the radius to fix
- * @param moving the block ID of the moving liquid
- * @param stationary the block ID of the stationary liquid
- * @return number of blocks affected
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- public int fixLiquid(Vector origin, double radius, int moving, int stationary) throws MaxChangedBlocksException {
- checkNotNull(origin);
- checkArgument(radius >= 0, "radius >= 0 required");
- // Our origins can only be liquids
- BlockMask liquidMask = new BlockMask(
- this,
- new BaseBlock(moving, -1),
- new BaseBlock(stationary, -1));
- // But we will also visit air blocks
- MaskIntersection blockMask =
- new MaskUnion(liquidMask,
- new BlockMask(
- this,
- new BaseBlock(BlockID.AIR)));
- // There are boundaries that the routine needs to stay in
- MaskIntersection mask = new MaskIntersection(
- new BoundedHeightMask(0, Math.min(origin.getBlockY(), getWorld().getMaxY())),
- new RegionMask(new EllipsoidRegion(null, origin, new Vector(radius, radius, radius))),
- blockMask);
- BlockReplace replace = new BlockReplace(this, new BlockPattern(new BaseBlock(stationary)));
- NonRisingVisitor visitor = new NonRisingVisitor(mask, replace);
- // Around the origin in a 3x3 block
- for (BlockVector position : CuboidRegion.fromCenter(origin, 1)) {
- if (liquidMask.test(position)) {
- visitor.visit(position);
- }
- }
- Operations.completeLegacy(visitor);
- return visitor.getAffected();
- }
- /**
- * Makes a cylinder.
- *
- * @param pos Center of the cylinder
- * @param block The block pattern to use
- * @param radius The cylinder's radius
- * @param height The cylinder's up/down extent. If negative, extend downward.
- * @param filled If false, only a shell will be generated.
- * @return number of blocks changed
- * @throws MaxChangedBlocksException thrown if too many blocks are changed
- */
- public int makeCylinder(Vector pos, Pattern block, double radius, int height, boolean filled) throws MaxChangedBlocksException {
- return makeCylinder(pos, block, radi