diff --git a/src/main/java/com/sk89q/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java
index 829ca2412..487395cd1 100644
--- a/src/main/java/com/sk89q/worldedit/EditSession.java
+++ b/src/main/java/com/sk89q/worldedit/EditSession.java
@@ -19,17 +19,17 @@
package com.sk89q.worldedit;
-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.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.event.extent.EditSessionEvent;
-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.extent.*;
+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;
@@ -37,10 +37,11 @@ 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.Counter;
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.*;
@@ -53,6 +54,9 @@ 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.masks.Mask;
import com.sk89q.worldedit.math.interpolation.Interpolation;
import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
@@ -70,6 +74,7 @@ import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.TreeGenerator;
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 javax.annotation.Nullable;
@@ -105,6 +110,7 @@ public class EditSession implements Extent {
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;
@@ -160,6 +166,7 @@ public class EditSession implements Extent {
// This 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);
@@ -182,6 +189,7 @@ public class EditSession implements Extent {
this.bypassNone = extent;
} else {
Extent extent = new NullExtent();
+ extent = survivalExtent = new SurvivalModeExtent(extent, new NullWorld());
extent = blockBagExtent = new BlockBagExtent(extent, blockBag);
extent = reorderExtent = new MultiStageReorder(extent, false);
extent = maskingExtent = new MaskingExtent(extent, Masks.alwaysTrue());
@@ -287,6 +295,15 @@ public class EditSession implements Extent {
}
}
+ /**
+ * Get the {@link SurvivalModeExtent}.
+ *
+ * @return the survival simulation extent
+ */
+ public SurvivalModeExtent getSurvivalExtent() {
+ return survivalExtent;
+ }
+
/**
* Set whether fast mode is enabled.
*
diff --git a/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java b/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java
index 5de7d14e0..40f1e4472 100644
--- a/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java
+++ b/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java
@@ -57,18 +57,16 @@ public class AreaPickaxe implements BlockTool {
}
EditSession editSession = session.createEditSession(player);
+ editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop);
try {
for (int x = ox - range; x <= ox + range; ++x) {
for (int y = oy - range; y <= oy + range; ++y) {
for (int z = oz - range; z <= oz + range; ++z) {
Vector pos = new Vector(x, y, z);
- if (world.getBlockType(pos) != initialType) {
+ if (editSession.getBlockType(pos) != initialType) {
continue;
}
- if (config.superPickaxeManyDrop) {
- world.simulateBlockMine(pos);
- }
world.queueBlockBreakEffect(server, pos, initialType, clicked.distanceSq(pos));
@@ -79,6 +77,7 @@ public class AreaPickaxe implements BlockTool {
} catch (MaxChangedBlocksException e) {
player.printError("Max blocks change limit reached.");
} finally {
+ editSession.flushQueue();
session.remember(editSession);
}
diff --git a/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java b/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java
index f886c2e5f..09cbc4340 100644
--- a/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java
+++ b/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java
@@ -19,13 +19,13 @@
package com.sk89q.worldedit.command.tool;
-import java.util.HashSet;
-import java.util.Set;
-
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* A pickaxe mode that recursively finds adjacent blocks within range of
* an initial block and of the same type.
@@ -59,14 +59,15 @@ public class RecursivePickaxe implements BlockTool {
}
EditSession editSession = session.createEditSession(player);
+ editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop);
try {
recurse(server, editSession, world, clicked.toBlockVector(),
- clicked, range, initialType, new HashSet(),
- config.superPickaxeManyDrop);
+ clicked, range, initialType, new HashSet());
} catch (MaxChangedBlocksException e) {
player.printError("Max blocks change limit reached.");
} finally {
+ editSession.flushQueue();
session.remember(editSession);
}
@@ -77,7 +78,6 @@ public class RecursivePickaxe implements BlockTool {
* Helper method.
*
* @param server
- * @param superPickaxeManyDrop
* @param world
* @param pos
* @param origin
@@ -88,7 +88,7 @@ public class RecursivePickaxe implements BlockTool {
private static void recurse(ServerInterface server, EditSession editSession,
LocalWorld world, BlockVector pos,
Vector origin, double size, int initialType,
- Set visited, boolean drop)
+ Set visited)
throws MaxChangedBlocksException {
final double distanceSq = origin.distanceSq(pos);
@@ -102,26 +102,22 @@ public class RecursivePickaxe implements BlockTool {
return;
}
- if (drop) {
- world.simulateBlockMine(pos);
- }
-
world.queueBlockBreakEffect(server, pos, initialType, distanceSq);
editSession.setBlock(pos, air);
recurse(server, editSession, world, pos.add(1, 0, 0).toBlockVector(),
- origin, size, initialType, visited, drop);
+ origin, size, initialType, visited);
recurse(server, editSession, world, pos.add(-1, 0, 0).toBlockVector(),
- origin, size, initialType, visited, drop);
+ origin, size, initialType, visited);
recurse(server, editSession, world, pos.add(0, 0, 1).toBlockVector(),
- origin, size, initialType, visited, drop);
+ origin, size, initialType, visited);
recurse(server, editSession, world, pos.add(0, 0, -1).toBlockVector(),
- origin, size, initialType, visited, drop);
+ origin, size, initialType, visited);
recurse(server, editSession, world, pos.add(0, 1, 0).toBlockVector(),
- origin, size, initialType, visited, drop);
+ origin, size, initialType, visited);
recurse(server, editSession, world, pos.add(0, -1, 0).toBlockVector(),
- origin, size, initialType, visited, drop);
+ origin, size, initialType, visited);
}
}
diff --git a/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java b/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java
index f7b662a55..e01c73ea9 100644
--- a/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java
+++ b/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java
@@ -20,6 +20,7 @@
package com.sk89q.worldedit.command.tool;
import com.sk89q.worldedit.*;
+import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
/**
@@ -43,11 +44,16 @@ public class SinglePickaxe implements BlockTool {
return true;
}
- if (config.superPickaxeDrop) {
- world.simulateBlockMine(clicked);
- }
+ EditSession editSession = session.createEditSession(player);
+ editSession.getSurvivalExtent().setToolUse(config.superPickaxeDrop);
- world.setBlockType(clicked, BlockID.AIR);
+ try {
+ editSession.setBlock(clicked, new BaseBlock(BlockID.AIR));
+ } catch (MaxChangedBlocksException e) {
+ player.printError("Max blocks change limit reached.");
+ } finally {
+ editSession.flushQueue();
+ }
world.playEffect(clicked, 2001, blockType);
diff --git a/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java b/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java
new file mode 100644
index 000000000..bbf5b4699
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java
@@ -0,0 +1,92 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.extent.world;
+
+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.extent.AbstractDelegateExtent;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.world.World;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Makes changes to the world as if a player had done so during survival mode.
+ *
+ * Note that this extent may choose to not call the underlying
+ * extent and may instead call methods on the {@link World} that is passed
+ * in the constructor. For that reason, if you wish to "catch" changes, you
+ * should catch them before the changes reach this extent.
+ */
+public class SurvivalModeExtent extends AbstractDelegateExtent {
+
+ private final World world;
+ private boolean toolUse = false;
+
+ /**
+ * Create a new instance.
+ *
+ * @param extent the extent
+ * @param world the world
+ */
+ public SurvivalModeExtent(Extent extent, World world) {
+ super(extent);
+ checkNotNull(world);
+ this.world = world;
+ }
+
+ /**
+ * Return whether changes to the world should be simulated with the
+ * use of game tools (such as pickaxes) whenever possible and reasonable.
+ *
+ * For example, we could pretend that the act of setting a coal ore block
+ * to air (nothing) was the act of a player mining that coal ore block
+ * with a pickaxe, which would mean that a coal item would be dropped.
+ *
+ * @return true if tool use is to be simulated
+ */
+ public boolean hasToolUse() {
+ return toolUse;
+ }
+
+ /**
+ * Set whether changes to the world should be simulated with the
+ * use of game tools (such as pickaxes) whenever possible and reasonable.
+ *
+ * @param toolUse true if tool use is to be simulated
+ * @see #hasToolUse() for an explanation
+ */
+ public void setToolUse(boolean toolUse) {
+ this.toolUse = toolUse;
+ }
+
+ @Override
+ public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
+ if (toolUse && block.getType() == BlockID.AIR) {
+ world.simulateBlockMine(location);
+ return true;
+ } else {
+ return super.setBlock(location, block);
+ }
+ }
+
+}
diff --git a/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java b/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java
index 778b4d960..29aa34f78 100644
--- a/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java
+++ b/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java
@@ -138,15 +138,20 @@ public abstract class AbstractWorld implements World {
public void simulateBlockMine(Vector pt) {
BaseBlock block = getLazyBlock(pt);
BaseItemStack stack = BlockType.getBlockDrop(block.getId(), (short) block.getData());
- if (stack == null) {
- return;
+
+ if (stack != null) {
+ final int amount = stack.getAmount();
+ if (amount > 1) {
+ dropItem(pt, new BaseItemStack(stack.getType(), 1, stack.getData()), amount);
+ } else {
+ dropItem(pt, stack, amount);
+ }
}
- final int amount = stack.getAmount();
- if (amount > 1) {
- dropItem(pt, new BaseItemStack(stack.getType(), 1, stack.getData()), amount);
- } else {
- dropItem(pt, stack, amount);
+ try {
+ setBlock(pt, new BaseBlock(BlockID.AIR));
+ } catch (WorldEditException e) {
+ throw new RuntimeException(e);
}
}
diff --git a/src/main/java/com/sk89q/worldedit/world/NullWorld.java b/src/main/java/com/sk89q/worldedit/world/NullWorld.java
new file mode 100644
index 000000000..62edc3352
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/world/NullWorld.java
@@ -0,0 +1,98 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.world;
+
+import com.sk89q.worldedit.*;
+import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.blocks.BaseItemStack;
+import com.sk89q.worldedit.blocks.BlockID;
+import com.sk89q.worldedit.regions.Region;
+import com.sk89q.worldedit.util.TreeGenerator.TreeType;
+
+/**
+ * A null implementation of {@link World} that drops all changes and
+ * returns dummy data.
+ */
+public class NullWorld extends AbstractWorld {
+
+ @Override
+ public String getName() {
+ return "null";
+ }
+
+ @Override
+ public boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) throws WorldEditException {
+ return false;
+ }
+
+ @Override
+ public int getBlockLightLevel(Vector position) {
+ return 0;
+ }
+
+ @Override
+ public boolean clearContainerBlockContents(Vector position) {
+ return false;
+ }
+
+ @Override
+ public BiomeType getBiome(Vector2D position) {
+ return null;
+ }
+
+ @Override
+ public void setBiome(Vector2D position, BiomeType biome) {
+ }
+
+ @Override
+ public void dropItem(Vector position, BaseItemStack item) {
+ }
+
+ @Override
+ public int killMobs(Vector origin, double radius, int flags) {
+ return 0;
+ }
+
+ @Override
+ public int removeEntities(EntityType type, Vector origin, int radius) {
+ return 0;
+ }
+
+ @Override
+ public boolean regenerate(Region region, EditSession editSession) {
+ return false;
+ }
+
+ @Override
+ public boolean generateTree(TreeType type, EditSession editSession, Vector position) throws MaxChangedBlocksException {
+ return false;
+ }
+
+ @Override
+ public BaseBlock getBlock(Vector position) {
+ return new BaseBlock(BlockID.AIR);
+ }
+
+ @Override
+ public BaseBlock getLazyBlock(Vector position) {
+ return new BaseBlock(BlockID.AIR);
+ }
+
+}