diff --git a/src/EditSession.java b/src/EditSession.java index 1a7693ae2..bf6d56b0c 100644 --- a/src/EditSession.java +++ b/src/EditSession.java @@ -19,11 +19,18 @@ import java.util.Map; import java.util.HashMap; +import java.util.HashSet; import com.sk89q.worldedit.*; /** + * This class can wrap all block editing operations into one "edit session" that + * stores the state of the blocks before modification. This allows for easy + * undo or redo. In addition to that, this class can use a "queue mode" that + * will know how to handle some special types of items such as signs and + * torches. For example, torches must be placed only after there is already + * a block below it, otherwise the torch will be placed as an item. * - * @author Albert + * @author sk89q */ public class EditSession { /** @@ -34,12 +41,38 @@ public class EditSession { * Stores the current blocks. */ private HashMap,Integer> current = new HashMap,Integer>(); + /** + * Queue. + */ + private HashMap,Integer> queue = new HashMap,Integer>(); /** * The maximum number of blocks to change at a time. If this number is * exceeded, a MaxChangedBlocksException exception will be * raised. -1 indicates no limit. */ private int maxBlocks = -1; + /** + * Indicates whether some types of blocks should be queued for best + * reproduction. + */ + private boolean queued = false; + /** + * List of object types to queue. + */ + private static HashSet queuedBlocks = new HashSet(); + + static { + queuedBlocks.add(50); // Torch + queuedBlocks.add(37); // Yellow flower + queuedBlocks.add(38); // Red rose + queuedBlocks.add(39); // Brown mushroom + queuedBlocks.add(40); // Red mushroom + queuedBlocks.add(59); // Crops + queuedBlocks.add(63); // Sign + queuedBlocks.add(75); // Redstone torch (off) + queuedBlocks.add(76); // Redstone torch (on) + queuedBlocks.add(84); // Reed + } /** * Default constructor. There is no maximum blocks limit. @@ -71,17 +104,20 @@ public class EditSession { } /** - * Sets the block at position x, y, z with a block type. + * Sets the block at position x, y, z with a block type. If queue mode is + * enabled, blocks may not be actually set in world until flushQueue() + * is called. * * @param x * @param y * @param z * @param blockType - * @return Whether the block changed + * @return Whether the block changed -- not entirely dependable */ public boolean setBlock(int x, int y, int z, int blockType) throws MaxChangedBlocksException { Point pt = new Point(x, y, z); + if (!original.containsKey(pt)) { original.put(pt, getBlock(x, y, z)); @@ -89,7 +125,35 @@ public class EditSession { throw new MaxChangedBlocksException(maxBlocks); } } + current.put(pt, blockType); + + return smartSetBlock(x, y, z, blockType); + } + + /** + * Actually set the block. Will use queue. + * + * @param x + * @param y + * @param z + * @param blockType + * @return + */ + private boolean smartSetBlock(int x, int y, int z, int blockType) { + Point pt = new Point(x, y, z); + + if (queued) { + if (blockType != 0 && queuedBlocks.contains(blockType) + && rawGetBlock(x, y - 1, z) == 0) { + queue.put(pt, blockType); + return getBlock(x, y, z) != blockType; + } else if (blockType == 0 + && queuedBlocks.contains(rawGetBlock(x, y + 1, z))) { + rawSetBlock(x, y + 1, z, 0); // Prevent items from being dropped + } + } + return rawSetBlock(x, y, z, blockType); } @@ -102,6 +166,26 @@ public class EditSession { * @return Block type */ public int getBlock(int x, int y, int z) { + // In the case of the queue, the block may have not actually been + // changed yet + if (queued) { + Point pt = new Point(x, y, z); + if (current.containsKey(pt)) { + return current.get(pt); + } + } + return etc.getMCServer().e.a(x, y, z); + } + + /** + * Gets the block type at a position x, y, z. + * + * @param x + * @param y + * @param z + * @return Block type + */ + public int rawGetBlock(int x, int y, int z) { return etc.getMCServer().e.a(x, y, z); } @@ -111,9 +195,10 @@ public class EditSession { public void undo() { for (Map.Entry,Integer> entry : original.entrySet()) { Point pt = (Point)entry.getKey(); - rawSetBlock((Integer)pt.getX(), (Integer)pt.getY(),(Integer)pt.getZ(), - (int)entry.getValue()); + smartSetBlock((Integer)pt.getX(), (Integer)pt.getY(),(Integer)pt.getZ(), + (int)entry.getValue()); } + flushQueue(); } /** @@ -122,9 +207,10 @@ public class EditSession { public void redo() { for (Map.Entry,Integer> entry : current.entrySet()) { Point pt = (Point)entry.getKey(); - rawSetBlock((Integer)pt.getX(), (Integer)pt.getY(),(Integer)pt.getZ(), - (int)entry.getValue()); + smartSetBlock((Integer)pt.getX(), (Integer)pt.getY(),(Integer)pt.getZ(), + (int)entry.getValue()); } + flushQueue(); } /** @@ -156,4 +242,43 @@ public class EditSession { } this.maxBlocks = maxBlocks; } + + /** + * Returns queue status. + * + * @return + */ + public boolean isQueueEnabled() { + return queued; + } + + /** + * Queue certain types of block for better reproduction of those blocks. + */ + public void enableQueue() { + queued = true; + } + + /** + * Disable the queue. This will flush the queue. + */ + public void disableQueue() { + if (queued != false) { + flushQueue(); + } + queued = false; + } + + /** + * Finish off the queue. + */ + public void flushQueue() { + if (!queued) { return; } + + for (Map.Entry,Integer> entry : queue.entrySet()) { + Point pt = (Point)entry.getKey(); + rawSetBlock((Integer)pt.getX(), (Integer)pt.getY(),(Integer)pt.getZ(), + (int)entry.getValue()); + } + } } diff --git a/src/WorldEdit.java b/src/WorldEdit.java index 80f96cba2..e94c5ec2b 100644 --- a/src/WorldEdit.java +++ b/src/WorldEdit.java @@ -271,11 +271,13 @@ public class WorldEdit extends Plugin { WorldEditSession session = getSession(player); EditSession editSession = new EditSession(session.getBlockChangeLimit()); + editSession.enableQueue(); try { return performCommand(player, session, editSession, split); } finally { session.remember(editSession); + editSession.flushQueue(); } } } else { @@ -284,6 +286,7 @@ public class WorldEdit extends Plugin { WorldEditSession session = getSession(player); EditSession editSession = new EditSession(session.getBlockChangeLimit()); + editSession.enableQueue(); String filename = split[0].substring(1) + ".js"; String[] args = new String[split.length - 1]; @@ -295,6 +298,7 @@ public class WorldEdit extends Plugin { return false; } finally { session.remember(editSession); + editSession.flushQueue(); } } } @@ -772,7 +776,7 @@ public class WorldEdit extends Plugin { throw new NoSuchScriptException(); } else if (!f.exists()) { throw new NoSuchScriptException(); - } else { + } else { // Read file StringBuffer buffer = new StringBuffer(); FileInputStream stream = new FileInputStream(f);