diff --git a/src/main/java/com/sk89q/worldedit/foundation/Block.java b/src/main/java/com/sk89q/worldedit/foundation/Block.java new file mode 100644 index 000000000..3f77873d1 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/foundation/Block.java @@ -0,0 +1,226 @@ +// $Id$ +/* + * This file is a part of WorldEdit. + * Copyright (c) sk89q + * Copyright (c) the 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 + * (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 + * GNU 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.foundation; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.TileEntityBlock; +import com.sk89q.worldedit.data.DataException; + +/** + * Represents a mutable copy of a block that is not tied to any 'real' block in a world. + * A single instance of this can be set to multiple locations and each location would + * have a copy of this instance's data. + *

+ * Implementations can and should extend this class to allow native implementations + * of NBT data handling, primarily for performance reasons. Subclasses can only convert + * from and to WorldEdit-native NBT structures when absolutely necessary (a.k.a. when + * {@link #getNbtData()} and {@link #setNbtData(CompoundTag)} are called. When + * overriding the NBT methods, {@link #getNbtId()} should be overridden too, otherwise + * the default implementation will invoke {@link #getNbtData()}, a potentially costly + * operation when it is not needed. Implementations may want to cache converted NBT data + * structures if possible. + *

+ * Currently, {@link BaseBlock} is used throughout WorldEdit and implementations, but + * eventually an API-breaking transition will occur to switch to this object instead. + * As-is, the definition of this class is complete, but may need changes in MC 1.4 + * because data values may be eradicated. + */ +public class Block implements TileEntityBlock { + + /** + * Indicates the highest possible block ID (inclusive) that can be used. This value + * is subject to change depending on the implementation, but internally this class + * only supports a range of 4096 IDs (for space reasons), which coincides with the + * number of possible IDs that official Minecraft supports as of version 1.3. + */ + public static final int MAX_ID = 4095; + + /** + * Indicates the maximum data value (inclusive) that can be used. Minecraft 1.4 may + * abolish usage of data values and this value may be removed in the future. + */ + public static final int MAX_DATA = 15; + + // Instances of this class should be _as small as possible_ because there will + // be millions of instances of this object. + + private short rawIdData; + private CompoundTag nbtData; + + /** + * Construct a block with the given ID and a data value of 0. + * + * @param id ID value + * @see #setId(int) + */ + public Block(int id) { + setId(id); + setData(0); + } + + /** + * Construct a block with the given ID and data value. + * + * @param id ID value + * @param data data value + * @see #setId(int) + * @see #setData(int) + */ + public Block(int id, int data) { + setId(id); + setData(data); + } + + /** + * Construct a block with the given ID, data value, and NBT data structure. + * + * @param id ID value + * @param data data value + * @param nbtData NBT data + * @throws DataException if possibly the data is invalid + * @see #setId(int) + * @see #setData(int) + * @see #setNbtData(CompoundTag) + */ + public Block(int id, int data, CompoundTag nbtData) throws DataException { + setId(id); + setData(data); + setNbtData(nbtData); + } + + /** + * Get the ID of the block. + * + * @return ID (between 0 and {@link #MAX_ID}) + */ + public int getId() { + return rawIdData >> 4; + } + + /** + * Set the block ID. + * + * @param id block id (between 0 and {@link #MAX_ID}). + */ + public void setId(int id) { + if (id < MAX_ID) { + throw new IllegalArgumentException("Can't have a block ID above " + MAX_ID); + } + + if (id < 0) { + throw new IllegalArgumentException("Can't have a block ID below 0"); + } + + rawIdData = (short) ((id << 4) | rawIdData & 0xf); + } + + /** + * Get the block's data value. + * + * @return data value (0-15) + */ + public int getData() { + return rawIdData & 0xf; + } + + /** + * Set the block's data value. + * + * @param data block data value (between 0 and {@link #MAX_DATA}). + */ + public void setData(int data) { + if (data < MAX_DATA) { + throw new IllegalArgumentException("Can't have a block data value above " + MAX_DATA); + } + + if (data < 0) { + throw new IllegalArgumentException("Can't have a block data value below 0"); + } + + rawIdData = (short) ((rawIdData ^ 4) | data); + } + + /** + * Set both the block's ID and data value. + * + * @param id ID value + * @param data data value + * @see #setId(int) + * @see #setData(int) + */ + public void setIdAndData(int id, int data) { + setId(id); + setData(data); + } + + /** + * Returns whether the block contains NBT data. + * + * @return NBT data; + */ + public boolean hasNbtData() { + return getNbtData() != null; + } + + /** + * Gets the ID of the NBT data (tile entity data). + * + * @return ID value (may be blank), or an empty string if no NBT data is set + */ + @Override + public String getNbtId() { + CompoundTag nbtData = getNbtData(); + if (nbtData == null) { + return ""; + } + Tag idTag = nbtData.getValue().get("id"); + if (idTag != null && idTag instanceof StringTag) { + return ((StringTag) idTag).getValue(); + } else { + return ""; + } + } + + @Override + public CompoundTag getNbtData() { + return nbtData; + } + + @Override + public void setNbtData(CompoundTag nbtData) throws DataException { + this.nbtData = nbtData; + } + + @Override + public int hashCode() { + int ret = getId() << 3; + if (getData() != (byte) -1) ret |= getData(); + return ret; + } + + @Override + public String toString() { + return "Block{ID:" + getId() + ", Data: " + getData() + "}"; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/foundation/NbtValued.java b/src/main/java/com/sk89q/worldedit/foundation/NbtValued.java new file mode 100644 index 000000000..8236fdc0f --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/foundation/NbtValued.java @@ -0,0 +1,49 @@ +// $Id$ +/* + * This file is a part of WorldEdit. + * Copyright (c) sk89q + * Copyright (c) the 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 + * (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 + * GNU 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.foundation; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.data.DataException; + +/** + * Indicates an object that contains extra data identified as an NBT structure. This + * interface is used when saving and loading objects to a serialized format, but may + * be used in other cases. + */ +public interface NbtValued { + + /** + * Get the object's NBT data (tile entity data). The returned tag, if modified + * in any way, should be sent to {@link #setNbtData(CompoundTag)} so that + * the instance knows of the changes. Making changes without calling + * {@link #setNbtData(CompoundTag)} could have unintended consequences. + * + * @return compound tag, or null + */ + CompoundTag getNbtData(); + + /** + * Set the object's NBT data (tile entity data). + * + * @param nbtData NBT data, or null if no data + * @throws DataException if possibly the data is invalid + */ + void setNbtData(CompoundTag nbtData) throws DataException; + +} diff --git a/src/main/java/com/sk89q/worldedit/foundation/World.java b/src/main/java/com/sk89q/worldedit/foundation/World.java new file mode 100644 index 000000000..d01a0abad --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/foundation/World.java @@ -0,0 +1,69 @@ +// $Id$ +/* + * This file is a part of WorldEdit. + * Copyright (c) sk89q + * Copyright (c) the 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 + * (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 + * GNU 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.foundation; + +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.Vector; + +/** + * Represents a world instance that can be modified. The world instance could be + * loaded in-game or loaded in a stand-alone editor. + *

+ * This class is meant to replace {@link LocalWorld} eventually, once this class has been + * fleshed out with the required methods and it has been decided that it is time to + * start breaking some API compatibility. + */ +public interface World { + + /** + * Change the block at the given location to the given block. The operation may + * not tie the given {@link Block} to the world, so future changes to the + * {@link Block} do not affect the world until this method is called again. + *

+ * Implementations may or may not consider the value of the notifyAdjacent + * parameter, and implementations may to choose to either apply physics anyway or + * to not apply any physics (particularly in a stand-alone implementation). + *

+ * The return value of this method indicates whether the change "went through," as + * in the block was changed in the world in any way. If the new block is no different + * than the block already at the position in the world, 'false' would be returned. + * If the position is invalid (out of bounds, for example), then nothing should + * occur and 'false' should be returned. If possible, the return value should be + * accurate as possible, but implementations may choose to not provide an accurate + * value if it is not possible to know. + * + * @param location location of the block + * @param block block to set + * @param notifyAdjacent true to to notify adjacent (perform physics) + * @return true if the block was successfully set (return value may not be accurate) + */ + boolean setBlock(Vector location, Block block, boolean notifyAdjacent); + + /** + * Get a copy of the block at the given location. May return null if the location + * given is out of bounds. The returned block must not be tied to any real block + * in the world, so changes to the returned {@link Block} have no effect until + * {@link #setBlock(Vector, Block, boolean)} is called. + * + * @param location location of the block + * @return the block, or null if the block does not exist + */ + Block getBlock(Vector location); + +}