Broke up EditSession into many Extents.

This commit is contained in:
sk89q 2014-03-31 16:55:58 -07:00
parent 10e672a94a
commit e7fe787b20
15 changed files with 1463 additions and 665 deletions

File diff suppressed because it is too large Load Diff

View File

@ -25,9 +25,11 @@ import com.sk89q.worldedit.foundation.Block;
import com.sk89q.worldedit.foundation.World;
import com.sk89q.worldedit.function.mask.BlockMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.TreeGenerator;
import javax.annotation.Nullable;
import java.util.PriorityQueue;
import java.util.Random;
@ -618,4 +620,9 @@ public abstract class LocalWorld implements World, Extent {
new BaseBlock(BlockID.WATER, -1));
}
@Override
public @Nullable Operation commit() {
return null;
}
}

View File

@ -0,0 +1,120 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.LocalWorld;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bags.BlockBag;
import com.sk89q.worldedit.bags.BlockBagException;
import com.sk89q.worldedit.bags.UnplaceableBlockException;
import com.sk89q.worldedit.blocks.BaseBlock;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Applies a {@link BlockBag} to operations.
*/
public class BlockBagExtent extends ExtentDelegate {
private final LocalWorld world;
private Map<Integer, Integer> missingBlocks = new HashMap<Integer, Integer>();
private BlockBag blockBag;
/**
* Create a new instance.
*
* @param extent the extent
* @param world the world
* @param blockBag the block bag
*/
public BlockBagExtent(Extent extent, LocalWorld world, @Nullable BlockBag blockBag) {
super(extent);
checkNotNull(world);
this.world = world;
this.blockBag = blockBag;
}
/**
* Get the block bag.
*
* @return a block bag, which may be null if none is used
*/
public @Nullable BlockBag getBlockBag() {
return blockBag;
}
/**
* Set the block bag.
*
* @param blockBag a block bag, which may be null if none is used
*/
public void setBlockBag(@Nullable BlockBag blockBag) {
this.blockBag = 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> popMissing() {
Map<Integer, Integer> missingBlocks = this.missingBlocks;
this.missingBlocks = new HashMap<Integer, Integer>();
return missingBlocks;
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
if (blockBag != null) {
final int type = block.getType();
final int existing = world.getBlockType(location);
if (type > 0) {
try {
blockBag.fetchPlacedBlock(type, 0);
} catch (UnplaceableBlockException e) {
return false;
} catch (BlockBagException e) {
if (!missingBlocks.containsKey(type)) {
missingBlocks.put(type, 1);
} else {
missingBlocks.put(type, missingBlocks.get(type) + 1);
}
return false;
}
}
if (existing > 0) {
try {
blockBag.storeDroppedBlock(existing, world.getBlockData(location));
} catch (BlockBagException ignored) {
}
}
}
return super.setBlock(location, block, notifyAdjacent);
}
}

View File

@ -0,0 +1,87 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Limits the number of blocks that can be changed before a
* {@link MaxChangedBlocksException} is thrown.
*/
public class BlockChangeLimiter extends ExtentDelegate {
private int limit;
private int count = 0;
/**
* Create a new instance.
*
* @param extent the extent
* @param limit the limit (>= 0) or -1 for no limit
*/
public BlockChangeLimiter(Extent extent, int limit) {
super(extent);
setLimit(limit);
}
/**
* Get the limit.
*
* @return the limit (>= 0) or -1 for no limit
*/
public int getLimit() {
return limit;
}
/**
* Set the limit.
*
* @param limit the limit (>= 0) or -1 for no limit
*/
public void setLimit(int limit) {
checkArgument(limit >= -1, "limit >= -1 required");
this.limit = limit;
}
/**
* Get the number of blocks that have been counted so far.
*
* @return the number of blocks
*/
public int getCount() {
return count;
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
if (limit >= 0) {
if (count >= limit) {
throw new MaxChangedBlocksException(limit);
}
count++;
}
return super.setBlock(location, block, notifyAdjacent);
}
}

View File

@ -0,0 +1,64 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.LocalWorld;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.blocks.BlockType;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handles various quirks when setting blocks, such as ice turning
* into water or containers dropping their contents.
*/
public class BlockQuirkExtent extends ExtentDelegate {
private final LocalWorld world;
/**
* Create a new instance.
*
* @param extent the extent
* @param world the world
*/
public BlockQuirkExtent(Extent extent, LocalWorld world) {
super(extent);
checkNotNull(world);
this.world = world;
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
final int existing = world.getBlockType(location);
if (BlockType.isContainerBlock(existing)) {
world.clearContainerBlockContents(location); // Clear the container block so that it doesn't drop items
} else if (existing == BlockID.ICE) {
world.setBlockType(location, BlockID.AIR); // Ice turns until water so this has to be done first
}
return super.setBlock(location, block, notifyAdjacent);
}
}

View File

@ -0,0 +1,56 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.history.change.BlockChange;
import com.sk89q.worldedit.history.changeset.ChangeSet;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Stores changes to a {@link ChangeSet}.
*/
public class ChangeSetExtent extends ExtentDelegate {
private final ChangeSet changeSet;
/**
* Create a new instance.
*
* @param extent the extent
* @param changeSet the change set
*/
public ChangeSetExtent(Extent extent, ChangeSet changeSet) {
super(extent);
checkNotNull(changeSet);
this.changeSet = changeSet;
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
BaseBlock previous = getBlock(location);
changeSet.add(new BlockChange(location.toBlockVector(), previous, block));
return super.setBlock(location, block, notifyAdjacent);
}
}

View File

@ -0,0 +1,66 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.LocalWorld;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Automatically loads chunks when blocks are accessed.
*/
public class ChunkLoadingExtent extends ExtentDelegate {
private final LocalWorld world;
private boolean enabled;
/**
* Create a new instance.
*
* @param extent the extent
* @param world the world
* @param enabled true to enable
*/
public ChunkLoadingExtent(Extent extent, LocalWorld world, boolean enabled) {
super(extent);
checkNotNull(world);
this.enabled = enabled;
this.world = world;
}
/**
* Create a new instance with chunk loading enabled.
*
* @param extent the extent
* @param world the world
*/
public ChunkLoadingExtent(Extent extent, LocalWorld world) {
this(extent, world, true);
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
world.checkLoadedChunk(location);
return super.setBlock(location, block, notifyAdjacent);
}
}

View File

@ -0,0 +1,63 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.LocalWorld;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Validates set data to prevent creating invalid blocks and such.
*/
public class DataValidatorExtent extends ExtentDelegate {
private final LocalWorld world;
/**
* Create a new instance.
*
* @param extent the extent
* @param world the world
*/
public DataValidatorExtent(Extent extent, LocalWorld world) {
super(extent);
checkNotNull(world);
this.world = world;
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
final int y = location.getBlockY();
final int type = block.getType();
if (y < 0 || y > world.getMaxY()) {
return false;
}
// No invalid blocks
if (!world.isValidBlockType(type)) {
return false;
}
return super.setBlock(location, block, notifyAdjacent);
}
}

View File

@ -22,6 +22,9 @@ package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.function.operation.Operation;
import javax.annotation.Nullable;
/**
* A world, portion of a world, clipboard, or other object that can have blocks set or
@ -76,4 +79,12 @@ public interface Extent {
*/
boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException;
/**
* Return an {@link Operation} that should be called to tie up loose ends
* (such as to commit changes in a buffer).
*
* @return an operation or null if there is none to execute
*/
@Nullable Operation commit();
}

View File

@ -0,0 +1,96 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.OperationQueue;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A base class for {@link Extent}s that merely passes extents onto another.
*/
public class ExtentDelegate implements Extent {
private final Extent extent;
/**
* Create a new instance.
*
* @param extent the extent
*/
public ExtentDelegate(Extent extent) {
checkNotNull(extent);
this.extent = extent;
}
/**
* Get the extent.
*
* @return the extent
*/
public Extent getExtent() {
return extent;
}
@Override
public BaseBlock getBlock(Vector location) {
return extent.getBlock(location);
}
@Override
public int getBlockType(Vector location) {
return extent.getBlockType(location);
}
@Override
public int getBlockData(Vector location) {
return extent.getBlockData(location);
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
return extent.setBlock(location, block, notifyAdjacent);
}
protected Operation commitBefore() {
return null;
}
@Override
public final @Nullable Operation commit() {
Operation ours = commitBefore();
Operation other = extent.commit();
if (ours != null && other != null) {
return new OperationQueue(ours, other);
} else if (ours != null) {
return ours;
} else if (other != null) {
return other;
} else {
return null;
}
}
}

View File

@ -0,0 +1,108 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.BlockVector2D;
import com.sk89q.worldedit.LocalWorld;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.function.operation.Operation;
import java.util.HashSet;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Implements "fast mode" which may skip physics, lighting, etc.
*/
public class FastModeExtent extends ExtentDelegate {
private final LocalWorld world;
private final Set<BlockVector2D> dirtyChunks = new HashSet<BlockVector2D>();
private boolean enabled = true;
/**
* Create a new instance with fast mode enabled.
*
* @param world the world
*/
public FastModeExtent(LocalWorld world) {
this(world, true);
}
/**
* Create a new instance.
*
* @param world the world
* @param enabled true to enable fast mode
*/
public FastModeExtent(LocalWorld world, boolean enabled) {
super(world);
checkNotNull(world);
this.world = world;
this.enabled = enabled;
}
/**
* Return whether fast mode is enabled.
*
* @return true if fast mode is enabled
*/
public boolean isEnabled() {
return enabled;
}
/**
* Set fast mode enable status.
*
* @param enabled true to enable fast mode
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
dirtyChunks.add(new BlockVector2D(location.getBlockX() >> 4, location.getBlockZ() >> 4));
return super.setBlock(location, block, notifyAdjacent || enabled);
}
@Override
protected Operation commitBefore() {
if (dirtyChunks.size() > 0) {
return new Operation() {
@Override
public Operation resume() throws WorldEditException {
world.fixAfterFastMode(dirtyChunks);
return null;
}
@Override
public void cancel() {
}
};
} else {
return null;
}
}
}

View File

@ -38,14 +38,16 @@ import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Buffers changes to an {@link Extent} and allows later retrieval of buffered
* changes for actual setting.
* Buffers changes to an {@link Extent} and allows later retrieval for
* actual application of the changes.
* </p>
* This buffer will not attempt to return results from the buffer when
* accessor methods (such as {@link #getBlock(Vector)}) are called.
*/
public class ExtentBuffer implements Extent, Pattern {
public class ForgetfulExtentBuffer extends ExtentDelegate implements Pattern {
private static final BaseBlock AIR = new BaseBlock(BlockID.AIR);
private final Extent delegate;
private final Map<BlockVector, BaseBlock> buffer = new LinkedHashMap<BlockVector, BaseBlock>();
private final Mask mask;
private Vector min = null;
@ -56,7 +58,7 @@ public class ExtentBuffer implements Extent, Pattern {
*
* @param delegate the delegate extent for {@link Extent#getBlock(Vector)}, etc. calls
*/
public ExtentBuffer(Extent delegate) {
public ForgetfulExtentBuffer(Extent delegate) {
this(delegate, Masks.alwaysTrue());
}
@ -67,28 +69,13 @@ public class ExtentBuffer implements Extent, Pattern {
* @param delegate the delegate extent for {@link Extent#getBlock(Vector)}, etc. calls
* @param mask the mask
*/
public ExtentBuffer(Extent delegate, Mask mask) {
public ForgetfulExtentBuffer(Extent delegate, Mask mask) {
super(delegate);
checkNotNull(delegate);
checkNotNull(mask);
this.delegate = delegate;
this.mask = mask;
}
@Override
public BaseBlock getBlock(Vector location) {
return delegate.getBlock(location);
}
@Override
public int getBlockType(Vector location) {
return delegate.getBlockType(location);
}
@Override
public int getBlockData(Vector location) {
return delegate.getBlockData(location);
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
// Update minimum
@ -110,7 +97,7 @@ public class ExtentBuffer implements Extent, Pattern {
buffer.put(blockVector, block);
return true;
} else {
return delegate.setBlock(location, block, notifyAdjacent);
return getExtent().setBlock(location, block, notifyAdjacent);
}
}

View File

@ -0,0 +1,73 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.function.mask.Mask;
import static com.google.common.base.Preconditions.checkNotNull;
public class MaskingExtent extends ExtentDelegate {
private Mask mask;
/**
* Create a new instance.
*
* @param extent the extent
* @param mask the mask
*/
public MaskingExtent(Extent extent, Mask mask) {
super(extent);
checkNotNull(mask);
this.mask = mask;
}
/**
* Get the mask.
*
* @return the mask
*/
public Mask getMask() {
return mask;
}
/**
* Set a mask.
*
* @param mask a mask
*/
public void setMask(Mask mask) {
checkNotNull(mask);
this.mask = mask;
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
if (mask.test(location)) {
return super.setBlock(location, block, notifyAdjacent);
} else {
return false;
}
}
}

View File

@ -0,0 +1,212 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extent.reorder;
import com.google.common.collect.Iterators;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.extent.ExtentDelegate;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.OperationQueue;
import com.sk89q.worldedit.function.visitor.BlockMapEntryVisitor;
import java.util.*;
/**
* Re-orders blocks into several stages.
*/
public class SimpleBlockReorder extends ExtentDelegate {
private DoubleArrayList<BlockVector, BaseBlock> stage1 = new DoubleArrayList<BlockVector, BaseBlock>(false);
private DoubleArrayList<BlockVector, BaseBlock> stage2 = new DoubleArrayList<BlockVector, BaseBlock>(false);
private DoubleArrayList<BlockVector, BaseBlock> stage3 = new DoubleArrayList<BlockVector, BaseBlock>(false);
private boolean enabled;
/**
* Create a new instance.
*
* @param extent the extent
* @param enabled true to enable
*/
public SimpleBlockReorder(Extent extent, boolean enabled) {
super(extent);
this.enabled = enabled;
}
/**
* Create a new instance when the re-ordering is enabled.
*
* @param extent the extent
*/
public SimpleBlockReorder(Extent extent) {
this(extent, true);
}
/**
* Return whether re-ordering is enabled.
*
* @return true if re-ordering is enabled
*/
public boolean isEnabled() {
return enabled;
}
/**
* Set whether re-ordering is enabled.
*
* @param enabled true if re-ordering is enabled
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public boolean setBlock(Vector location, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
if (!enabled) {
return super.setBlock(location, block, notifyAdjacent);
}
if (BlockType.shouldPlaceLast(block.getType())) {
// Place torches, etc. last
stage2.put(location.toBlockVector(), block);
return !(getBlockType(location) == block.getType() && getBlockData(location) == block.getData());
} else if (BlockType.shouldPlaceFinal(block.getType())) {
// Place signs, reed, etc even later
stage3.put(location.toBlockVector(), block);
return !(getBlockType(location) == block.getType() && getBlockData(location) == block.getData());
} else if (BlockType.shouldPlaceLast(getBlockType(location))) {
// Destroy torches, etc. first
super.setBlock(location, new BaseBlock(BlockID.AIR), notifyAdjacent);
return super.setBlock(location, block, notifyAdjacent);
} else {
stage1.put(location.toBlockVector(), block);
return !(getBlockType(location) == block.getType() && getBlockData(location) == block.getData());
}
}
@Override
public Operation commitBefore() {
return new OperationQueue(
new BlockMapEntryVisitor(
getExtent(),
Iterators.concat(stage1.iterator(), stage2.iterator())),
new Stage3Committer());
}
private class Stage3Committer implements Operation {
@Override
public Operation resume() throws WorldEditException {
Extent extent = getExtent();
final Set<BlockVector> blocks = new HashSet<BlockVector>();
final Map<BlockVector, BaseBlock> blockTypes = new HashMap<BlockVector, BaseBlock>();
for (Map.Entry<BlockVector, BaseBlock> entry : stage3) {
final BlockVector pt = entry.getKey();
blocks.add(pt);
blockTypes.put(pt, entry.getValue());
}
while (!blocks.isEmpty()) {
BlockVector current = blocks.iterator().next();
if (!blocks.contains(current)) {
continue;
}
final Deque<BlockVector> walked = new LinkedList<BlockVector>();
while (true) {
walked.addFirst(current);
assert (blockTypes.containsKey(current));
final BaseBlock baseBlock = blockTypes.get(current);
final int type = baseBlock.getType();
final int data = baseBlock.getData();
switch (type) {
case BlockID.WOODEN_DOOR:
case BlockID.IRON_DOOR:
if ((data & 0x8) == 0) {
// Deal with lower door halves being attached to the floor AND the upper half
BlockVector upperBlock = current.add(0, 1, 0).toBlockVector();
if (blocks.contains(upperBlock) && !walked.contains(upperBlock)) {
walked.addFirst(upperBlock);
}
}
break;
case BlockID.MINECART_TRACKS:
case BlockID.POWERED_RAIL:
case BlockID.DETECTOR_RAIL:
case BlockID.ACTIVATOR_RAIL:
// Here, rails are hardcoded to be attached to the block below them.
// They're also attached to the block they're ascending towards via BlockType.getAttachment.
BlockVector lowerBlock = current.add(0, -1, 0).toBlockVector();
if (blocks.contains(lowerBlock) && !walked.contains(lowerBlock)) {
walked.addFirst(lowerBlock);
}
break;
}
final PlayerDirection attachment = BlockType.getAttachment(type, data);
if (attachment == null) {
// Block is not attached to anything => we can place it
break;
}
current = current.add(attachment.vector()).toBlockVector();
if (!blocks.contains(current)) {
// We ran outside the remaing set => assume we can place blocks on this
break;
}
if (walked.contains(current)) {
// Cycle detected => This will most likely go wrong, but there's nothing we can do about it.
break;
}
}
for (BlockVector pt : walked) {
extent.setBlock(pt, blockTypes.get(pt), true);
blocks.remove(pt);
}
}
stage1.clear();
stage2.clear();
stage3.clear();
return null;
}
@Override
public void cancel() {
}
}
}

View File

@ -0,0 +1,58 @@
/*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.visitor;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.operation.Operation;
import java.util.Iterator;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
public class BlockMapEntryVisitor implements Operation {
private final Extent extent;
private final Iterator<Map.Entry<BlockVector, BaseBlock>> iterator;
public BlockMapEntryVisitor(Extent extent, Iterator<Map.Entry<BlockVector, BaseBlock>> iterator) {
checkNotNull(extent);
checkNotNull(iterator);
this.extent = extent;
this.iterator = iterator;
}
@Override
public Operation resume() throws WorldEditException {
while (iterator.hasNext()) {
Map.Entry<BlockVector, BaseBlock> entry = iterator.next();
extent.setBlock(entry.getKey(), entry.getValue(), true);
}
return null;
}
@Override
public void cancel() {
}
}