From fa24eb60faa7c6d202a0c0e975579b29ca8e1dc5 Mon Sep 17 00:00:00 2001
From: sk89q
Date: Mon, 31 Mar 2014 18:22:44 -0700
Subject: [PATCH] Added getLazyBlock() to Extent for performance.
---
.../java/com/sk89q/worldedit/EditSession.java | 23 +++-
.../java/com/sk89q/worldedit/LocalWorld.java | 7 ++
.../com/sk89q/worldedit/blocks/LazyBlock.java | 105 ++++++++++++++++++
.../sk89q/worldedit/bukkit/BukkitWorld.java | 97 +++++-----------
.../com/sk89q/worldedit/extent/Extent.java | 33 ++++--
.../worldedit/extent/ExtentDelegate.java | 13 +--
.../extent/reorder/SimpleBlockReorder.java | 10 +-
.../com/sk89q/worldedit/foundation/Block.java | 36 ++++--
.../function/mask/ExistingBlockMask.java | 2 +-
.../function/mask/FuzzyBlockMask.java | 3 +-
.../function/mask/SolidBlockMask.java | 4 +-
11 files changed, 224 insertions(+), 109 deletions(-)
create mode 100644 src/main/java/com/sk89q/worldedit/blocks/LazyBlock.java
diff --git a/src/main/java/com/sk89q/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java
index 0ddf22cac..de74e7608 100644
--- a/src/main/java/com/sk89q/worldedit/EditSession.java
+++ b/src/main/java/com/sk89q/worldedit/EditSession.java
@@ -295,17 +295,36 @@ public class EditSession implements Extent {
return changeSet.size();
}
+ @Override
+ public BaseBlock getLazyBlock(Vector position) {
+ return world.getLazyBlock(position);
+ }
+
@Override
public BaseBlock getBlock(Vector position) {
return world.getBlock(position);
}
- @Override
+ /**
+ * 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);
}
- @Override
+ /**
+ * 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);
}
diff --git a/src/main/java/com/sk89q/worldedit/LocalWorld.java b/src/main/java/com/sk89q/worldedit/LocalWorld.java
index 433ec7fd0..3e4571bfc 100644
--- a/src/main/java/com/sk89q/worldedit/LocalWorld.java
+++ b/src/main/java/com/sk89q/worldedit/LocalWorld.java
@@ -93,6 +93,7 @@ public abstract class LocalWorld implements World, Extent {
* @param pt
* @return
*/
+ @Deprecated
public abstract int getBlockType(Vector pt);
/**
@@ -162,6 +163,7 @@ public abstract class LocalWorld implements World, Extent {
* @param pt
* @return
*/
+ @Deprecated
public abstract int getBlockData(Vector pt);
/**
@@ -548,6 +550,11 @@ public abstract class LocalWorld implements World, Extent {
return successful;
}
+ @Override
+ public BaseBlock getLazyBlock(Vector position) {
+ return getBlock(position);
+ }
+
@Override
public BaseBlock getBlock(Vector pt) {
checkLoadedChunk(pt);
diff --git a/src/main/java/com/sk89q/worldedit/blocks/LazyBlock.java b/src/main/java/com/sk89q/worldedit/blocks/LazyBlock.java
new file mode 100644
index 000000000..551592923
--- /dev/null
+++ b/src/main/java/com/sk89q/worldedit/blocks/LazyBlock.java
@@ -0,0 +1,105 @@
+/*
+ * 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 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 .
+ */
+
+package com.sk89q.worldedit.blocks;
+
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.data.DataException;
+import com.sk89q.worldedit.extent.Extent;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A implementation of a lazy block for {@link Extent#getLazyBlock(Vector)}
+ * that takes the block's ID and metadata, but will defer loading of NBT
+ * data until time of access.
+ *
+ * NBT data is later loaded using a call to {@link Extent#getBlock(Vector)}
+ * with a stored {@link Extent} and location.
+ *
+ * All mutators on this object will throw an
+ * {@link UnsupportedOperationException}.
+ */
+public class LazyBlock extends BaseBlock {
+
+ private final Extent extent;
+ private final Vector position;
+ private boolean loaded = false;
+
+ /**
+ * Create a new lazy block.
+ *
+ * @param type the block type
+ * @param extent the extent to later load the full block data from
+ * @param position the position to later load the full block data from
+ */
+ public LazyBlock(int type, Extent extent, Vector position) {
+ super(type);
+ checkNotNull(extent);
+ checkNotNull(position);
+ this.extent = extent;
+ this.position = position;
+ }
+
+ /**
+ * Create a new lazy block.
+ *
+ * @param type the block type
+ * @param data the data value
+ * @param extent the extent to later load the full block data from
+ * @param position the position to later load the full block data from
+ */
+ public LazyBlock(int type, int data, Extent extent, Vector position) {
+ super(type, data);
+ checkNotNull(extent);
+ checkNotNull(position);
+ this.extent = extent;
+ this.position = position;
+ }
+
+ @Override
+ public void setId(int id) {
+ throw new UnsupportedOperationException("This object is immutable");
+ }
+
+ @Override
+ public void setData(int data) {
+ throw new UnsupportedOperationException("This object is immutable");
+ }
+
+ @Override
+ public CompoundTag getNbtData() {
+ if (!loaded) {
+ BaseBlock loadedBlock = extent.getBlock(position);
+ try {
+ super.setNbtData(loadedBlock.getNbtData());
+ } catch (DataException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return super.getNbtData();
+ }
+
+ @Override
+ public void setNbtData(CompoundTag nbtData) throws DataException {
+ throw new UnsupportedOperationException("This object is immutable");
+ }
+
+}
diff --git a/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java
index c7bf76097..a53cd12d0 100644
--- a/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java
+++ b/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java
@@ -19,83 +19,33 @@
package com.sk89q.worldedit.bukkit;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.InputStream;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.bukkit.Bukkit;
-import org.bukkit.Effect;
+import com.sk89q.worldedit.*;
+import com.sk89q.worldedit.EntityType;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.blocks.*;
+import com.sk89q.worldedit.blocks.ContainerBlock;
+import com.sk89q.worldedit.blocks.NoteBlock;
+import com.sk89q.worldedit.bukkit.entity.BukkitEntity;
+import com.sk89q.worldedit.regions.Region;
+import com.sk89q.worldedit.util.TreeGenerator;
+import org.bukkit.*;
import org.bukkit.Location;
-import org.bukkit.Material;
-import org.bukkit.SkullType;
-import org.bukkit.TreeType;
-import org.bukkit.World;
-import org.bukkit.block.Biome;
-import org.bukkit.block.Block;
-import org.bukkit.block.BlockFace;
-import org.bukkit.block.BlockState;
-import org.bukkit.block.Chest;
-import org.bukkit.block.CreatureSpawner;
-import org.bukkit.block.Furnace;
-import org.bukkit.block.Sign;
-import org.bukkit.block.Skull;
+import org.bukkit.block.*;
import org.bukkit.enchantments.Enchantment;
-import org.bukkit.entity.Ambient;
-import org.bukkit.entity.Animals;
-import org.bukkit.entity.Boat;
-import org.bukkit.entity.Entity;
-import org.bukkit.entity.ExperienceOrb;
-import org.bukkit.entity.FallingBlock;
-import org.bukkit.entity.Golem;
-import org.bukkit.entity.Hanging;
-import org.bukkit.entity.HumanEntity;
-import org.bukkit.entity.Item;
-import org.bukkit.entity.ItemFrame;
-import org.bukkit.entity.LivingEntity;
-import org.bukkit.entity.Minecart;
-import org.bukkit.entity.Painting;
-import org.bukkit.entity.Projectile;
-import org.bukkit.entity.TNTPrimed;
-import org.bukkit.entity.Tameable;
-import org.bukkit.entity.Villager;
+import org.bukkit.entity.*;
import org.bukkit.inventory.DoubleChestInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
-import com.sk89q.worldedit.BiomeType;
-import com.sk89q.worldedit.BlockVector2D;
-import com.sk89q.worldedit.EditSession;
-import com.sk89q.worldedit.EntityType;
-import com.sk89q.worldedit.LocalEntity;
-import com.sk89q.worldedit.LocalWorld;
-import com.sk89q.worldedit.Vector;
-import com.sk89q.worldedit.Vector2D;
-import com.sk89q.worldedit.WorldEdit;
-import com.sk89q.worldedit.blocks.BaseBlock;
-import com.sk89q.worldedit.blocks.BaseItemStack;
-import com.sk89q.worldedit.blocks.BlockID;
-import com.sk89q.worldedit.blocks.ContainerBlock;
-import com.sk89q.worldedit.blocks.FurnaceBlock;
-import com.sk89q.worldedit.blocks.MobSpawnerBlock;
-import com.sk89q.worldedit.blocks.NoteBlock;
-import com.sk89q.worldedit.blocks.SignBlock;
-import com.sk89q.worldedit.blocks.SkullBlock;
-import com.sk89q.worldedit.bukkit.entity.BukkitEntity;
-import com.sk89q.worldedit.regions.Region;
-import com.sk89q.worldedit.util.TreeGenerator;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
public class BukkitWorld extends LocalWorld {
@@ -1355,6 +1305,13 @@ public class BukkitWorld extends LocalWorld {
return super.getBlock(pt);
}
+ @SuppressWarnings("deprecation")
+ @Override
+ public BaseBlock getLazyBlock(Vector position) {
+ Block bukkitBlock = world.getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ());
+ return new LazyBlock(bukkitBlock.getTypeId(), bukkitBlock.getData(), this, position);
+ }
+
@Override
public boolean setBlock(Vector pt, com.sk89q.worldedit.foundation.Block block, boolean notifyAdjacent) {
if (!skipNmsSafeSet) {
diff --git a/src/main/java/com/sk89q/worldedit/extent/Extent.java b/src/main/java/com/sk89q/worldedit/extent/Extent.java
index 999ca6b20..dd052e0de 100644
--- a/src/main/java/com/sk89q/worldedit/extent/Extent.java
+++ b/src/main/java/com/sk89q/worldedit/extent/Extent.java
@@ -47,27 +47,36 @@ public interface Extent {
* Calls to this method can actually be quite expensive, so cache results
* whenever it is possible, while being aware of the mutability aspect.
* The cost, however, depends on the implementation and particular extent.
+ * If only basic information about the block is required, then use of
+ * {@link #getLazyBlock(Vector)} is recommended.
*
* @param position position of the block
- * @return the block, or null if the block does not exist
+ * @return the block
*/
BaseBlock getBlock(Vector position);
/**
- * Get the block ID at the given location.
+ * Get a lazy, immutable snapshot of the block at the given location that only
+ * immediately contains information about the block's type (and metadata).
+ *
+ * Further information (such as NBT data) will be available by the
+ * time of access. Therefore, it is not recommended that
+ * this method is used if the world is being simulated at the time of
+ * call. If the block needs to be stored for future use, then this method should
+ * definitely not be used. Moreover, the block that is returned is immutable (or
+ * should be), and therefore modifications should not be attempted on it. If a
+ * modifiable copy is required, then the block should be cloned.
+ *
+ * This method exists because it is sometimes important to inspect the block
+ * at a given location, but {@link #getBlock(Vector)} may be too expensive in
+ * the underlying implementation. It is also not possible to implement
+ * caching if the returned object is mutable, so this methods allows caching
+ * implementations to be used.
*
* @param position position of the block
- * @return the block ID
+ * @return the block
*/
- int getBlockType(Vector position);
-
- /**
- * Get the data value of the block at the given location.
- *
- * @param position position of the block
- * @return the block data value
- */
- int getBlockData(Vector position);
+ BaseBlock getLazyBlock(Vector position);
/**
* Change the block at the given location to the given block. The operation may
diff --git a/src/main/java/com/sk89q/worldedit/extent/ExtentDelegate.java b/src/main/java/com/sk89q/worldedit/extent/ExtentDelegate.java
index 17b5801bd..19d1688e5 100644
--- a/src/main/java/com/sk89q/worldedit/extent/ExtentDelegate.java
+++ b/src/main/java/com/sk89q/worldedit/extent/ExtentDelegate.java
@@ -56,18 +56,13 @@ public class ExtentDelegate implements Extent {
}
@Override
- public BaseBlock getBlock(Vector location) {
- return extent.getBlock(location);
+ public BaseBlock getBlock(Vector position) {
+ return extent.getBlock(position);
}
@Override
- public int getBlockType(Vector location) {
- return extent.getBlockType(location);
- }
-
- @Override
- public int getBlockData(Vector location) {
- return extent.getBlockData(location);
+ public BaseBlock getLazyBlock(Vector position) {
+ return extent.getLazyBlock(position);
}
@Override
diff --git a/src/main/java/com/sk89q/worldedit/extent/reorder/SimpleBlockReorder.java b/src/main/java/com/sk89q/worldedit/extent/reorder/SimpleBlockReorder.java
index 18f9a919c..8a08871d0 100644
--- a/src/main/java/com/sk89q/worldedit/extent/reorder/SimpleBlockReorder.java
+++ b/src/main/java/com/sk89q/worldedit/extent/reorder/SimpleBlockReorder.java
@@ -83,6 +83,8 @@ public class SimpleBlockReorder extends ExtentDelegate {
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
+ BaseBlock lazyBlock = getLazyBlock(location);
+
if (!enabled) {
return super.setBlock(location, block);
}
@@ -90,18 +92,18 @@ public class SimpleBlockReorder extends ExtentDelegate {
if (BlockType.shouldPlaceLast(block.getType())) {
// Place torches, etc. last
stage2.put(location.toBlockVector(), block);
- return !(getBlockType(location) == block.getType() && getBlockData(location) == block.getData());
+ return !(lazyBlock.getType() == block.getType() && lazyBlock.getData() == 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))) {
+ return !(lazyBlock.getType() == block.getType() && lazyBlock.getData() == block.getData());
+ } else if (BlockType.shouldPlaceLast(lazyBlock.getType())) {
// Destroy torches, etc. first
super.setBlock(location, new BaseBlock(BlockID.AIR));
return super.setBlock(location, block);
} else {
stage1.put(location.toBlockVector(), block);
- return !(getBlockType(location) == block.getType() && getBlockData(location) == block.getData());
+ return !(lazyBlock.getType() == block.getType() && lazyBlock.getData() == block.getData());
}
}
diff --git a/src/main/java/com/sk89q/worldedit/foundation/Block.java b/src/main/java/com/sk89q/worldedit/foundation/Block.java
index 464ad2173..c3ffcabe9 100644
--- a/src/main/java/com/sk89q/worldedit/foundation/Block.java
+++ b/src/main/java/com/sk89q/worldedit/foundation/Block.java
@@ -74,8 +74,8 @@ public class Block implements TileEntityBlock {
* @see #setId(int)
*/
public Block(int id) {
- setId(id);
- setData(0);
+ internalSetId(id);
+ internalSetData(0);
}
/**
@@ -87,8 +87,8 @@ public class Block implements TileEntityBlock {
* @see #setData(int)
*/
public Block(int id, int data) {
- setId(id);
- setData(data);
+ internalSetId(id);
+ internalSetData(data);
}
/**
@@ -116,13 +116,13 @@ public class Block implements TileEntityBlock {
public int getId() {
return id;
}
-
+
/**
* Set the block ID.
- *
+ *
* @param id block id (between 0 and {@link #MAX_ID}).
*/
- public void setId(int id) {
+ protected final void internalSetId(int id) {
if (id > MAX_ID) {
throw new IllegalArgumentException("Can't have a block ID above "
+ MAX_ID + " (" + id + " given)");
@@ -131,9 +131,18 @@ public class Block implements TileEntityBlock {
if (id < 0) {
throw new IllegalArgumentException("Can't have a block ID below 0");
}
-
+
this.id = (short) id;
}
+
+ /**
+ * Set the block ID.
+ *
+ * @param id block id (between 0 and {@link #MAX_ID}).
+ */
+ public void setId(int id) {
+ internalSetId(id);
+ }
/**
* Get the block's data value.
@@ -149,7 +158,7 @@ public class Block implements TileEntityBlock {
*
* @param data block data value (between 0 and {@link #MAX_DATA}).
*/
- public void setData(int data) {
+ protected final void internalSetData(int data) {
if (data > MAX_DATA) {
throw new IllegalArgumentException(
"Can't have a block data value above " + MAX_DATA + " ("
@@ -162,6 +171,15 @@ public class Block implements TileEntityBlock {
this.data = (short) data;
}
+
+ /**
+ * Set the block's data value.
+ *
+ * @param data block data value (between 0 and {@link #MAX_DATA}).
+ */
+ public void setData(int data) {
+ internalSetData(data);
+ }
/**
* Set both the block's ID and data value.
diff --git a/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java b/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java
index 47d11a177..f6ec67458 100644
--- a/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java
+++ b/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java
@@ -40,7 +40,7 @@ public class ExistingBlockMask extends AbstractExtentMask {
@Override
public boolean test(Vector vector) {
- return getExtent().getBlockType(vector) != BlockID.AIR;
+ return getExtent().getLazyBlock(vector).getType() != BlockID.AIR;
}
}
diff --git a/src/main/java/com/sk89q/worldedit/function/mask/FuzzyBlockMask.java b/src/main/java/com/sk89q/worldedit/function/mask/FuzzyBlockMask.java
index b08d53cb9..e015c7d12 100644
--- a/src/main/java/com/sk89q/worldedit/function/mask/FuzzyBlockMask.java
+++ b/src/main/java/com/sk89q/worldedit/function/mask/FuzzyBlockMask.java
@@ -39,7 +39,8 @@ public class FuzzyBlockMask extends BlockMask {
public boolean test(Vector vector) {
Extent extent = getExtent();
Collection blocks = getBlocks();
- BaseBlock compare = new BaseBlock(extent.getBlockType(vector), extent.getBlockData(vector));
+ BaseBlock lazyBlock = extent.getLazyBlock(vector);
+ BaseBlock compare = new BaseBlock(lazyBlock.getType(), lazyBlock.getData());
return BaseBlock.containsFuzzy(blocks, compare);
}
}
diff --git a/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java b/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java
index 8769236c0..c23e5b50f 100644
--- a/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java
+++ b/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java
@@ -19,6 +19,7 @@
package com.sk89q.worldedit.function.mask;
+import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BlockType;
@@ -32,7 +33,8 @@ public class SolidBlockMask extends AbstractExtentMask {
@Override
public boolean test(Vector vector) {
Extent extent = getExtent();
- return !BlockType.canPassThrough(extent.getBlockType(vector), extent.getBlockData(vector));
+ BaseBlock lazyBlock = extent.getLazyBlock(vector);
+ return !BlockType.canPassThrough(lazyBlock.getType(), lazyBlock.getData());
}
}