// $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.bukkit;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import net.minecraft.server.NBTBase;
import net.minecraft.server.NBTTagByte;
import net.minecraft.server.NBTTagByteArray;
import net.minecraft.server.NBTTagCompound;
import net.minecraft.server.NBTTagDouble;
import net.minecraft.server.NBTTagEnd;
import net.minecraft.server.NBTTagFloat;
import net.minecraft.server.NBTTagInt;
import net.minecraft.server.NBTTagIntArray;
import net.minecraft.server.NBTTagList;
import net.minecraft.server.NBTTagLong;
import net.minecraft.server.NBTTagShort;
import net.minecraft.server.NBTTagString;
import net.minecraft.server.TileEntity;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.ByteTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.EndTag;
import com.sk89q.jnbt.FloatTag;
import com.sk89q.jnbt.IntArrayTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.LongTag;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.TileEntityBlock;
import com.sk89q.worldedit.data.DataException;
import com.sk89q.worldedit.foundation.Block;
/**
* A blind handler of blocks with TileEntity data that directly access Minecraft's
* classes through CraftBukkit.
*
* Usage of this class may break terribly in the future, and therefore usage should
* be trapped in a handler for {@link Throwable}.
*/
class NmsBlock extends BaseBlock implements TileEntityBlock {
private static final Logger logger = Logger.getLogger(NmsBlock.class.getCanonicalName());
private static Field compoundMapField;
private NBTTagCompound nbtData = null;
/**
* Create a new instance with a given type ID, data value, and previous
* {@link TileEntityBlock}-implementing object.
*
* @param type block type ID
* @param data data value
* @param tileEntityBlock tile entity block
*/
public NmsBlock(int type, int data, TileEntityBlock tileEntityBlock) {
super(type, data);
nbtData = (NBTTagCompound) fromNative(tileEntityBlock.getNbtData());
}
/**
* Create a new instance with a given type ID, data value, and raw
* {@link NBTTagCompound} copy.
*
* @param type block type ID
* @param data data value
* @param nbtData raw NBT data
*/
public NmsBlock(int type, int data, NBTTagCompound nbtData) {
super(type, data);
this.nbtData = nbtData;
}
/**
* Build a {@link NBTTagCompound} that has valid coordinates.
*
* @param pt coordinates to set
* @return the tag compound
*/
private NBTTagCompound getNmsData(Vector pt) {
if (nbtData == null) {
return null;
}
nbtData.set("x", new NBTTagInt("x", pt.getBlockX()));
nbtData.set("y", new NBTTagInt("y", pt.getBlockY()));
nbtData.set("z", new NBTTagInt("z", pt.getBlockZ()));
return nbtData;
}
@Override
public boolean hasNbtData() {
return nbtData != null;
}
@Override
public String getNbtId() {
if (nbtData == null) {
return "";
}
return nbtData.getString("id");
}
@Override
public CompoundTag getNbtData() {
if (nbtData == null) {
return new CompoundTag(getNbtId(),
new HashMap());
}
return (CompoundTag) toNative(nbtData);
}
@Override
public void setNbtData(CompoundTag tag) throws DataException {
if (tag == null) {
this.nbtData = null;
}
this.nbtData = (NBTTagCompound) fromNative(tag);
}
/**
* Build an instance from the given information.
*
* @param world world to get the block from
* @param position position to get the block at
* @param type type ID of block
* @param data data value of block
* @return the block, or null
*/
public static NmsBlock get(World world, Vector position, int type, int data) {
TileEntity te = ((CraftWorld) world).getHandle().getTileEntity(
position.getBlockX(), position.getBlockY(), position.getBlockZ());
if (te != null) {
NBTTagCompound tag = new NBTTagCompound();
te.b(tag); // Load data
return new NmsBlock(type, data, tag);
}
return null;
}
/**
* Set an instance or a {@link TileEntityBlock} to the given position.
*
* @param world world to set the block in
* @param position position to set the block at
* @param block the block to set
* @return true if tile entity data was copied to the world
*/
public static boolean set(World world, Vector position, BaseBlock block) {
NBTTagCompound data = null;
if (block instanceof NmsBlock) {
NmsBlock nmsProxyBlock = (NmsBlock) block;
data = nmsProxyBlock.getNmsData(position);
} else if (block instanceof TileEntityBlock) {
NmsBlock nmsProxyBlock = new NmsBlock(
block.getType(), block.getData(), block);
data = nmsProxyBlock.getNmsData(position);
}
if (data != null) {
TileEntity te = ((CraftWorld) world).getHandle().getTileEntity(
position.getBlockX(), position.getBlockY(), position.getBlockZ());
if (te != null) {
te.a(data); // Load data
return true;
}
}
return false;
}
/**
* Tries to set a block 'safely', as in setting the block data to the location, and
* then triggering physics only at the end.
*
* @param world world to set the block in
* @param position position to set the block at
* @param block the block to set
* @param notifyAdjacent true to notify physics and what not
* @return true if set
*/
public static boolean setSafely(BukkitWorld world, Vector position,
Block block, boolean notifyAdjacent) {
int x = position.getBlockX();
int y = position.getBlockY();
int z = position.getBlockZ();
CraftWorld craftWorld = ((CraftWorld) world.getWorld());
boolean successful = craftWorld.getHandle().setRawTypeIdAndData(
x, y, z, block.getId(), block.getData());
if (successful) {
if (block instanceof BaseBlock) {
world.copyToWorld(position, (BaseBlock) block);
}
if (notifyAdjacent) {
craftWorld.getHandle().update(x, y, z, block.getId());
} else {
craftWorld.getHandle().notify(x, y, z);
}
}
return successful;
}
/**
* Converts from a non-native NMS NBT structure to a native WorldEdit NBT
* structure.
*
* @param foreign non-native NMS NBT structure
* @return native WorldEdit NBT structure
*/
@SuppressWarnings("unchecked")
private static Tag toNative(NBTBase foreign) {
if (foreign == null) {
return null;
}
if (foreign instanceof NBTTagCompound) {
Map values = new HashMap();
Collection