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()); } }