Projects >> WorldEdit >>91e7042614cf15a47c9897e55b1883e37dd6d9b4

Chunk
Conflicting content
<<<<<<< HEAD:src/main/java/org/enginehub/worldedit/bukkit/DefaultNmsBlock.java
package org.enginehub.worldedit.bukkit;
// $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 .
 */

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.v1_5_R1.NBTBase;
import net.minecraft.server.v1_5_R1.NBTTagByte;
    }
import net.minecraft.server.v1_5_R1.NBTTagByteArray;
import net.minecraft.server.v1_5_R1.NBTTagCompound;
import net.minecraft.server.v1_5_R1.NBTTagDouble;
import net.minecraft.server.v1_5_R1.NBTTagEnd;
import net.minecraft.server.v1_5_R1.NBTTagFloat;
import net.minecraft.server.v1_5_R1.NBTTagInt;
import net.minecraft.server.v1_5_R1.NBTTagIntArray;
import net.minecraft.server.v1_5_R1.NBTTagList;
import net.minecraft.server.v1_5_R1.NBTTagLong;
import net.minecraft.server.v1_5_R1.NBTTagShort;
import net.minecraft.server.v1_5_R1.NBTTagString;
import net.minecraft.server.v1_5_R1.TileEntity;

import org.bukkit.World;
import org.bukkit.craftbukkit.v1_5_R1.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.WorldEdit;
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}. */ public class DefaultNmsBlock extends BukkitBlock { private static final Logger logger = WorldEdit.logger; private static Field compoundMapField; private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why. private NBTTagCompound nbtData = null; static { Field field; try { field = net.minecraft.server.v1_5_R1.Block.class.getDeclaredField("isTileEntity"); field.setAccessible(true); } catch (NoSuchFieldException e) { // logger.severe("Could not find NMS block tile entity field!"); field = null; } nmsBlock_isTileEntityField = field; } public static boolean verify() { return nmsBlock_isTileEntityField != 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 DefaultNmsBlock(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 DefaultNmsBlock(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 DefaultNmsBlock get(World world, Vector position, int type, int data) { if (!hasTileEntity(type)) { return null; } 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 DefaultNmsBlock(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 (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) { return false; } if (block instanceof DefaultNmsBlock) { DefaultNmsBlock nmsProxyBlock = (DefaultNmsBlock) block; data = nmsProxyBlock.getNmsData(position); } else if (block instanceof TileEntityBlock) { DefaultNmsBlock nmsProxyBlock = new DefaultNmsBlock( block.getId(), 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 block id or data was changed */ public static boolean setSafely(LoadedWorld world, Vector position, Block block, boolean notifyAdjacent) { int x = position.getBlockX(); int y = position.getBlockY(); int z = position.getBlockZ(); CraftWorld craftWorld = ((CraftWorld) world.getWorld()); // TileEntity te = craftWorld.getHandle().getTileEntity(x, y, z); // craftWorld.getHandle().tileEntityList.remove(te); boolean changed = craftWorld.getHandle().setRawTypeId(x, y, z, block.getId()); if (block instanceof BaseBlock) { world.copyToWorld(position, (BaseBlock) block); } changed = craftWorld.getHandle().setData(x, y, z, block.getData(), 2) || changed; if (changed) { if (notifyAdjacent) { craftWorld.getHandle().update(x, y, z, block.getId()); } else { craftWorld.getHandle().notify(x, y, z); } } return changed; } public static boolean hasTileEntity(int type) { net.minecraft.server.v1_5_R1.Block nmsBlock = getNmsBlock(type); if (nmsBlock == null) { return false; } try { return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast } catch (IllegalAccessException e) { return false; } } public static net.minecraft.server.v1_5_R1.Block getNmsBlock(int type) { if (type < 0 || type >= net.minecraft.server.v1_5_R1.Block.byId.length) { return null; } return net.minecraft.server.v1_5_R1.Block.byId[type]; } /** * 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 foreignValues = null; if (compoundMapField == null) { try { // Method name may change! foreignValues = ((NBTTagCompound) foreign).c(); } catch (Throwable t) { try { logger.warning("WorldEdit: Couldn't get NBTTagCompound.c(), " + "so we're going to try to get at the 'map' field directly from now on"); if (compoundMapField == null) { compoundMapField = NBTTagCompound.class.getDeclaredField("map"); compoundMapField.setAccessible(true); } } catch (Throwable e) { // Can't do much beyond this throw new RuntimeException(e); } } } if (compoundMapField != null) { try { foreignValues = ((HashMap) compoundMapField.get(foreign)).values(); } catch (Throwable e) { // Can't do much beyond this throw new RuntimeException(e); } } for (Object obj : foreignValues) { NBTBase base = (NBTBase) obj; values.put(base.getName(), toNative(base)); } return new CompoundTag(foreign.getName(), values); } else if (foreign instanceof NBTTagByte) { return new ByteTag(foreign.getName(), ((NBTTagByte) foreign).data); } else if (foreign instanceof NBTTagByteArray) { return new ByteArrayTag(foreign.getName(), ((NBTTagByteArray) foreign).data); } else if (foreign instanceof NBTTagDouble) { return new DoubleTag(foreign.getName(), ((NBTTagDouble) foreign).data); } else if (foreign instanceof NBTTagFloat) { return new FloatTag(foreign.getName(), ((NBTTagFloat) foreign).data); } else if (foreign instanceof NBTTagInt) { return new IntTag(foreign.getName(), ((NBTTagInt) foreign).data); } else if (foreign instanceof NBTTagIntArray) { return new IntArrayTag(foreign.getName(), ((NBTTagIntArray) foreign).data); } else if (foreign instanceof NBTTagList) { List values = new ArrayList(); NBTTagList foreignList = (NBTTagList) foreign; int type = NBTConstants.TYPE_BYTE; for (int i = 0; i < foreignList.size(); i++) { NBTBase foreignTag = foreignList.get(i); values.add(toNative(foreignTag)); type = foreignTag.getTypeId(); } Class cls = NBTConstants.getClassFromType(type); return new ListTag(foreign.getName(), cls, values); } else if (foreign instanceof NBTTagLong) { return new LongTag(foreign.getName(), ((NBTTagLong) foreign).data); } else if (foreign instanceof NBTTagShort) { return new ShortTag(foreign.getName(), ((NBTTagShort) foreign).data); } else if (foreign instanceof NBTTagString) { return new StringTag(foreign.getName(), ((NBTTagString) foreign).data); } else if (foreign instanceof NBTTagEnd) { return new EndTag(); } else { throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName()); } } /** * Converts a WorldEdit-native NBT structure to a NMS structure. * * @param foreign structure to convert * @return non-native structure */ private static NBTBase fromNative(Tag foreign) { if (foreign == null) { return null; } if (foreign instanceof CompoundTag) { NBTTagCompound tag = new NBTTagCompound(foreign.getName()); for (Map.Entry entry : ((CompoundTag) foreign) .getValue().entrySet()) { tag.set(entry.getKey(), fromNative(entry.getValue())); } return tag; } else if (foreign instanceof ByteTag) { return new NBTTagByte(foreign.getName(), ((ByteTag) foreign).getValue()); } else if (foreign instanceof ByteArrayTag) { return new NBTTagByteArray(foreign.getName(), ((ByteArrayTag) foreign).getValue()); } else if (foreign instanceof DoubleTag) { return new NBTTagDouble(foreign.getName(), ((DoubleTag) foreign).getValue()); } else if (foreign instanceof FloatTag) { return new NBTTagFloat(foreign.getName(), ((FloatTag) foreign).getValue()); } else if (foreign instanceof IntTag) { return new NBTTagInt(foreign.getName(), ((IntTag) foreign).getValue()); } else if (foreign instanceof IntArrayTag) { return new NBTTagIntArray(foreign.getName(), ((IntArrayTag) foreign).getValue()); } else if (foreign instanceof ListTag) { NBTTagList tag = new NBTTagList(foreign.getName()); ListTag foreignList = (ListTag) foreign; for (Tag t : foreignList.getValue()) { tag.add(fromNative(t)); } return tag; } else if (foreign instanceof LongTag) { return new NBTTagLong(foreign.getName(), ((LongTag) foreign).getValue()); } else if (foreign instanceof ShortTag) { return new NBTTagShort(foreign.getName(), ((ShortTag) foreign).getValue()); } else if (foreign instanceof StringTag) { return new NBTTagString(foreign.getName(), ((StringTag) foreign).getValue()); } else if (foreign instanceof EndTag) { return new NBTTagEnd(); } else { throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName()); } } public static boolean isValidBlockType(int type) throws NoClassDefFoundError { return type == 0 || (type >= 1 && type < net.minecraft.server.v1_5_R1.Block.byId.length && net.minecraft.server.v1_5_R1.Block.byId[type] != null); } } ======= package com.sk89q.worldedit.bukkit; // $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 . */ 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.v1_5_R2.NBTBase; import net.minecraft.server.v1_5_R2.NBTTagByte; import net.minecraft.server.v1_5_R2.NBTTagByteArray; import net.minecraft.server.v1_5_R2.NBTTagCompound; import net.minecraft.server.v1_5_R2.NBTTagDouble; import net.minecraft.server.v1_5_R2.NBTTagEnd; import net.minecraft.server.v1_5_R2.NBTTagFloat; import net.minecraft.server.v1_5_R2.NBTTagInt; import net.minecraft.server.v1_5_R2.NBTTagIntArray; import net.minecraft.server.v1_5_R2.NBTTagList; import net.minecraft.server.v1_5_R2.NBTTagLong; import net.minecraft.server.v1_5_R2.NBTTagShort; import net.minecraft.server.v1_5_R2.NBTTagString; import net.minecraft.server.v1_5_R2.TileEntity; import org.bukkit.World; import org.bukkit.craftbukkit.v1_5_R2.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.WorldEdit; 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}. */ public class DefaultNmsBlock extends NmsBlock { private static final Logger logger = WorldEdit.logger; private static Field compoundMapField; private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why. private NBTTagCompound nbtData = null; static { Field field; try { field = net.minecraft.server.v1_5_R2.Block.class.getDeclaredField("isTileEntity"); field.setAccessible(true); } catch (NoSuchFieldException e) { // logger.severe("Could not find NMS block tile entity field!"); field = null; } nmsBlock_isTileEntityField = field; } public static boolean verify() { return nmsBlock_isTileEntityField != 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 DefaultNmsBlock(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 DefaultNmsBlock(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 DefaultNmsBlock get(World world, Vector position, int type, int data) { if (!hasTileEntity(type)) { return null; } 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 DefaultNmsBlock(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 (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) { return false; } if (block instanceof DefaultNmsBlock) { DefaultNmsBlock nmsProxyBlock = (DefaultNmsBlock) block; data = nmsProxyBlock.getNmsData(position); } else if (block instanceof TileEntityBlock) { DefaultNmsBlock nmsProxyBlock = new DefaultNmsBlock( block.getId(), 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 block id or data was changed */ 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()); // TileEntity te = craftWorld.getHandle().getTileEntity(x, y, z); // craftWorld.getHandle().tileEntityList.remove(te); boolean changed = craftWorld.getHandle().setTypeIdAndData(x, y, z, block.getId(), 0, 0); if (block instanceof BaseBlock) { world.copyToWorld(position, (BaseBlock) block); } changed = craftWorld.getHandle().setData(x, y, z, block.getData(), 0) || changed; if (changed && notifyAdjacent) { craftWorld.getHandle().notify(x, y, z); craftWorld.getHandle().update(x, y, z, block.getId()); } return changed; } public static boolean hasTileEntity(int type) { net.minecraft.server.v1_5_R2.Block nmsBlock = getNmsBlock(type); if (nmsBlock == null) { return false; } try { return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast } catch (IllegalAccessException e) { return false; } } public static net.minecraft.server.v1_5_R2.Block getNmsBlock(int type) { if (type < 0 || type >= net.minecraft.server.v1_5_R2.Block.byId.length) { return null; } return net.minecraft.server.v1_5_R2.Block.byId[type]; } /** * 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 foreignValues = null; if (compoundMapField == null) { try { // Method name may change! foreignValues = ((NBTTagCompound) foreign).c(); } catch (Throwable t) { try { logger.warning("WorldEdit: Couldn't get NBTTagCompound.c(), " + ((ByteTag) foreign).getValue()); "so we're going to try to get at the 'map' field directly from now on"); if (compoundMapField == null) { compoundMapField = NBTTagCompound.class.getDeclaredField("map"); compoundMapField.setAccessible(true); } } catch (Throwable e) { // Can't do much beyond this throw new RuntimeException(e); } } } if (compoundMapField != null) { try { foreignValues = ((HashMap) compoundMapField.get(foreign)).values(); } catch (Throwable e) { // Can't do much beyond this throw new RuntimeException(e); } } for (Object obj : foreignValues) { NBTBase base = (NBTBase) obj; values.put(base.getName(), toNative(base)); } return new CompoundTag(foreign.getName(), values); } else if (foreign instanceof NBTTagByte) { return new ByteTag(foreign.getName(), ((NBTTagByte) foreign).data); } else if (foreign instanceof NBTTagByteArray) { return new ByteArrayTag(foreign.getName(), ((NBTTagByteArray) foreign).data); } else if (foreign instanceof NBTTagDouble) { return new DoubleTag(foreign.getName(), ((NBTTagDouble) foreign).data); } else if (foreign instanceof NBTTagFloat) { return new FloatTag(foreign.getName(), ((NBTTagFloat) foreign).data); } else if (foreign instanceof NBTTagInt) { return new IntTag(foreign.getName(), ((NBTTagInt) foreign).data); } else if (foreign instanceof NBTTagIntArray) { return new IntArrayTag(foreign.getName(), ((NBTTagIntArray) foreign).data); } else if (foreign instanceof NBTTagList) { List values = new ArrayList(); NBTTagList foreignList = (NBTTagList) foreign; int type = NBTConstants.TYPE_BYTE; for (int i = 0; i < foreignList.size(); i++) { NBTBase foreignTag = foreignList.get(i); values.add(toNative(foreignTag)); type = foreignTag.getTypeId(); } Class cls = NBTConstants.getClassFromType(type); return new ListTag(foreign.getName(), cls, values); } else if (foreign instanceof NBTTagLong) { return new LongTag(foreign.getName(), ((NBTTagLong) foreign).data); } else if (foreign instanceof NBTTagShort) { return new ShortTag(foreign.getName(), ((NBTTagShort) foreign).data); } else if (foreign instanceof NBTTagString) { return new StringTag(foreign.getName(), ((NBTTagString) foreign).data); } else if (foreign instanceof NBTTagEnd) { return new EndTag(); } else { throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName()); } } /** * Converts a WorldEdit-native NBT structure to a NMS structure. * * @param foreign structure to convert * @return non-native structure */ private static NBTBase fromNative(Tag foreign) { if (foreign == null) { return null; } if (foreign instanceof CompoundTag) { NBTTagCompound tag = new NBTTagCompound(foreign.getName()); for (Map.Entry entry : ((CompoundTag) foreign) .getValue().entrySet()) { tag.set(entry.getKey(), fromNative(entry.getValue())); } return tag; } else if (foreign instanceof ByteTag) { return new NBTTagByte(foreign.getName(), } else if (foreign instanceof ByteArrayTag) { return new NBTTagByteArray(foreign.getName(), ((ByteArrayTag) foreign).getValue()); } else if (foreign instanceof DoubleTag) { return new NBTTagDouble(foreign.getName(), ((DoubleTag) foreign).getValue()); } else if (foreign instanceof FloatTag) { return new NBTTagFloat(foreign.getName(), ((FloatTag) foreign).getValue()); } else if (foreign instanceof IntTag) { return new NBTTagInt(foreign.getName(), ((IntTag) foreign).getValue()); } else if (foreign instanceof IntArrayTag) { return new NBTTagIntArray(foreign.getName(), ((IntArrayTag) foreign).getValue()); } else if (foreign instanceof ListTag) { NBTTagList tag = new NBTTagList(foreign.getName()); ListTag foreignList = (ListTag) foreign; for (Tag t : foreignList.getValue()) { tag.add(fromNative(t)); } return tag; } else if (foreign instanceof LongTag) { return new NBTTagLong(foreign.getName(), ((LongTag) foreign).getValue()); } else if (foreign instanceof ShortTag) { return new NBTTagShort(foreign.getName(), ((ShortTag) foreign).getValue()); } else if (foreign instanceof StringTag) { return new NBTTagString(foreign.getName(), ((StringTag) foreign).getValue()); } else if (foreign instanceof EndTag) { return new NBTTagEnd(); } else { throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName()); } } public static boolean isValidBlockType(int type) throws NoClassDefFoundError { return type == 0 || (type >= 1 && type < net.minecraft.server.v1_5_R2.Block.byId.length && net.minecraft.server.v1_5_R2.Block.byId[type] != null); } } >>>>>>> 382b6f35adcec87a0a2272bb84c2be42115a6244:src/main/java/com/sk89q/worldedit/bukkit/DefaultNmsBlock.java
Solution content
package org.enginehub.worldedit.bukkit;
// $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 .
 */

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.v1_5_R2.NBTBase;
import net.minecraft.server.v1_5_R2.NBTTagByte;
import net.minecraft.server.v1_5_R2.NBTTagByteArray;
import net.minecraft.server.v1_5_R2.NBTTagCompound;
import net.minecraft.server.v1_5_R2.NBTTagDouble;
import net.minecraft.server.v1_5_R2.NBTTagEnd;
import net.minecraft.server.v1_5_R2.NBTTagFloat;
import net.minecraft.server.v1_5_R2.NBTTagInt;
import net.minecraft.server.v1_5_R2.NBTTagIntArray;
import net.minecraft.server.v1_5_R2.NBTTagList;
import net.minecraft.server.v1_5_R2.NBTTagLong;
import net.minecraft.server.v1_5_R2.NBTTagShort;
import net.minecraft.server.v1_5_R2.NBTTagString;
import net.minecraft.server.v1_5_R2.TileEntity;

import org.bukkit.World;
import org.bukkit.craftbukkit.v1_5_R2.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.WorldEdit;
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}. */ public class DefaultNmsBlock extends BukkitBlock { private static final Logger logger = WorldEdit.logger; private static Field compoundMapField; private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why. private NBTTagCompound nbtData = null; static { Field field; try { field = net.minecraft.server.v1_5_R2.Block.class.getDeclaredField("isTileEntity"); field.setAccessible(true); } catch (NoSuchFieldException e) { // logger.severe("Could not find NMS block tile entity field!"); field = null; } nmsBlock_isTileEntityField = field; } public static boolean verify() { return nmsBlock_isTileEntityField != 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 DefaultNmsBlock(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 DefaultNmsBlock(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 DefaultNmsBlock get(World world, Vector position, int type, int data) { if (!hasTileEntity(type)) { return null; } 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 DefaultNmsBlock(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 (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) { return false; } if (block instanceof DefaultNmsBlock) { DefaultNmsBlock nmsProxyBlock = (DefaultNmsBlock) block; data = nmsProxyBlock.getNmsData(position); } else if (block instanceof TileEntityBlock) { DefaultNmsBlock nmsProxyBlock = new DefaultNmsBlock( block.getId(), 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 block id or data was changed */ public static boolean setSafely(LoadedWorld world, Vector position, Block block, boolean notifyAdjacent) { int x = position.getBlockX(); int y = position.getBlockY(); int z = position.getBlockZ(); CraftWorld craftWorld = ((CraftWorld) world.getWorld()); // TileEntity te = craftWorld.getHandle().getTileEntity(x, y, z); // craftWorld.getHandle().tileEntityList.remove(te); boolean changed = craftWorld.getHandle().setTypeIdAndData(x, y, z, block.getId(), 0, 0); if (block instanceof BaseBlock) { world.copyToWorld(position, (BaseBlock) block); } changed = craftWorld.getHandle().setData(x, y, z, block.getData(), 0) || changed; if (changed && notifyAdjacent) { craftWorld.getHandle().notify(x, y, z); List values = new ArrayList(); } craftWorld.getHandle().update(x, y, z, block.getId()); } return changed; } public static boolean hasTileEntity(int type) { net.minecraft.server.v1_5_R2.Block nmsBlock = getNmsBlock(type); if (nmsBlock == null) { return false; } try { return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast } catch (IllegalAccessException e) { return false; } } public static net.minecraft.server.v1_5_R2.Block getNmsBlock(int type) { if (type < 0 || type >= net.minecraft.server.v1_5_R2.Block.byId.length) { return null; } return net.minecraft.server.v1_5_R2.Block.byId[type]; } /** * 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 foreignValues = null; if (compoundMapField == null) { try { // Method name may change! foreignValues = ((NBTTagCompound) foreign).c(); } catch (Throwable t) { try { logger.warning("WorldEdit: Couldn't get NBTTagCompound.c(), " + "so we're going to try to get at the 'map' field directly from now on"); if (compoundMapField == null) { compoundMapField = NBTTagCompound.class.getDeclaredField("map"); compoundMapField.setAccessible(true); } } catch (Throwable e) { // Can't do much beyond this throw new RuntimeException(e); } } } if (compoundMapField != null) { try { foreignValues = ((HashMap) compoundMapField.get(foreign)).values(); } catch (Throwable e) { // Can't do much beyond this throw new RuntimeException(e); } for (Object obj : foreignValues) { NBTBase base = (NBTBase) obj; values.put(base.getName(), toNative(base)); } return new CompoundTag(foreign.getName(), values); } else if (foreign instanceof NBTTagByte) { return new ByteTag(foreign.getName(), ((NBTTagByte) foreign).data); } else if (foreign instanceof NBTTagByteArray) { return new ByteArrayTag(foreign.getName(), ((NBTTagByteArray) foreign).data); } else if (foreign instanceof NBTTagDouble) { return new DoubleTag(foreign.getName(), ((NBTTagDouble) foreign).data); } else if (foreign instanceof NBTTagFloat) { return new FloatTag(foreign.getName(), ((NBTTagFloat) foreign).data); } else if (foreign instanceof NBTTagInt) { return new IntTag(foreign.getName(), ((NBTTagInt) foreign).data); } else if (foreign instanceof NBTTagIntArray) { return new IntArrayTag(foreign.getName(), ((NBTTagIntArray) foreign).data); } else if (foreign instanceof NBTTagList) { } NBTTagList foreignList = (NBTTagList) foreign; int type = NBTConstants.TYPE_BYTE; for (int i = 0; i < foreignList.size(); i++) { NBTBase foreignTag = foreignList.get(i); values.add(toNative(foreignTag)); type = foreignTag.getTypeId(); } Class cls = NBTConstants.getClassFromType(type); return new ListTag(foreign.getName(), cls, values); } else if (foreign instanceof NBTTagLong) { return new LongTag(foreign.getName(), ((NBTTagLong) foreign).data); } else if (foreign instanceof NBTTagShort) { return new ShortTag(foreign.getName(), ((NBTTagShort) foreign).data); } else if (foreign instanceof NBTTagString) { return new StringTag(foreign.getName(), ((NBTTagString) foreign).data); } else if (foreign instanceof NBTTagEnd) { return new EndTag(); } else { throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName()); } } /** * Converts a WorldEdit-native NBT structure to a NMS structure. * * @param foreign structure to convert * @return non-native structure */ private static NBTBase fromNative(Tag foreign) { if (foreign == null) { return null; } if (foreign instanceof CompoundTag) { NBTTagCompound tag = new NBTTagCompound(foreign.getName()); for (Map.Entry entry : ((CompoundTag) foreign) .getValue().entrySet()) { tag.set(entry.getKey(), fromNative(entry.getValue())); } return tag; } else if (foreign instanceof ByteTag) { return new NBTTagByte(foreign.getName(), ((ByteTag) foreign).getValue()); } else if (foreign instanceof ByteArrayTag) { return new NBTTagByteArray(foreign.getName(), ((ByteArrayTag) foreign).getValue()); } else if (foreign instanceof DoubleTag) { return new NBTTagDouble(foreign.getName(), ((DoubleTag) foreign).getValue()); } else if (foreign instanceof FloatTag) { return new NBTTagFloat(foreign.getName(), ((FloatTag) foreign).getValue()); } else if (foreign instanceof IntTag) { return new NBTTagInt(foreign.getName(), ((IntTag) foreign).getValue()); } else if (foreign instanceof IntArrayTag) { return new NBTTagIntArray(foreign.getName(), ((IntArrayTag) foreign).getValue()); } else if (foreign instanceof ListTag) { NBTTagList tag = new NBTTagList(foreign.getName()); ListTag foreignList = (ListTag) foreign; for (Tag t : foreignList.getValue()) { tag.add(fromNative(t)); } return tag; } else if (foreign instanceof LongTag) { return new NBTTagLong(foreign.getName(), ((LongTag) foreign).getValue()); } else if (foreign instanceof ShortTag) { return new NBTTagShort(foreign.getName(), ((ShortTag) foreign).getValue()); } else if (foreign instanceof StringTag) { return new NBTTagString(foreign.getName(), ((StringTag) foreign).getValue()); } else if (foreign instanceof EndTag) { return new NBTTagEnd(); } else { throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName()); } } public static boolean isValidBlockType(int type) throws NoClassDefFoundError { return type == 0 || (type >= 1 && type < net.minecraft.server.v1_5_R2.Block.byId.length && net.minecraft.server.v1_5_R2.Block.byId[type] != null); }
File
DefaultNmsBlock.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
<<<<<<< HEAD:src/main/java/org/enginehub/worldedit/bukkit/LoadedWorld.java
// $Id$
/*
 * WorldEdit
 * Copyright (C) 2010 sk89q  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 org.enginehub.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 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.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.MinecartTNT;
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.inventory.DoubleChestInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.enginehub.worldedit.EditSession;

import com.sk89q.worldedit.BiomeType;
import com.sk89q.worldedit.BlockVector2D;
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;
     * @param type
     * @return
     */
    @Override

            } else {
     * @param pt
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.BukkitBiomeType;
import com.sk89q.worldedit.bukkit.EditSessionBlockChangeDelegate;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.entity.BukkitEntity;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.TreeGenerator;

/**
 * An implementation of a world for Bukkit.
 */
public class LoadedWorld extends LocalWorld {

    private static final Logger logger = WorldEdit.logger;
    private World world;
    private static boolean skipNmsAccess = false;
    private static boolean skipNmsSafeSet = false;
    private static boolean skipNmsValidBlockCheck = false;

    /*
     * holder for the nmsblock class that we should use
     */
    private static Class nmsBlockType;
    private static Method nmsSetMethod;
    private static Method nmsValidBlockMethod;
    private static Method nmsGetMethod;
    private static Method nmsSetSafeMethod;

    /**
     * Construct the object.
     * @param world
     */
    public LoadedWorld(World world) {
        this.world = world;

        // check if we have a class we can use for nms access

        // only run once per server startup
        if (nmsBlockType != null || skipNmsAccess || skipNmsSafeSet || skipNmsValidBlockCheck) return;
        Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldEdit");
        if (!(plugin instanceof WorldEditPlugin)) return; // hopefully never happens
        WorldEditPlugin wePlugin = ((WorldEditPlugin) plugin);
        File nmsBlocksDir = new File(wePlugin.getDataFolder() + File.separator + "nmsblocks" + File.separator);
        if (nmsBlocksDir.listFiles() == null) { // no files to use
            skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
            return;
        }
        try {
            // make a classloader that can handle our blocks
            NmsBlockClassLoader loader = new NmsBlockClassLoader(LoadedWorld.class.getClassLoader(), nmsBlocksDir);
            String filename;
            for (File f : nmsBlocksDir.listFiles()) {
                if (!f.isFile()) continue;
                filename = f.getName();
                // load class using magic keyword
                Class testBlock = null;
                try {
                    testBlock = loader.loadClass("CL-NMS" + filename);
                } catch (Throwable e) {
                    // someone is putting things where they don't belong
                    continue;
                }
                filename = filename.replaceFirst(".class$", ""); // get rid of extension
                if (BukkitBlock.class.isAssignableFrom(testBlock)) {
                    // got a NmsBlock, test it now
                    Class nmsClass = (Class) testBlock;
                    boolean canUse = false;
                    try {
                        canUse = (Boolean) nmsClass.getMethod("verify", null).invoke(null, null);
                    } catch (Throwable e) {
                        continue;
                    }
                    if (!canUse) continue; // not for this server
                    nmsBlockType = nmsClass;
                    nmsSetMethod = nmsBlockType.getMethod("set", World.class, Vector.class, BaseBlock.class);
                    nmsValidBlockMethod = nmsBlockType.getMethod("isValidBlockType", int.class);
                    nmsGetMethod = nmsBlockType.getMethod("get", World.class, Vector.class, int.class, int.class);
                    nmsSetSafeMethod = nmsBlockType.getMethod("setSafely",
                            LoadedWorld.class, Vector.class, com.sk89q.worldedit.foundation.Block.class, boolean.class);
                    // phew
                    break;
                }
            }
            if (nmsBlockType != null) {
                logger.info("[WorldEdit] Using external NmsBlock for this version: " + nmsBlockType.getName());
            } else {
                // try our default
                try {
                    nmsBlockType = (Class) Class.forName("com.sk89q.worldedit.bukkit.DefaultNmsBlock");
                    boolean canUse = (Boolean) nmsBlockType.getMethod("verify", null).invoke(null, null);
                    if (canUse) {
                        nmsSetMethod = nmsBlockType.getMethod("set", World.class, Vector.class, BaseBlock.class);
                        nmsValidBlockMethod = nmsBlockType.getMethod("isValidBlockType", int.class);
                        nmsGetMethod = nmsBlockType.getMethod("get", World.class, Vector.class, int.class, int.class);
                        nmsSetSafeMethod = nmsBlockType.getMethod("setSafely",
                                LoadedWorld.class, Vector.class, com.sk89q.worldedit.foundation.Block.class, boolean.class);
                        logger.info("[WorldEdit] Using inbuilt NmsBlock for this version.");
                    }
                } catch (Throwable e) {
                    // OMG DEVS WAI U NO SUPPORT  SERVER
                    skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
                    logger.warning("[WorldEdit] No compatible nms block class found.");
                }
            }
        } catch (Throwable e) {
            logger.warning("[WorldEdit] Unable to load NmsBlock classes, make sure they are installed correctly.");
            e.printStackTrace();
            skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
        }
    }

    private class NmsBlockClassLoader extends ClassLoader {
        public File searchDir;
        public NmsBlockClassLoader(ClassLoader parent, File searchDir) {
            super(parent);
            this.searchDir = searchDir;
        }

        @Override
        public Class loadClass(String name) throws ClassNotFoundException {
            if (!name.startsWith("CL-NMS")) {
                return super.loadClass(name);
                name = name.replace("CL-NMS", ""); // hacky lol
            }
            try {
                URL url = new File(searchDir, name).toURI().toURL();
                InputStream input = url.openConnection().getInputStream();
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();

                int data = input.read();
                while (data != -1) {
                    buffer.write(data);
                    data = input.read();
                }
                input.close();

                byte[] classData = buffer.toByteArray();

                return defineClass(name.replaceFirst(".class$", ""), classData, 0, classData.length);
            } catch (Throwable e) {
                throw new ClassNotFoundException();
            }
        }
    }

    /**
     * Get the world handle.
     *
     * @return
     */
    public World getWorld() {
        return world;
    }

    /**
     * Get the name of the world
     *
     * @return
     */
    @Override
    public String getName() {
        return world.getName();
    }

    /**
     * Set block type.
     *
     * @param pt
     * @param type
     * @return
     */
    @Override
    public boolean setBlockType(Vector pt, int type) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeId(type);
    }

    /**
     * Set block type.
     *
    public boolean setBlockTypeFast(Vector pt, int type) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeId(type, false);
    }

    /**
     * set block type & data
     * @param pt
     * @param type
     * @param data
     * @return
     */
    @Override
    public boolean setTypeIdAndData(Vector pt, int type, int data) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeIdAndData(type, (byte) data, true);
    }

    /**
     * set block type & data
     * @param pt
     * @param type
     * @param data
     * @return
     */
    @Override
    public boolean setTypeIdAndDataFast(Vector pt, int type, int data) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeIdAndData(type, (byte) data, false);
    }

    /**
     * Get block type.
     *
     * @param pt
     * @return
     */
    @Override
    public int getBlockType(Vector pt) {
        return world.getBlockTypeIdAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
    }

    /**
     * Set block data.
     *
     * @param pt
     * @param data
     */
    @Override
    public void setBlockData(Vector pt, int data) {
        world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setData((byte) data);
    }

    /**
     * Set block data.
     *
     * @param pt
     * @param data
     */
    @Override
    public void setBlockDataFast(Vector pt, int data) {
        world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setData((byte) data, false);
    }

    /**
     * Get block data.
     *
     * @param pt
     * @return
     */
    @Override
    public int getBlockData(Vector pt) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getData();
    }

    /**
     * Get block light level.
     *
     * @param pt
     * @return
     */
    @Override
    public int getBlockLightLevel(Vector pt) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getLightLevel();
    }

    /**
     * Get biome type
     *
     * @param pt
     * @return
     */
    @Override
    public BiomeType getBiome(Vector2D pt) {
        Biome bukkitBiome = world.getBiome(pt.getBlockX(), pt.getBlockZ());
        try {
            return BukkitBiomeType.valueOf(bukkitBiome.name());
        } catch (IllegalArgumentException exc) {
            return BiomeType.UNKNOWN;
        }
    }

    @Override
    public void setBiome(Vector2D pt, BiomeType biome) {
        if (biome instanceof BukkitBiomeType) {
            Biome bukkitBiome;
            bukkitBiome = ((BukkitBiomeType) biome).getBukkitBiome();
            world.setBiome(pt.getBlockX(), pt.getBlockZ(), bukkitBiome);
        }
    }

    /**
     * Regenerate an area.
     *
     * @param region
     * @param editSession
     * @return
     */
    @Override
    public boolean regenerate(Region region, EditSession editSession) {
        BaseBlock[] history = new BaseBlock[16 * 16 * (getMaxY() + 1)];

        for (Vector2D chunk : region.getChunks()) {
            Vector min = new Vector(chunk.getBlockX() * 16, 0, chunk.getBlockZ() * 16);

            // First save all the blocks inside
            for (int x = 0; x < 16; ++x) {
                for (int y = 0; y < (getMaxY() + 1); ++y) {
                    for (int z = 0; z < 16; ++z) {
                        Vector pt = min.add(x, y, z);
                        int index = y * 16 * 16 + z * 16 + x;
                        history[index] = editSession.getBlock(pt);
                    }
                }
            }

            try {
                world.regenerateChunk(chunk.getBlockX(), chunk.getBlockZ());
            } catch (Throwable t) {
                t.printStackTrace();
            }

            // Then restore
            for (int x = 0; x < 16; ++x) {
                for (int y = 0; y < (getMaxY() + 1); ++y) {
                    for (int z = 0; z < 16; ++z) {
                        Vector pt = min.add(x, y, z);
                        int index = y * 16 * 16 + z * 16 + x;

                        // We have to restore the block if it was outside
                        if (!region.contains(pt)) {
                            editSession.smartSetBlock(pt, history[index]);
                        } else { // Otherwise fool with history
                            editSession.rememberChange(pt, history[index],
                                    editSession.rawGetBlock(pt));
                        }
                    }
                }
            }
        }

        return true;
    }

    /**
     * Attempts to accurately copy a BaseBlock's extra data to the world.
     *
     * @param pt
     * @param block
     * @return
     */
    @Override
    public boolean copyToWorld(Vector pt, BaseBlock block) {
        if (block instanceof SignBlock) {
            // Signs
            setSignText(pt, ((SignBlock) block).getText());
            return true;
        }

        if (block instanceof FurnaceBlock) {
            // Furnaces
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof Furnace)) return false;
            Furnace bukkit = (Furnace) state;
            FurnaceBlock we = (FurnaceBlock) block;
            bukkit.setBurnTime(we.getBurnTime());
            bukkit.setCookTime(we.getCookTime());
            return setContainerBlockContents(pt, ((ContainerBlock) block).getItems());
        }

        if (block instanceof ContainerBlock) {
            // Chests/dispenser
            return setContainerBlockContents(pt, ((ContainerBlock) block).getItems());
        }

        if (block instanceof MobSpawnerBlock) {
            // Mob spawners
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof CreatureSpawner)) return false;
            CreatureSpawner bukkit = (CreatureSpawner) state;
            MobSpawnerBlock we = (MobSpawnerBlock) block;
            bukkit.setCreatureTypeByName(we.getMobType());
            bukkit.setDelay(we.getDelay());
            return true;
        }

        if (block instanceof NoteBlock) {

            // Note block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.NoteBlock)) return false;
            org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state;
            NoteBlock we = (NoteBlock) block;
            bukkit.setRawNote(we.getNote());
            return true;
        }

        if (block instanceof SkullBlock) {
            // Skull block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.Skull)) return false;
            Skull bukkit = (Skull) state;
            SkullBlock we = (SkullBlock) block;
            // this is dumb
            SkullType skullType = SkullType.SKELETON;
            switch (we.getSkullType()) {
            case 0:
                skullType = SkullType.SKELETON;
                break;
            case 1:
                skullType = SkullType.WITHER;
                break;
            case 2:
                skullType = SkullType.ZOMBIE;
                break;
            case 3:
                skullType = SkullType.PLAYER;
                break;
            case 4:
                skullType = SkullType.CREEPER;
                break;
            }
            bukkit.setSkullType(skullType);
            BlockFace rotation;
            switch (we.getRot()) {
            // soooo dumb
            case 0:
                rotation = BlockFace.NORTH;
                break;
            case 1:
                rotation = BlockFace.NORTH_NORTH_EAST;
                break;
            case 2:
                rotation = BlockFace.NORTH_EAST;
                break;
            case 3:
                rotation = BlockFace.EAST_NORTH_EAST;
                break;
            case 4:
                rotation = BlockFace.EAST;
                break;
            case 5:
                rotation = BlockFace.EAST_SOUTH_EAST;
                break;
            case 6:
                rotation = BlockFace.SOUTH_EAST;
                break;
            case 7:
                rotation = BlockFace.SOUTH_SOUTH_EAST;
                break;
            case 8:
                rotation = BlockFace.SOUTH;
                break;
            case 9:
                rotation = BlockFace.SOUTH_SOUTH_WEST;
                break;
            case 10:
                rotation = BlockFace.SOUTH_WEST;
                break;
            case 11:
                rotation = BlockFace.WEST_SOUTH_WEST;
                break;
            case 12:
                rotation = BlockFace.WEST;
                break;
            case 13:
                rotation = BlockFace.WEST_NORTH_WEST;
                break;
            case 14:
                rotation = BlockFace.NORTH_WEST;
                break;
            case 15:
                rotation = BlockFace.NORTH_NORTH_WEST;
                break;
            default:
                rotation = BlockFace.NORTH;
                break;
            }
            bukkit.setRotation(rotation);
            if (we.getOwner() != null && !we.getOwner().isEmpty()) bukkit.setOwner(we.getOwner());
            bukkit.update(true);
            return true;
        }
            // Mob spawners
                break;
        if (!skipNmsAccess) {
            try {
                return (Boolean) nmsSetMethod.invoke(null, world, pt, block);
            } catch (Throwable t) {
                logger.log(Level.WARNING, "WorldEdit: Failed to do NMS access for direct NBT data copy", t);
                skipNmsAccess = true;
            }
        }

        return false;
    }

    /**
     * Attempts to read a BaseBlock's extra data from the world.
     *
     * @param pt
     * @param block
     * @return
     */
    @Override
    public boolean copyFromWorld(Vector pt, BaseBlock block) {
        if (block instanceof SignBlock) {
            // Signs
            ((SignBlock) block).setText(getSignText(pt));
            return true;
        }

        if (block instanceof FurnaceBlock) {
            // Furnaces
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof Furnace)) return false;
            Furnace bukkit = (Furnace) state;
            FurnaceBlock we = (FurnaceBlock) block;
            we.setBurnTime(bukkit.getBurnTime());
            we.setCookTime(bukkit.getCookTime());
            ((ContainerBlock) block).setItems(getContainerBlockContents(pt));
            return true;
        }

        if (block instanceof ContainerBlock) {
            // Chests/dispenser
            ((ContainerBlock) block).setItems(getContainerBlockContents(pt));
            return true;
        }

        if (block instanceof MobSpawnerBlock) {
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof CreatureSpawner)) return false;
            CreatureSpawner bukkit = (CreatureSpawner) state;
            MobSpawnerBlock we = (MobSpawnerBlock) block;
            we.setMobType(bukkit.getCreatureTypeName());
            we.setDelay((short) bukkit.getDelay());
            return true;
        }

        if (block instanceof NoteBlock) {
            // Note block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.NoteBlock)) return false;
            org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state;
            NoteBlock we = (NoteBlock) block;
            we.setNote(bukkit.getRawNote());
            return true;
        }

        if (block instanceof SkullBlock) {
            // Skull block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.Skull)) return false;
            Skull bukkit = (Skull) state;
            SkullBlock we = (SkullBlock) block;
            byte skullType = 0;
            switch (bukkit.getSkullType()) {
            // this is dumb but whoever wrote the class is stupid
            case SKELETON:
                skullType = 0;
                break;
            case WITHER:
                skullType = 1;
                break;
            case ZOMBIE:
                skullType = 2;
                break;
            case PLAYER:
                skullType = 3;
            case CREEPER:
                skullType = 4;
                break;
            }
            we.setSkullType(skullType);
            byte rot = 0;
            switch (bukkit.getRotation()) {
            // this is even more dumb, hurray for copy/paste
            case NORTH:
                rot = (byte) 0;
                break;
            case NORTH_NORTH_EAST:
                rot = (byte) 1;
                break;
            case NORTH_EAST:
                rot = (byte) 2;
                break;
            case EAST_NORTH_EAST:
                rot = (byte) 3;
                break;
            case EAST:
                rot = (byte) 4;
                break;
            case EAST_SOUTH_EAST:
                rot = (byte) 5;
                break;
            case SOUTH_EAST:
                rot = (byte) 6;
                break;
            case SOUTH_SOUTH_EAST:
                rot = (byte) 7;
                break;
            case SOUTH:
                rot = (byte) 8;
                break;
            case SOUTH_SOUTH_WEST:
                rot = (byte) 9;
                break;
            case SOUTH_WEST:
                rot = (byte) 10;
                break;
            case WEST_SOUTH_WEST:
                rot = (byte) 11;
                break;
            case WEST:
                rot = (byte) 12;
                break;
            case WEST_NORTH_WEST:
                rot = (byte) 13;
                break;
            case NORTH_WEST:
                rot = (byte) 14;
                break;
            case NORTH_NORTH_WEST:
                rot = (byte) 15;
                break;
            }
            we.setRot(rot);
            we.setOwner(bukkit.hasOwner() ? bukkit.getOwner() : "");
            return true;
        }

        return false;
    }

    /**
     * Gets the single block inventory for a potentially double chest.
     * Handles people who have an old version of Bukkit.
     * This should be replaced with {@link org.bukkit.block.Chest#getBlockInventory()}
     * in a few months (now = March 2012) // note from future dev - lol
     *
     * @param chest The chest to get a single block inventory for
     * @return The chest's inventory
     */
    private Inventory getBlockInventory(Chest chest) {
        try {
            return chest.getBlockInventory();
        } catch (Throwable t) {
            if (chest.getInventory() instanceof DoubleChestInventory) {
                DoubleChestInventory inven = (DoubleChestInventory) chest.getInventory();
                if (inven.getLeftSide().getHolder().equals(chest)) {
                    return inven.getLeftSide();
                } else if (inven.getRightSide().getHolder().equals(chest)) {
                    return inven.getRightSide();
                } else {
                    return inven;
                }
            } else {
                return chest.getInventory();
            }
        }
    }

    /**
     * Clear a chest's contents.
     *
     * @param pt
     */
    @Override
    public boolean clearContainerBlockContents(Vector pt) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) {
            return false;
        }
        BlockState state = block.getState();
        if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
            return false;
        }

        org.bukkit.inventory.InventoryHolder chest = (org.bukkit.inventory.InventoryHolder) state;
        Inventory inven = chest.getInventory();
        if (chest instanceof Chest) {
            inven = getBlockInventory((Chest) chest);
        }
        inven.clear();
        return true;
    }

    /**
     * Generate a tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.TREE, editSession, pt);
    }

    /**
     * Generate a big tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateBigTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.BIG_TREE, editSession, pt);
    }

    /**
     * Generate a birch tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateBirchTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.BIRCH, editSession, pt);
    }

    /**
     * Generate a redwood tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateRedwoodTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.REDWOOD, editSession, pt);
    }

    /**
     * Generate a redwood tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateTallRedwoodTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.TALL_REDWOOD, editSession, pt);
    }

    /**
     * An EnumMap that stores which WorldEdit TreeTypes apply to which Bukkit TreeTypes
     */
    private static final EnumMap treeTypeMapping =
            new EnumMap(TreeGenerator.TreeType.class);

    static {
        // Mappings for new TreeType values not yet in Bukkit
        treeTypeMapping.put(TreeGenerator.TreeType.SWAMP, TreeType.TREE);
        treeTypeMapping.put(TreeGenerator.TreeType.JUNGLE_BUSH, TreeType.TREE);
        try {
            treeTypeMapping.put(TreeGenerator.TreeType.SHORT_JUNGLE, TreeType.valueOf("SMALL_JUNGLE"));
        } catch (IllegalArgumentException e) {
            treeTypeMapping.put(TreeGenerator.TreeType.SHORT_JUNGLE, TreeType.TREE);
        }
        for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
            try {
                TreeType bukkitType = TreeType.valueOf(type.name());
                treeTypeMapping.put(type, bukkitType);
            } catch (IllegalArgumentException e) {
                // Unhandled TreeType
            }
        }
        // Other mappings for WE-specific values
        treeTypeMapping.put(TreeGenerator.TreeType.RANDOM, TreeType.BROWN_MUSHROOM);
        treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_REDWOOD, TreeType.REDWOOD);
        treeTypeMapping.put(TreeGenerator.TreeType.PINE, TreeType.REDWOOD);
        for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
            if (treeTypeMapping.get(type) == null) {
                WorldEdit.logger.severe("No TreeType mapping for TreeGenerator.TreeType." + type);
            }
        }
    }

    public static TreeType toBukkitTreeType(TreeGenerator.TreeType type) {
        return treeTypeMapping.get(type);
    }

    @Override
    public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector pt) {
        TreeType bukkitType = toBukkitTreeType(type);
        return type != null && world.generateTree(BukkitUtils.toLocation(world, pt), bukkitType,
                new EditSessionBlockChangeDelegate(editSession));
    }

    /**
     * Drop an item.
     *
     * @param pt
     * @param item
     */
    @Override
    public void dropItem(Vector pt, BaseItemStack item) {
        ItemStack bukkitItem = new ItemStack(item.getType(), item.getAmount(),
                item.getData());
        world.dropItemNaturally(BukkitUtils.toLocation(world, pt), bukkitItem);
    }

    /**
     * Kill mobs in an area.
     *
     * @param origin The center of the area to kill mobs in.
     * @param radius Maximum distance to kill mobs at; radius < 0 means kill all mobs
     * @param flags various flags that determine what to kill
     * @return
     */
    @Override
    public int killMobs(Vector origin, double radius, int flags) {
        boolean killPets = (flags & KillFlags.PETS) != 0;
        boolean killNPCs = (flags & KillFlags.NPCS) != 0;
        boolean killAnimals = (flags & KillFlags.ANIMALS) != 0;
        boolean withLightning = (flags & KillFlags.WITH_LIGHTNING) != 0;
        boolean killGolems = (flags & KillFlags.GOLEMS) != 0;
        boolean killAmbient = (flags & KillFlags.AMBIENT) != 0;

        int num = 0;
                    }
        double radiusSq = radius * radius;

        Location bukkitOrigin = BukkitUtils.toLocation(world, origin);

        for (LivingEntity ent : world.getLivingEntities()) {
            if (ent instanceof HumanEntity) {
                continue;
            }

            if (!killAnimals && ent instanceof Animals) {
                continue;
            }

            if (!killPets && ent instanceof Tameable && ((Tameable) ent).isTamed()) {
                continue; // tamed pet
            }

            if (!killGolems && ent instanceof Golem) {
                continue;
            }

            if (!killNPCs && ent instanceof Villager) {
                continue;
            }

            if (!killAmbient && ent instanceof Ambient) {
                continue;
            }

            if (radius < 0 || bukkitOrigin.distanceSquared(ent.getLocation()) <= radiusSq) {
                if (withLightning) {
                    world.strikeLightningEffect(ent.getLocation());
                }
                ent.remove();
                ++num;
            }
        }

        return num;
    }

    /**
     * Remove entities in an area.
     *
     * @param origin
     * @param radius
     * @return
     */
    @Override
    public int removeEntities(EntityType type, Vector origin, int radius) {
        int num = 0;
        double radiusSq = Math.pow(radius, 2);

        for (Entity ent : world.getEntities()) {
            if (radius != -1
                if (ent instanceof ItemFrame) {
            }
                    && origin.distanceSq(BukkitUtils.toVector(ent.getLocation())) > radiusSq) {
                continue;
            }

            if (type == EntityType.ALL) {
                if (ent instanceof Projectile || ent instanceof Boat || ent instanceof Item
                        || ent instanceof FallingBlock || ent instanceof Minecart || ent instanceof Hanging
                        || ent instanceof TNTPrimed || ent instanceof ExperienceOrb) {
                    ent.remove();
                    num++;
                }
            } else if (type == EntityType.PROJECTILES || type == EntityType.ARROWS) {
                if (ent instanceof Projectile) {
                    // covers: arrow, egg, enderpearl, fireball, fish, snowball, throwpotion, thrownexpbottle
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.BOATS) {
                if (ent instanceof Boat) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.ITEMS) {
                if (ent instanceof Item) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.FALLING_BLOCKS) {
                if (ent instanceof FallingBlock) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.MINECARTS) {
                if (ent instanceof Minecart) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.PAINTINGS) {
                if (ent instanceof Painting) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.ITEM_FRAMES) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.TNT) {
                if (ent instanceof TNTPrimed || ent instanceof MinecartTNT) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.XP_ORBS) {
                if (ent instanceof ExperienceOrb) {
                    ent.remove();
                    ++num;
                }
            }
        }

        return num;
    }

    /**
     * Set a sign's text.
     *
     * @param pt
     * @param text
     * @return
     */
    private boolean setSignText(Vector pt, String[] text) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) return false;
        BlockState state = block.getState();
        if (state == null || !(state instanceof Sign)) return false;
        Sign sign = (Sign) state;
        sign.setLine(0, text[0]);
        sign.setLine(1, text[1]);
        sign.setLine(2, text[2]);
        sign.setLine(3, text[3]);
        sign.update();
        return true;
    }

    /**
     * Get a sign's text.
     *
     * @param pt
     * @return
     */
    private String[] getSignText(Vector pt) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) return new String[] { "", "", "", "" };
        BlockState state = block.getState();
        if (state == null || !(state instanceof Sign)) return new String[] { "", "", "", "" };
        Sign sign = (Sign) state;
        String line0 = sign.getLine(0);
        String line1 = sign.getLine(1);
        String line2 = sign.getLine(2);
        String line3 = sign.getLine(3);
        return new String[] {
                line0 != null ? line0 : "",
                line1 != null ? line1 : "",
                line2 != null ? line2 : "",
                line3 != null ? line3 : "",
            };
    }

    /**
     * Get a container block's contents.
     *
     * @param pt
     * @return
     */
    private BaseItemStack[] getContainerBlockContents(Vector pt) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) {
            return new BaseItemStack[0];
        }
        BlockState state = block.getState();
        if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
            return new BaseItemStack[0];
        }

        org.bukkit.inventory.InventoryHolder container = (org.bukkit.inventory.InventoryHolder) state;
        Inventory inven = container.getInventory();
        if (container instanceof Chest) {
            inven = getBlockInventory((Chest) container);
        }
        int size = inven.getSize();
        BaseItemStack[] contents = new BaseItemStack[size];

        for (int i = 0; i < size; ++i) {
            ItemStack bukkitStack = inven.getItem(i);
            if (bukkitStack != null && bukkitStack.getTypeId() > 0) {
                contents[i] = new BaseItemStack(
                        bukkitStack.getTypeId(),
                        bukkitStack.getAmount(),
                        bukkitStack.getDurability());
                try {
                    for (Map.Entry entry : bukkitStack.getEnchantments().entrySet()) {
                        contents[i].getEnchantments().put(entry.getKey().getId(), entry.getValue());
                } catch (Throwable ignore) {}
            }
        }

        return contents;
    }

    /**
     * Set a container block's contents.
     *
     * @param pt
     * @param contents
     * @return
     */
    private boolean setContainerBlockContents(Vector pt, BaseItemStack[] contents) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) {
            return false;
        }
        BlockState state = block.getState();
        if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
            return false;
        }

        org.bukkit.inventory.InventoryHolder chest = (org.bukkit.inventory.InventoryHolder) state;
        Inventory inven = chest.getInventory();
        if (chest instanceof Chest) {
            inven = getBlockInventory((Chest) chest);
        }
        int size = inven.getSize();

        for (int i = 0; i < size; ++i) {
            if (i >= contents.length) {
                break;
            }

            if (contents[i] != null) {
                ItemStack toAdd = new ItemStack(contents[i].getType(),
                        contents[i].getAmount(),
                        contents[i].getData());
                try {
                    for (Map.Entry entry : contents[i].getEnchantments().entrySet()) {
                        toAdd.addEnchantment(Enchantment.getById(entry.getKey()), entry.getValue());
                    }
                } catch (Throwable ignore) {}
                inven.setItem(i, toAdd);
            } else {
                inven.setItem(i, null);
            }
        }
        return true;
    }

    /**
     * Returns whether a block has a valid ID.
     *
     * @param type
     * @return
     */
    @Override
    public boolean isValidBlockType(int type) {
        if (!skipNmsValidBlockCheck) {
            try {
                return (Boolean) nmsValidBlockMethod.invoke(null, type);
            } catch (Throwable e) {
                skipNmsValidBlockCheck = true;
            }
        }
        return Material.getMaterial(type) != null && Material.getMaterial(type).isBlock();
    }

    @Override
    public void checkLoadedChunk(Vector pt) {
        if (!world.isChunkLoaded(pt.getBlockX() >> 4, pt.getBlockZ() >> 4)) {
            world.loadChunk(pt.getBlockX() >> 4, pt.getBlockZ() >> 4);
        }
    }

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof LoadedWorld)) {
            return false;
        }

        return ((LoadedWorld) other).world.equals(world);
    }

    @Override
    public int hashCode() {
        return world.hashCode();
    }

    @Override
    public int getMaxY() {
        return world.getMaxHeight() - 1;
    }

    @Override
    public void fixAfterFastMode(Iterable chunks) {
        for (BlockVector2D chunkPos : chunks) {
            world.refreshChunk(chunkPos.getBlockX(), chunkPos.getBlockZ());
        }
    }

    private static final Map effects = new HashMap();
    static {
        for (Effect effect : Effect.values()) {
            effects.put(effect.getId(), effect);
        }
    }

    @Override
    public boolean playEffect(Vector position, int type, int data) {
        final Effect effect = effects.get(type);
        if (effect == null) {
            return false;
        }

        world.playEffect(BukkitUtils.toLocation(world, position), effect, data);

        return true;
    }

    @Override
    public void simulateBlockMine(Vector pt) {
        world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).breakNaturally();
    }

    @Override
    public LocalEntity[] getEntities(Region region) {
        List entities = new ArrayList();
        for (Vector2D pt : region.getChunks()) {
            if (world.isChunkLoaded(pt.getBlockX(), pt.getBlockZ())) {
                Entity[] ents = world.getChunkAt(pt.getBlockX(), pt.getBlockZ()).getEntities();
                for (Entity ent : ents) {
                    if (region.contains(BukkitUtils.toVector(ent.getLocation()))) {
                        entities.add(BukkitUtils.toLocalEntity(ent));
                    }
                }
            }
        }
        return entities.toArray(new BukkitEntity[entities.size()]);
    }

    @Override
    public int killEntities(LocalEntity... entities) {
        int amount = 0;
        Set toKill = new HashSet();
        for (LocalEntity entity : entities) {
            toKill.add(((BukkitEntity) entity).getEntityId());
        }
        for (Entity entity : world.getEntities()) {
            if (toKill.contains(entity.getUniqueId())) {
                entity.remove();
                ++amount;
        }
        return amount;
    }

    @Override
    public BaseBlock getBlock(Vector pt) {
        int type = getBlockType(pt);
        int data = getBlockData(pt);

        switch (type) {
        case BlockID.WALL_SIGN:
        case BlockID.SIGN_POST:
        //case BlockID.CHEST: // Prevent data loss for now
        //case BlockID.FURNACE:
        //case BlockID.BURNING_FURNACE:
        //case BlockID.DISPENSER:
        //case BlockID.MOB_SPAWNER:
        case BlockID.NOTE_BLOCK:
        case BlockID.HEAD:
            return super.getBlock(pt);
        default:
            if (!skipNmsAccess) {
                try {
                    BukkitBlock block = null;
                    block = (BukkitBlock) nmsGetMethod.invoke(null, getWorld(), pt, type, data);
                    if (block != null) {
                        return block;
                    }
                } catch (Throwable t) {
                    logger.log(Level.WARNING,
                            "WorldEdit: Failed to do NMS access for direct NBT data copy", t);
                    skipNmsAccess = true;
                }
            }
        }

        return super.getBlock(pt);
    }

    @Override
    public boolean setBlock(Vector pt, com.sk89q.worldedit.foundation.Block block, boolean notifyAdjacent) {
        if (!skipNmsSafeSet) {
            try {
                return (Boolean) nmsSetSafeMethod.invoke(null, this, pt, block, notifyAdjacent);
            } catch (Throwable t) {
                logger.log(Level.WARNING, "WorldEdit: Failed to do NMS safe block set", t);
                skipNmsSafeSet = true;
            }
        }

        return super.setBlock(pt, block, notifyAdjacent);
    }
}
=======
// $Id$
/*
 * WorldEdit
 * Copyright (C) 2010 sk89q  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.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 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.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.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;

public class BukkitWorld extends LocalWorld {

    private static final Logger logger = WorldEdit.logger;
    private World world;
    private static boolean skipNmsAccess = false;
    private static boolean skipNmsSafeSet = false;
    private static boolean skipNmsValidBlockCheck = false;

    /*
     * holder for the nmsblock class that we should use
     */
    private static Class nmsBlockType;
    private static Method nmsSetMethod;
    private static Method nmsValidBlockMethod;
    private static Method nmsGetMethod;
    private static Method nmsSetSafeMethod;

    // copied from WG
    private static > T tryEnum(Class enumType, String ... values) {
        for (String val : values) {
            try {
                return Enum.valueOf(enumType, val);
            } catch (IllegalArgumentException e) {}
        }
        return null;
    }
    private static org.bukkit.entity.EntityType tntMinecartType;
    private static boolean checkMinecartType = true;

    /**
     * Construct the object.
     * @param world
     */
    public BukkitWorld(World world) {
        this.world = world;

        if (checkMinecartType) {
            tntMinecartType = tryEnum(org.bukkit.entity.EntityType.class, "MINECART_TNT");
            checkMinecartType = false;
        }
        // check if we have a class we can use for nms access

        // only run once per server startup
        if (nmsBlockType != null || skipNmsAccess || skipNmsSafeSet || skipNmsValidBlockCheck) return;
        Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldEdit");
        if (!(plugin instanceof WorldEditPlugin)) return; // hopefully never happens
        WorldEditPlugin wePlugin = ((WorldEditPlugin) plugin);
        File nmsBlocksDir = new File(wePlugin.getDataFolder() + File.separator + "nmsblocks" + File.separator);
        if (nmsBlocksDir.listFiles() == null) { // no files to use
            skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
            return;
        }
        try {
            // make a classloader that can handle our blocks
            NmsBlockClassLoader loader = new NmsBlockClassLoader(BukkitWorld.class.getClassLoader(), nmsBlocksDir);
            String filename;
            for (File f : nmsBlocksDir.listFiles()) {
                if (!f.isFile()) continue;
                filename = f.getName();
                // load class using magic keyword
                Class testBlock = null;
                try {
                    testBlock = loader.loadClass("CL-NMS" + filename);
                } catch (Throwable e) {
                    // someone is putting things where they don't belong
                    continue;
                }
                filename = filename.replaceFirst(".class$", ""); // get rid of extension
                if (NmsBlock.class.isAssignableFrom(testBlock)) {
                    // got a NmsBlock, test it now
                    Class nmsClass = (Class) testBlock;
                    boolean canUse = false;
                    try {
                        canUse = (Boolean) nmsClass.getMethod("verify", null).invoke(null, null);
                    } catch (Throwable e) {
                        continue;
                    }
                    if (!canUse) continue; // not for this server
                    nmsBlockType = nmsClass;
                    nmsSetMethod = nmsBlockType.getMethod("set", World.class, Vector.class, BaseBlock.class);
                    nmsValidBlockMethod = nmsBlockType.getMethod("isValidBlockType", int.class);
                    nmsGetMethod = nmsBlockType.getMethod("get", World.class, Vector.class, int.class, int.class);
                    nmsSetSafeMethod = nmsBlockType.getMethod("setSafely",
                            BukkitWorld.class, Vector.class, com.sk89q.worldedit.foundation.Block.class, boolean.class);
                    // phew
                    break;
                }
            }
            if (nmsBlockType != null) {
                logger.info("[WorldEdit] Using external NmsBlock for this version: " + nmsBlockType.getName());
            } else {
                // try our default
                try {
                    nmsBlockType = (Class) Class.forName("com.sk89q.worldedit.bukkit.DefaultNmsBlock");
                    boolean canUse = (Boolean) nmsBlockType.getMethod("verify", null).invoke(null, null);
                    if (canUse) {
                        nmsSetMethod = nmsBlockType.getMethod("set", World.class, Vector.class, BaseBlock.class);
                        nmsValidBlockMethod = nmsBlockType.getMethod("isValidBlockType", int.class);
                        nmsGetMethod = nmsBlockType.getMethod("get", World.class, Vector.class, int.class, int.class);
                        nmsSetSafeMethod = nmsBlockType.getMethod("setSafely",
                                BukkitWorld.class, Vector.class, com.sk89q.worldedit.foundation.Block.class, boolean.class);
                        logger.info("[WorldEdit] Using inbuilt NmsBlock for this version.");
                    }
                } catch (Throwable e) {
                    // OMG DEVS WAI U NO SUPPORT  SERVER
                    skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
                    logger.warning("[WorldEdit] No compatible nms block class found.");
                }
            }
        } catch (Throwable e) {
            logger.warning("[WorldEdit] Unable to load NmsBlock classes, make sure they are installed correctly.");
            e.printStackTrace();
            skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
        }
    }

    private class NmsBlockClassLoader extends ClassLoader {
        public File searchDir;
        public NmsBlockClassLoader(ClassLoader parent, File searchDir) {
            super(parent);
            this.searchDir = searchDir;
        }

        @Override
        public Class loadClass(String name) throws ClassNotFoundException {
            if (!name.startsWith("CL-NMS")) {
                return super.loadClass(name);
            } else {
                name = name.replace("CL-NMS", ""); // hacky lol
            }
            try {
                URL url = new File(searchDir, name).toURI().toURL();
                InputStream input = url.openConnection().getInputStream();
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();

                int data = input.read();
                while (data != -1) {
                    buffer.write(data);
                    data = input.read();
                }
                input.close();

                byte[] classData = buffer.toByteArray();

                return defineClass(name.replaceFirst(".class$", ""), classData, 0, classData.length);
            } catch (Throwable e) {
                throw new ClassNotFoundException();
            }
        }
    }

    /**
     * Get the world handle.
     *
     * @return
     */
    public World getWorld() {
        return world;
    }

    /**
     * Get the name of the world
     *
     * @return
     */
    @Override
    public String getName() {
        return world.getName();
    }

    /**
     * Set block type.
     *
     * @param pt
     * @param type
     * @return
     */
    @Override
    public boolean setBlockType(Vector pt, int type) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeId(type);
    }

    /**
     * Set block type.
     *
     * @param pt
     * @param type
     * @return
     */
    @Override
    public boolean setBlockTypeFast(Vector pt, int type) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeId(type, false);
    }

    /**
     * set block type & data
     * @param pt
     * @param type
     * @param data
     * @return
     */
    @Override
    public boolean setTypeIdAndData(Vector pt, int type, int data) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeIdAndData(type, (byte) data, true);
    }

    /**
     * set block type & data
     * @param pt
     * @param type
     * @param data
     * @return
     */
    @Override
    public boolean setTypeIdAndDataFast(Vector pt, int type, int data) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeIdAndData(type, (byte) data, false);
    }

    /**
     * Get block type.
     *
     * @param pt
     * @return
     */
    @Override
    public int getBlockType(Vector pt) {
        return world.getBlockTypeIdAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
    }

    /**
     * Set block data.
     *
     * @param pt
     * @param data
     */
    @Override
    public void setBlockData(Vector pt, int data) {
        world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setData((byte) data);
    }

    /**
     * Set block data.
     *
     * @param pt
     * @param data
     */
    @Override
    public void setBlockDataFast(Vector pt, int data) {
        world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setData((byte) data, false);
    }

    /**
     * Get block data.
     *
     * @param pt
     * @return
     */
    @Override
    public int getBlockData(Vector pt) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getData();
    }

    /**
     * Get block light level.
     *
     * @param pt
     * @return
     */
    @Override
    public int getBlockLightLevel(Vector pt) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getLightLevel();
    }

    /**
     * Get biome type
     *
     * @param pt
     * @return
     */
    @Override
    public BiomeType getBiome(Vector2D pt) {
        Biome bukkitBiome = world.getBiome(pt.getBlockX(), pt.getBlockZ());
        try {
            return BukkitBiomeType.valueOf(bukkitBiome.name());
        } catch (IllegalArgumentException exc) {
            return BiomeType.UNKNOWN;
        }
    }

    @Override
    public void setBiome(Vector2D pt, BiomeType biome) {
        if (biome instanceof BukkitBiomeType) {
            Biome bukkitBiome;
            bukkitBiome = ((BukkitBiomeType) biome).getBukkitBiome();
            world.setBiome(pt.getBlockX(), pt.getBlockZ(), bukkitBiome);
        }
    }

    /**
     * Regenerate an area.
     *
     * @param region
     * @param editSession
     * @return
     */
    @Override
    public boolean regenerate(Region region, EditSession editSession) {
        BaseBlock[] history = new BaseBlock[16 * 16 * (getMaxY() + 1)];

        for (Vector2D chunk : region.getChunks()) {
            Vector min = new Vector(chunk.getBlockX() * 16, 0, chunk.getBlockZ() * 16);

            // First save all the blocks inside
            for (int x = 0; x < 16; ++x) {
                for (int y = 0; y < (getMaxY() + 1); ++y) {
                    for (int z = 0; z < 16; ++z) {
                        Vector pt = min.add(x, y, z);
                        int index = y * 16 * 16 + z * 16 + x;
                        history[index] = editSession.getBlock(pt);
                    }
                }
            }

            try {
                world.regenerateChunk(chunk.getBlockX(), chunk.getBlockZ());
            } catch (Throwable t) {
                t.printStackTrace();
            }

            // Then restore
            for (int x = 0; x < 16; ++x) {
                for (int y = 0; y < (getMaxY() + 1); ++y) {
                    for (int z = 0; z < 16; ++z) {
                        Vector pt = min.add(x, y, z);
                        int index = y * 16 * 16 + z * 16 + x;

                        // We have to restore the block if it was outside
                        if (!region.contains(pt)) {
                            editSession.smartSetBlock(pt, history[index]);
                        } else { // Otherwise fool with history
                            editSession.rememberChange(pt, history[index],
                                    editSession.rawGetBlock(pt));
                        }
                    }
                }
            }
        }

        return true;
    }

    /**
     * Attempts to accurately copy a BaseBlock's extra data to the world.
     *
     * @param pt
     * @param block
     * @return
     */
    @Override
    public boolean copyToWorld(Vector pt, BaseBlock block) {
        if (block instanceof SignBlock) {
            // Signs
            setSignText(pt, ((SignBlock) block).getText());
            return true;
        }

        if (block instanceof FurnaceBlock) {
            // Furnaces
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof Furnace)) return false;
            Furnace bukkit = (Furnace) state;
            FurnaceBlock we = (FurnaceBlock) block;
            bukkit.setBurnTime(we.getBurnTime());
            bukkit.setCookTime(we.getCookTime());
            return setContainerBlockContents(pt, ((ContainerBlock) block).getItems());
        }

        if (block instanceof ContainerBlock) {
            // Chests/dispenser
            return setContainerBlockContents(pt, ((ContainerBlock) block).getItems());
        }

        if (block instanceof MobSpawnerBlock) {
            // Mob spawners
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof CreatureSpawner)) return false;
            CreatureSpawner bukkit = (CreatureSpawner) state;
            MobSpawnerBlock we = (MobSpawnerBlock) block;
            bukkit.setCreatureTypeByName(we.getMobType());
            bukkit.setDelay(we.getDelay());
            return true;
        }

        if (block instanceof NoteBlock) {
            // Note block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.NoteBlock)) return false;
            org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state;
            NoteBlock we = (NoteBlock) block;
            bukkit.setRawNote(we.getNote());
            return true;
        }

        if (block instanceof SkullBlock) {
            // Skull block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.Skull)) return false;
            Skull bukkit = (Skull) state;
            SkullBlock we = (SkullBlock) block;
            // this is dumb
            SkullType skullType = SkullType.SKELETON;
            switch (we.getSkullType()) {
            case 0:
                skullType = SkullType.SKELETON;
                break;
            case 1:
                skullType = SkullType.WITHER;
                break;
            case 2:
                skullType = SkullType.ZOMBIE;
                break;
            case 3:
                skullType = SkullType.PLAYER;
                break;
            case 4:
                skullType = SkullType.CREEPER;
                break;
            }
            bukkit.setSkullType(skullType);
            BlockFace rotation;
            switch (we.getRot()) {
            // soooo dumb
            case 0:
                rotation = BlockFace.NORTH;
                break;
            case 1:
                rotation = BlockFace.NORTH_NORTH_EAST;
                break;
            case 2:
                rotation = BlockFace.NORTH_EAST;
                break;
            case 3:
                rotation = BlockFace.EAST_NORTH_EAST;
                break;
            case 4:
                rotation = BlockFace.EAST;
                break;
            case 5:
                rotation = BlockFace.EAST_SOUTH_EAST;
                break;
            case 6:
                rotation = BlockFace.SOUTH_EAST;
                break;
            case 7:
                rotation = BlockFace.SOUTH_SOUTH_EAST;
                break;
            case 8:
                rotation = BlockFace.SOUTH;
                break;
            case 9:
                rotation = BlockFace.SOUTH_SOUTH_WEST;
                break;
            case 10:
                rotation = BlockFace.SOUTH_WEST;
                break;
            case 11:
                rotation = BlockFace.WEST_SOUTH_WEST;
                break;
            case 12:
                rotation = BlockFace.WEST;
                break;
            case 13:
                rotation = BlockFace.WEST_NORTH_WEST;
                break;
            case 14:
                rotation = BlockFace.NORTH_WEST;
                break;
            case 15:
                rotation = BlockFace.NORTH_NORTH_WEST;
                break;
            default:
                rotation = BlockFace.NORTH;
                break;
            }
            bukkit.setRotation(rotation);
            if (we.getOwner() != null && !we.getOwner().isEmpty()) bukkit.setOwner(we.getOwner());
            bukkit.update(true);
            return true;
        }

        if (!skipNmsAccess) {
            try {
                return (Boolean) nmsSetMethod.invoke(null, world, pt, block);
            } catch (Throwable t) {
                logger.log(Level.WARNING, "WorldEdit: Failed to do NMS access for direct NBT data copy", t);
                skipNmsAccess = true;
            }
        }

        return false;
    }

    /**
     * Attempts to read a BaseBlock's extra data from the world.
     *
     * @param pt
     * @param block
     * @return
     */
    @Override
    public boolean copyFromWorld(Vector pt, BaseBlock block) {
        if (block instanceof SignBlock) {
            // Signs
            ((SignBlock) block).setText(getSignText(pt));
            return true;
        }

        if (block instanceof FurnaceBlock) {
            // Furnaces
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof Furnace)) return false;
            Furnace bukkit = (Furnace) state;
            FurnaceBlock we = (FurnaceBlock) block;
            we.setBurnTime(bukkit.getBurnTime());
            we.setCookTime(bukkit.getCookTime());
            ((ContainerBlock) block).setItems(getContainerBlockContents(pt));
            return true;
        }

        if (block instanceof ContainerBlock) {
            // Chests/dispenser
            ((ContainerBlock) block).setItems(getContainerBlockContents(pt));
            return true;
        }

        if (block instanceof MobSpawnerBlock) {
            // Mob spawners
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof CreatureSpawner)) return false;
            CreatureSpawner bukkit = (CreatureSpawner) state;
            MobSpawnerBlock we = (MobSpawnerBlock) block;
            we.setMobType(bukkit.getCreatureTypeName());
            we.setDelay((short) bukkit.getDelay());
            return true;
        }

        if (block instanceof NoteBlock) {
            // Note block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.NoteBlock)) return false;
            org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state;
            NoteBlock we = (NoteBlock) block;
            we.setNote(bukkit.getRawNote());
            return true;
        }

        if (block instanceof SkullBlock) {
            // Skull block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.Skull)) return false;
            Skull bukkit = (Skull) state;
            SkullBlock we = (SkullBlock) block;
            byte skullType = 0;
    }
            switch (bukkit.getSkullType()) {
            // this is dumb but whoever wrote the class is stupid
            case SKELETON:
                skullType = 0;
                break;
            case WITHER:
                skullType = 1;
                break;
            case ZOMBIE:
                skullType = 2;
                break;
            case PLAYER:
                skullType = 3;
                break;
            case CREEPER:
                skullType = 4;
                break;
            }
            we.setSkullType(skullType);
            byte rot = 0;
            switch (bukkit.getRotation()) {
            // this is even more dumb, hurray for copy/paste
            case NORTH:
                rot = (byte) 0;
                break;
            case NORTH_NORTH_EAST:
                rot = (byte) 1;
                break;
            case NORTH_EAST:
                rot = (byte) 2;
                break;
            case EAST_NORTH_EAST:
                rot = (byte) 3;
                break;
            case EAST:
                rot = (byte) 4;
                break;
            case EAST_SOUTH_EAST:
                rot = (byte) 5;
                break;
            case SOUTH_EAST:
                rot = (byte) 6;
                break;
            case SOUTH_SOUTH_EAST:
                rot = (byte) 7;
                break;
            case SOUTH:
                rot = (byte) 8;
                break;
            case SOUTH_SOUTH_WEST:
                rot = (byte) 9;
                break;
            case SOUTH_WEST:
                rot = (byte) 10;
                break;
            case WEST_SOUTH_WEST:
                rot = (byte) 11;
                break;
            case WEST:
                rot = (byte) 12;
                break;
            case WEST_NORTH_WEST:
                rot = (byte) 13;
                break;
            case NORTH_WEST:
                rot = (byte) 14;
                break;
            case NORTH_NORTH_WEST:
                rot = (byte) 15;
                break;
            }
            we.setRot(rot);
            we.setOwner(bukkit.hasOwner() ? bukkit.getOwner() : "");
            return true;
        }

        return false;
    }

    /**
     * Gets the single block inventory for a potentially double chest.
     * Handles people who have an old version of Bukkit.
     * This should be replaced with {@link org.bukkit.block.Chest#getBlockInventory()}
     * in a few months (now = March 2012) // note from future dev - lol
     *
     * @param chest The chest to get a single block inventory for
     * @return The chest's inventory
     */
    private Inventory getBlockInventory(Chest chest) {
        try {
            return chest.getBlockInventory();
        } catch (Throwable t) {
            if (chest.getInventory() instanceof DoubleChestInventory) {
                DoubleChestInventory inven = (DoubleChestInventory) chest.getInventory();
                if (inven.getLeftSide().getHolder().equals(chest)) {
                    return inven.getLeftSide();
                } else if (inven.getRightSide().getHolder().equals(chest)) {
                    return inven.getRightSide();
                } else {
                    return inven;
                }
            } else {
                return chest.getInventory();
            }
        }

    /**
     * Clear a chest's contents.
     *
     * @param pt
     */
    @Override
    public boolean clearContainerBlockContents(Vector pt) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) {
            return false;
        }
        BlockState state = block.getState();
        if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
            return false;
        }

        org.bukkit.inventory.InventoryHolder chest = (org.bukkit.inventory.InventoryHolder) state;
        Inventory inven = chest.getInventory();
        if (chest instanceof Chest) {
            inven = getBlockInventory((Chest) chest);
        }
        inven.clear();
        return true;
    }

    /**
     * Generate a tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.TREE, editSession, pt);
    }

    /**
     * Generate a big tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateBigTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.BIG_TREE, editSession, pt);
    }

    /**
     * Generate a birch tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateBirchTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.BIRCH, editSession, pt);
    }

    /**
     * Generate a redwood tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateRedwoodTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.REDWOOD, editSession, pt);
    }

    /**
     * Generate a redwood tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateTallRedwoodTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.TALL_REDWOOD, editSession, pt);
    }

    /**
     * An EnumMap that stores which WorldEdit TreeTypes apply to which Bukkit TreeTypes
     */
    private static final EnumMap treeTypeMapping =
            new EnumMap(TreeGenerator.TreeType.class);

    static {
        // Mappings for new TreeType values not yet in Bukkit
        treeTypeMapping.put(TreeGenerator.TreeType.SWAMP, TreeType.TREE);
        treeTypeMapping.put(TreeGenerator.TreeType.JUNGLE_BUSH, TreeType.TREE);
        try {
            treeTypeMapping.put(TreeGenerator.TreeType.SHORT_JUNGLE, TreeType.valueOf("SMALL_JUNGLE"));
        } catch (IllegalArgumentException e) {
            treeTypeMapping.put(TreeGenerator.TreeType.SHORT_JUNGLE, TreeType.TREE);
        }
        for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
            try {
                TreeType bukkitType = TreeType.valueOf(type.name());
                treeTypeMapping.put(type, bukkitType);
            } catch (IllegalArgumentException e) {
                // Unhandled TreeType
            }
        }
        // Other mappings for WE-specific values
        treeTypeMapping.put(TreeGenerator.TreeType.RANDOM, TreeType.BROWN_MUSHROOM);
        treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_REDWOOD, TreeType.REDWOOD);
        treeTypeMapping.put(TreeGenerator.TreeType.PINE, TreeType.REDWOOD);
        for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
            if (treeTypeMapping.get(type) == null) {
                WorldEdit.logger.severe("No TreeType mapping for TreeGenerator.TreeType." + type);
            }
        }
    }

    public static TreeType toBukkitTreeType(TreeGenerator.TreeType type) {
        return treeTypeMapping.get(type);
    }

    @Override
    public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector pt) {
        TreeType bukkitType = toBukkitTreeType(type);
        return type != null && world.generateTree(BukkitUtil.toLocation(world, pt), bukkitType,
                new EditSessionBlockChangeDelegate(editSession));
    }

    /**
     * Drop an item.
     *
     * @param pt
     * @param item
     */
    @Override
    public void dropItem(Vector pt, BaseItemStack item) {
        ItemStack bukkitItem = new ItemStack(item.getType(), item.getAmount(),
                item.getData());
        world.dropItemNaturally(BukkitUtil.toLocation(world, pt), bukkitItem);
    }

    /**
     * Kill mobs in an area.
     *
     * @param origin The center of the area to kill mobs in.
     * @param radius Maximum distance to kill mobs at; radius < 0 means kill all mobs
     * @param flags various flags that determine what to kill
     * @return
     */
    @Override
    public int killMobs(Vector origin, double radius, int flags) {
        boolean killPets = (flags & KillFlags.PETS) != 0;
        boolean killNPCs = (flags & KillFlags.NPCS) != 0;
        boolean killAnimals = (flags & KillFlags.ANIMALS) != 0;
        boolean withLightning = (flags & KillFlags.WITH_LIGHTNING) != 0;
        boolean killGolems = (flags & KillFlags.GOLEMS) != 0;
        boolean killAmbient = (flags & KillFlags.AMBIENT) != 0;

        int num = 0;
        double radiusSq = radius * radius;

        Location bukkitOrigin = BukkitUtil.toLocation(world, origin);

        for (LivingEntity ent : world.getLivingEntities()) {
            if (ent instanceof HumanEntity) {
                continue;
            }

            if (!killAnimals && ent instanceof Animals) {
                continue;
            }

            if (!killPets && ent instanceof Tameable && ((Tameable) ent).isTamed()) {
                continue; // tamed pet
            }

            if (!killGolems && ent instanceof Golem) {
                continue;
            }

            if (!killNPCs && ent instanceof Villager) {
                continue;
            }

            if (!killAmbient && ent instanceof Ambient) {
                continue;
            }

            if (radius < 0 || bukkitOrigin.distanceSquared(ent.getLocation()) <= radiusSq) {
                if (withLightning) {
                    world.strikeLightningEffect(ent.getLocation());
                }
                ent.remove();
                ++num;
            }
        }

        return num;
    }

    /**
     * Remove entities in an area.
     *
     * @param origin
     * @param radius
     * @return
     */
    @Override
    public int removeEntities(EntityType type, Vector origin, int radius) {
        int num = 0;
        double radiusSq = Math.pow(radius, 2);

        for (Entity ent : world.getEntities()) {
            if (radius != -1
                    && origin.distanceSq(BukkitUtil.toVector(ent.getLocation())) > radiusSq) {
                continue;
            }

            if (type == EntityType.ALL) {
                if (ent instanceof Projectile || ent instanceof Boat || ent instanceof Item
                        || ent instanceof FallingBlock || ent instanceof Minecart || ent instanceof Hanging
                        || ent instanceof TNTPrimed || ent instanceof ExperienceOrb) {
                    ent.remove();
                    num++;
                }
            } else if (type == EntityType.PROJECTILES || type == EntityType.ARROWS) {
                if (ent instanceof Projectile) {
                    // covers: arrow, egg, enderpearl, fireball, fish, snowball, throwpotion, thrownexpbottle
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.BOATS) {
                if (ent instanceof Boat) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.ITEMS) {
                if (ent instanceof Item) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.FALLING_BLOCKS) {
                if (ent instanceof FallingBlock) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.MINECARTS) {
                if (ent instanceof Minecart) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.PAINTINGS) {
                if (ent instanceof Painting) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.ITEM_FRAMES) {
                if (ent instanceof ItemFrame) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.TNT) {
                if (ent instanceof TNTPrimed || ent.getType() == tntMinecartType) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.XP_ORBS) {
                if (ent instanceof ExperienceOrb) {
                    ent.remove();
                    ++num;
                }
            }
        }

        return num;
    }

    /**
     * Set a sign's text.
     *
     * @param pt
     * @param text
     * @return
     */
    private boolean setSignText(Vector pt, String[] text) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) return false;
        BlockState state = block.getState();
        if (state == null || !(state instanceof Sign)) return false;
        Sign sign = (Sign) state;
        sign.setLine(0, text[0]);
        sign.setLine(1, text[1]);
        sign.setLine(2, text[2]);
        sign.setLine(3, text[3]);
        sign.update();
        return true;
    }

    /**
     * Get a sign's text.
     *
     * @param pt
     * @return
     */
    private String[] getSignText(Vector pt) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) return new String[] { "", "", "", "" };
        BlockState state = block.getState();
        if (state == null || !(state instanceof Sign)) return new String[] { "", "", "", "" };
        Sign sign = (Sign) state;
        String line0 = sign.getLine(0);
        String line1 = sign.getLine(1);
        String line2 = sign.getLine(2);
        String line3 = sign.getLine(3);
        return new String[] {
                line0 != null ? line0 : "",
                line1 != null ? line1 : "",
                line2 != null ? line2 : "",
                line3 != null ? line3 : "",
            };
    }

    /**
     * Get a container block's contents.
     *
     * @param pt
     * @return
     */
    private BaseItemStack[] getContainerBlockContents(Vector pt) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) {
            return new BaseItemStack[0];
        }
        BlockState state = block.getState();
        if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
            return new BaseItemStack[0];
        }

        org.bukkit.inventory.InventoryHolder container = (org.bukkit.inventory.InventoryHolder) state;
        Inventory inven = container.getInventory();
        if (container instanceof Chest) {
            inven = getBlockInventory((Chest) container);
        }
        int size = inven.getSize();
        BaseItemStack[] contents = new BaseItemStack[size];

        for (int i = 0; i < size; ++i) {
            ItemStack bukkitStack = inven.getItem(i);
            if (bukkitStack != null && bukkitStack.getTypeId() > 0) {
                contents[i] = new BaseItemStack(
                        bukkitStack.getTypeId(),
                        bukkitStack.getAmount(),
                        bukkitStack.getDurability());
                try {
                    for (Map.Entry entry : bukkitStack.getEnchantments().entrySet()) {
                        contents[i].getEnchantments().put(entry.getKey().getId(), entry.getValue());
                    }
                } catch (Throwable ignore) {}
            }
        }

        return contents;
    }

    /**
     * Set a container block's contents.
     *
     * @param pt
     * @param contents
     * @return
     */
    private boolean setContainerBlockContents(Vector pt, BaseItemStack[] contents) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) {
            return false;
        }
        BlockState state = block.getState();
        if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
            return false;
        }

        org.bukkit.inventory.InventoryHolder chest = (org.bukkit.inventory.InventoryHolder) state;
        Inventory inven = chest.getInventory();
        if (chest instanceof Chest) {
            inven = getBlockInventory((Chest) chest);
        }
        int size = inven.getSize();

        for (int i = 0; i < size; ++i) {
            if (i >= contents.length) {
                break;
            }

            if (contents[i] != null) {
                ItemStack toAdd = new ItemStack(contents[i].getType(),
                        contents[i].getAmount(),
                        contents[i].getData());
                try {
                    for (Map.Entry entry : contents[i].getEnchantments().entrySet()) {
                        toAdd.addEnchantment(Enchantment.getById(entry.getKey()), entry.getValue());
                    }
                } catch (Throwable ignore) {}
                inven.setItem(i, toAdd);
            } else {
                inven.setItem(i, null);
            }
        }

        return true;
    }

    /**
     * Returns whether a block has a valid ID.
     *
     * @param type
     * @return
     */
    @Override
    public boolean isValidBlockType(int type) {
        if (!skipNmsValidBlockCheck) {
            try {
                return (Boolean) nmsValidBlockMethod.invoke(null, type);
            } catch (Throwable e) {
                skipNmsValidBlockCheck = true;
            }
        }
        return Material.getMaterial(type) != null && Material.getMaterial(type).isBlock();
    }

    @Override
    public void checkLoadedChunk(Vector pt) {
        if (!world.isChunkLoaded(pt.getBlockX() >> 4, pt.getBlockZ() >> 4)) {
            world.loadChunk(pt.getBlockX() >> 4, pt.getBlockZ() >> 4);
        }
    }

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof BukkitWorld)) {
            return false;
        }

        return ((BukkitWorld) other).world.equals(world);
    }

    @Override
    public int hashCode() {
        return world.hashCode();
    }

    @Override
    public int getMaxY() {
        return world.getMaxHeight() - 1;
    }

    @Override
    public void fixAfterFastMode(Iterable chunks) {
        for (BlockVector2D chunkPos : chunks) {
            world.refreshChunk(chunkPos.getBlockX(), chunkPos.getBlockZ());
        }
    }

    private static final Map effects = new HashMap();
    static {
        for (Effect effect : Effect.values()) {
            effects.put(effect.getId(), effect);
        }
    }

    @Override
    public boolean playEffect(Vector position, int type, int data) {
        final Effect effect = effects.get(type);
        if (effect == null) {
            return false;
        }

        world.playEffect(BukkitUtil.toLocation(world, position), effect, data);

        return true;
    }

    @Override
    public void simulateBlockMine(Vector pt) {
        world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).breakNaturally();
    }

    @Override
    public LocalEntity[] getEntities(Region region) {
        List entities = new ArrayList();
        for (Vector2D pt : region.getChunks()) {
            if (world.isChunkLoaded(pt.getBlockX(), pt.getBlockZ())) {
                Entity[] ents = world.getChunkAt(pt.getBlockX(), pt.getBlockZ()).getEntities();
                for (Entity ent : ents) {
                    if (region.contains(BukkitUtil.toVector(ent.getLocation()))) {
                        entities.add(BukkitUtil.toLocalEntity(ent));
                    }
                }
            }
        }
        return entities.toArray(new BukkitEntity[entities.size()]);
    }

    @Override
    public int killEntities(LocalEntity... entities) {
        int amount = 0;
        Set toKill = new HashSet();
        for (LocalEntity entity : entities) {
            toKill.add(((BukkitEntity) entity).getEntityId());
        }
        for (Entity entity : world.getEntities()) {
            if (toKill.contains(entity.getUniqueId())) {
                entity.remove();
                ++amount;
            }
        }
        return amount;
    }

    @Override
    public BaseBlock getBlock(Vector pt) {
        int type = getBlockType(pt);
        int data = getBlockData(pt);

        switch (type) {
        case BlockID.WALL_SIGN:
        case BlockID.SIGN_POST:
        //case BlockID.CHEST: // Prevent data loss for now
        //case BlockID.FURNACE:
        //case BlockID.BURNING_FURNACE:
        //case BlockID.DISPENSER:
        //case BlockID.MOB_SPAWNER:
        case BlockID.NOTE_BLOCK:
        case BlockID.HEAD:
            return super.getBlock(pt);
        default:
            if (!skipNmsAccess) {
                try {
                    NmsBlock block = null;
                    block = (NmsBlock) nmsGetMethod.invoke(null, getWorld(), pt, type, data);
                    if (block != null) {
                        return block;
                    }
                } catch (Throwable t) {
                    logger.log(Level.WARNING,
                            "WorldEdit: Failed to do NMS access for direct NBT data copy", t);
                    skipNmsAccess = true;
                }
            }
        }

        return super.getBlock(pt);
    }

    @Override
    public boolean setBlock(Vector pt, com.sk89q.worldedit.foundation.Block block, boolean notifyAdjacent) {
        if (!skipNmsSafeSet) {
            try {
                return (Boolean) nmsSetSafeMethod.invoke(null, this, pt, block, notifyAdjacent);
            } catch (Throwable t) {
                logger.log(Level.WARNING, "WorldEdit: Failed to do NMS safe block set", t);
                skipNmsSafeSet = true;
            }
        }

        return super.setBlock(pt, block, notifyAdjacent);
    }
}
>>>>>>> 382b6f35adcec87a0a2272bb84c2be42115a6244:src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java
Solution content
// $Id$

/*
 * WorldEdit
 * Copyright (C) 2010 sk89q  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 org.enginehub.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 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.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.inventory.DoubleChestInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.enginehub.worldedit.EditSession;

import com.sk89q.worldedit.BiomeType;
import com.sk89q.worldedit.BlockVector2D;
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.BukkitBiomeType;
import com.sk89q.worldedit.bukkit.EditSessionBlockChangeDelegate;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.entity.BukkitEntity;
     * @param pt
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.TreeGenerator;

/**
 * An implementation of a world for Bukkit.
 */
public class LoadedWorld extends LocalWorld {

    private static final Logger logger = WorldEdit.logger;
    private World world;
    private static boolean skipNmsAccess = false;
    private static boolean skipNmsSafeSet = false;
    private static boolean skipNmsValidBlockCheck = false;

    /*
     * holder for the nmsblock class that we should use
     */
    private static Class nmsBlockType;
    private static Method nmsSetMethod;
    private static Method nmsValidBlockMethod;
    private static Method nmsGetMethod;
    private static Method nmsSetSafeMethod;

    // copied from WG
    private static > T tryEnum(Class enumType, String ... values) {
        for (String val : values) {
            try {
                return Enum.valueOf(enumType, val);
            } catch (IllegalArgumentException e) {}
        }
        return null;
    }
    private static org.bukkit.entity.EntityType tntMinecartType;
    private static boolean checkMinecartType = true;

    /**
     * Construct the object.
     * @param world
     */
    public LoadedWorld(World world) {
        this.world = world;

        if (checkMinecartType) {
            tntMinecartType = tryEnum(org.bukkit.entity.EntityType.class, "MINECART_TNT");
            checkMinecartType = false;
        }
        // check if we have a class we can use for nms access

        // only run once per server startup
        if (nmsBlockType != null || skipNmsAccess || skipNmsSafeSet || skipNmsValidBlockCheck) return;
        Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldEdit");
        if (!(plugin instanceof WorldEditPlugin)) return; // hopefully never happens
        WorldEditPlugin wePlugin = ((WorldEditPlugin) plugin);
        File nmsBlocksDir = new File(wePlugin.getDataFolder() + File.separator + "nmsblocks" + File.separator);
        if (nmsBlocksDir.listFiles() == null) { // no files to use
            skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
            return;
        }
        try {
            // make a classloader that can handle our blocks
            NmsBlockClassLoader loader = new NmsBlockClassLoader(LoadedWorld.class.getClassLoader(), nmsBlocksDir);
            String filename;
            for (File f : nmsBlocksDir.listFiles()) {
                if (!f.isFile()) continue;
                filename = f.getName();
                // load class using magic keyword
                Class testBlock = null;
                try {
                    testBlock = loader.loadClass("CL-NMS" + filename);
                } catch (Throwable e) {
                    // someone is putting things where they don't belong
                    continue;
                }
                filename = filename.replaceFirst(".class$", ""); // get rid of extension
                if (BukkitBlock.class.isAssignableFrom(testBlock)) {
                    // got a NmsBlock, test it now
                    Class nmsClass = (Class) testBlock;
                    boolean canUse = false;
                    try {
                        canUse = (Boolean) nmsClass.getMethod("verify", null).invoke(null, null);
                    } catch (Throwable e) {
                        continue;
                    }
                    if (!canUse) continue; // not for this server
                    nmsBlockType = nmsClass;
                    nmsSetMethod = nmsBlockType.getMethod("set", World.class, Vector.class, BaseBlock.class);
                    nmsValidBlockMethod = nmsBlockType.getMethod("isValidBlockType", int.class);
                    nmsGetMethod = nmsBlockType.getMethod("get", World.class, Vector.class, int.class, int.class);
                int data = input.read();
                    nmsSetSafeMethod = nmsBlockType.getMethod("setSafely",
                            LoadedWorld.class, Vector.class, com.sk89q.worldedit.foundation.Block.class, boolean.class);
                    // phew
                    break;
                }
            }
            if (nmsBlockType != null) {
                logger.info("[WorldEdit] Using external NmsBlock for this version: " + nmsBlockType.getName());
            } else {
                // try our default
                try {
                    nmsBlockType = (Class) Class.forName("com.sk89q.worldedit.bukkit.DefaultNmsBlock");
                    boolean canUse = (Boolean) nmsBlockType.getMethod("verify", null).invoke(null, null);
                    if (canUse) {
                        nmsSetMethod = nmsBlockType.getMethod("set", World.class, Vector.class, BaseBlock.class);
                        nmsValidBlockMethod = nmsBlockType.getMethod("isValidBlockType", int.class);
                        nmsGetMethod = nmsBlockType.getMethod("get", World.class, Vector.class, int.class, int.class);
                        nmsSetSafeMethod = nmsBlockType.getMethod("setSafely",
                                LoadedWorld.class, Vector.class, com.sk89q.worldedit.foundation.Block.class, boolean.class);
                        logger.info("[WorldEdit] Using inbuilt NmsBlock for this version.");
                    }
                } catch (Throwable e) {
                    // OMG DEVS WAI U NO SUPPORT  SERVER
                    skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
                    logger.warning("[WorldEdit] No compatible nms block class found.");
                }
            }
        } catch (Throwable e) {
            logger.warning("[WorldEdit] Unable to load NmsBlock classes, make sure they are installed correctly.");
            e.printStackTrace();
            skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
        }
    }

    private class NmsBlockClassLoader extends ClassLoader {
        public File searchDir;
        public NmsBlockClassLoader(ClassLoader parent, File searchDir) {
            super(parent);
            this.searchDir = searchDir;
        }

        @Override
        public Class loadClass(String name) throws ClassNotFoundException {
            if (!name.startsWith("CL-NMS")) {
                return super.loadClass(name);
            } else {
                name = name.replace("CL-NMS", ""); // hacky lol
            }
            try {
                URL url = new File(searchDir, name).toURI().toURL();
                InputStream input = url.openConnection().getInputStream();
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                while (data != -1) {
                    buffer.write(data);
                    data = input.read();
                }
                input.close();

                byte[] classData = buffer.toByteArray();

                return defineClass(name.replaceFirst(".class$", ""), classData, 0, classData.length);
            } catch (Throwable e) {
                throw new ClassNotFoundException();
            }
        }
    }

    /**
     * Get the world handle.
     *
     * @return
     */
    public World getWorld() {
        return world;
    }

    /**
     * Get the name of the world
     *
     * @return
     */
    @Override
    public String getName() {
        return world.getName();
    }

    /**
     * Set block type.
     *
     * @param pt
     * @param type
     * @return
     */
     * Get block data.
        if (biome instanceof BukkitBiomeType) {
    @Override
    public boolean setBlockType(Vector pt, int type) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeId(type);
    }

    /**
     * Set block type.
     *
     * @param pt
     * @param type
     * @return
     */
    @Override
    public boolean setBlockTypeFast(Vector pt, int type) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeId(type, false);
    }

    /**
     * set block type & data
     * @param pt
     * @param type
     * @param data
     * @return
     */
    @Override
    public boolean setTypeIdAndData(Vector pt, int type, int data) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeIdAndData(type, (byte) data, true);
    }

    /**
     * set block type & data
     * @param pt
     * @param type
     * @param data
     * @return
     */
    @Override
    public boolean setTypeIdAndDataFast(Vector pt, int type, int data) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeIdAndData(type, (byte) data, false);
    }

    /**
     * Get block type.
     *
     * @param pt
     * @return
     */
    @Override
    public int getBlockType(Vector pt) {
        return world.getBlockTypeIdAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
    }

    /**
     * Set block data.
     *
     * @param pt
     * @param data
     */
    @Override
    public void setBlockData(Vector pt, int data) {
        world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setData((byte) data);
    }

    /**
     * Set block data.
     *
     * @param pt
     * @param data
     */
    @Override
    public void setBlockDataFast(Vector pt, int data) {
        world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setData((byte) data, false);
    }

    /**
     * @return
     */
    @Override
    public int getBlockData(Vector pt) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getData();
    }

    /**
     * Get block light level.
     *
     * @param pt
     * @return
     */
    @Override
    public int getBlockLightLevel(Vector pt) {
        return world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getLightLevel();
    }

    /**
     * Get biome type
     *
     * @param pt
     * @return
     */
    @Override
    public BiomeType getBiome(Vector2D pt) {
        Biome bukkitBiome = world.getBiome(pt.getBlockX(), pt.getBlockZ());
        try {
            return BukkitBiomeType.valueOf(bukkitBiome.name());
        } catch (IllegalArgumentException exc) {
            return BiomeType.UNKNOWN;
        }
    }

    @Override
    public void setBiome(Vector2D pt, BiomeType biome) {
            Biome bukkitBiome;
            bukkitBiome = ((BukkitBiomeType) biome).getBukkitBiome();
            world.setBiome(pt.getBlockX(), pt.getBlockZ(), bukkitBiome);
        }
    }

    /**
     * Regenerate an area.
     *
     * @param region
     * @param editSession
     * @return
     */
    @Override
    public boolean regenerate(Region region, EditSession editSession) {
        BaseBlock[] history = new BaseBlock[16 * 16 * (getMaxY() + 1)];

        for (Vector2D chunk : region.getChunks()) {
            Vector min = new Vector(chunk.getBlockX() * 16, 0, chunk.getBlockZ() * 16);

            // First save all the blocks inside
            for (int x = 0; x < 16; ++x) {
                for (int y = 0; y < (getMaxY() + 1); ++y) {
                    for (int z = 0; z < 16; ++z) {
                        Vector pt = min.add(x, y, z);
                        int index = y * 16 * 16 + z * 16 + x;
                        history[index] = editSession.getBlock(pt);
                    }
                }
            }

            try {
                world.regenerateChunk(chunk.getBlockX(), chunk.getBlockZ());
            } catch (Throwable t) {
                t.printStackTrace();
            }

            // Then restore
            for (int x = 0; x < 16; ++x) {
                for (int y = 0; y < (getMaxY() + 1); ++y) {
                    for (int z = 0; z < 16; ++z) {
                        Vector pt = min.add(x, y, z);
                        int index = y * 16 * 16 + z * 16 + x;

                        // We have to restore the block if it was outside
                        if (!region.contains(pt)) {
                            editSession.smartSetBlock(pt, history[index]);
                        } else { // Otherwise fool with history
                            editSession.rememberChange(pt, history[index],
                                    editSession.rawGetBlock(pt));
                        }
                    }
                }
            }
        }

        return true;
    }

    /**
     * Attempts to accurately copy a BaseBlock's extra data to the world.
     *
     * @param pt
     * @param block
     * @return
     */
    @Override
    public boolean copyToWorld(Vector pt, BaseBlock block) {
        if (block instanceof SignBlock) {
            // Signs
            setSignText(pt, ((SignBlock) block).getText());
            return true;
        }

        if (block instanceof FurnaceBlock) {
            // Furnaces
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof Furnace)) return false;
            Furnace bukkit = (Furnace) state;
            FurnaceBlock we = (FurnaceBlock) block;
            bukkit.setBurnTime(we.getBurnTime());
            bukkit.setCookTime(we.getCookTime());
            return setContainerBlockContents(pt, ((ContainerBlock) block).getItems());
        }

        if (block instanceof ContainerBlock) {
            // Chests/dispenser
            return setContainerBlockContents(pt, ((ContainerBlock) block).getItems());
        }

        if (block instanceof MobSpawnerBlock) {
            // Mob spawners
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof CreatureSpawner)) return false;
            CreatureSpawner bukkit = (CreatureSpawner) state;
            MobSpawnerBlock we = (MobSpawnerBlock) block;
            bukkit.setCreatureTypeByName(we.getMobType());
            bukkit.setDelay(we.getDelay());
            return true;
        }

        if (block instanceof NoteBlock) {
            // Note block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.NoteBlock)) return false;
            org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state;
            NoteBlock we = (NoteBlock) block;
            bukkit.setRawNote(we.getNote());
            return true;
        }

        if (block instanceof SkullBlock) {
            // Skull block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.Skull)) return false;
            Skull bukkit = (Skull) state;
            SkullBlock we = (SkullBlock) block;
            // this is dumb
            SkullType skullType = SkullType.SKELETON;
            switch (we.getSkullType()) {
            case 0:
                skullType = SkullType.SKELETON;
                break;
            case 1:
                skullType = SkullType.WITHER;
                break;
            case 2:
                skullType = SkullType.ZOMBIE;
                break;
            case 3:
                skullType = SkullType.PLAYER;
                break;
            case 4:
                skullType = SkullType.CREEPER;
                break;
            }
            bukkit.setSkullType(skullType);
            BlockFace rotation;
            switch (we.getRot()) {
            // soooo dumb
            case 0:
                rotation = BlockFace.NORTH;
                break;
            case 1:
                rotation = BlockFace.NORTH_NORTH_EAST;
                break;
            case 2:
                rotation = BlockFace.NORTH_EAST;
                break;
            case 3:
                rotation = BlockFace.EAST_NORTH_EAST;
                break;
            case 4:
                rotation = BlockFace.EAST;
                break;
            case 5:
                rotation = BlockFace.EAST_SOUTH_EAST;
                break;
            case 6:
                rotation = BlockFace.SOUTH_EAST;
                break;
            case 7:
                rotation = BlockFace.SOUTH_SOUTH_EAST;
                break;
            case 8:
                rotation = BlockFace.SOUTH;
                break;
            case 9:
                rotation = BlockFace.SOUTH_SOUTH_WEST;
                break;
            case 10:
                rotation = BlockFace.SOUTH_WEST;
                break;
            case 11:
                rotation = BlockFace.WEST_SOUTH_WEST;
                break;
            case 12:
                rotation = BlockFace.WEST;
                break;
            case 13:
                rotation = BlockFace.WEST_NORTH_WEST;
                break;
            case 14:
                rotation = BlockFace.NORTH_WEST;
                break;
            case 15:
                rotation = BlockFace.NORTH_NORTH_WEST;
        }
                break;
            default:
                rotation = BlockFace.NORTH;
                break;
            }
            bukkit.setRotation(rotation);
            if (we.getOwner() != null && !we.getOwner().isEmpty()) bukkit.setOwner(we.getOwner());
            bukkit.update(true);
            return true;
        }

        if (!skipNmsAccess) {
            try {
                return (Boolean) nmsSetMethod.invoke(null, world, pt, block);
            } catch (Throwable t) {
                logger.log(Level.WARNING, "WorldEdit: Failed to do NMS access for direct NBT data copy", t);
                skipNmsAccess = true;
            }
        }

        return false;
    }

    /**
     * Attempts to read a BaseBlock's extra data from the world.
     *
     * @param pt
     * @param block
     * @return
     */
    @Override
    public boolean copyFromWorld(Vector pt, BaseBlock block) {
        if (block instanceof SignBlock) {
            // Signs
            ((SignBlock) block).setText(getSignText(pt));
            return true;
        }

        if (block instanceof FurnaceBlock) {
            // Furnaces
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof Furnace)) return false;
            Furnace bukkit = (Furnace) state;
            FurnaceBlock we = (FurnaceBlock) block;
            we.setBurnTime(bukkit.getBurnTime());
            we.setCookTime(bukkit.getCookTime());
            ((ContainerBlock) block).setItems(getContainerBlockContents(pt));
            return true;
        }

        if (block instanceof ContainerBlock) {
            // Chests/dispenser
            ((ContainerBlock) block).setItems(getContainerBlockContents(pt));
            return true;
        }

        if (block instanceof MobSpawnerBlock) {
            // Mob spawners
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof CreatureSpawner)) return false;
            CreatureSpawner bukkit = (CreatureSpawner) state;
            MobSpawnerBlock we = (MobSpawnerBlock) block;
            we.setMobType(bukkit.getCreatureTypeName());
            we.setDelay((short) bukkit.getDelay());
            return true;
        }

        if (block instanceof NoteBlock) {
            // Note block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.NoteBlock)) return false;
            org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state;
            NoteBlock we = (NoteBlock) block;
            we.setNote(bukkit.getRawNote());
            return true;
        }

        if (block instanceof SkullBlock) {
            // Skull block
            Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
            if (bukkitBlock == null) return false;
            BlockState state = bukkitBlock.getState();
            if (!(state instanceof org.bukkit.block.Skull)) return false;
            Skull bukkit = (Skull) state;
            SkullBlock we = (SkullBlock) block;
            byte skullType = 0;
            switch (bukkit.getSkullType()) {
        }
            // this is dumb but whoever wrote the class is stupid
            case SKELETON:
                skullType = 0;
                break;
            case WITHER:
                skullType = 1;
                break;
            case ZOMBIE:
                skullType = 2;
                break;
            case PLAYER:
                skullType = 3;
                break;
            case CREEPER:
                skullType = 4;
                break;
            }
            we.setSkullType(skullType);
            byte rot = 0;
            switch (bukkit.getRotation()) {
            // this is even more dumb, hurray for copy/paste
            case NORTH:
                rot = (byte) 0;
                break;
            case NORTH_NORTH_EAST:
                rot = (byte) 1;
                break;
            case NORTH_EAST:
                rot = (byte) 2;
                break;
            case EAST_NORTH_EAST:
                rot = (byte) 3;
                break;
            case EAST:
                rot = (byte) 4;
                break;
            case EAST_SOUTH_EAST:
                rot = (byte) 5;
                break;
            case SOUTH_EAST:
                rot = (byte) 6;
                break;
            case SOUTH_SOUTH_EAST:
                rot = (byte) 7;
                break;
            case SOUTH:
                rot = (byte) 8;
                break;
            case SOUTH_SOUTH_WEST:
                rot = (byte) 9;
                break;
            case SOUTH_WEST:
                rot = (byte) 10;
                break;
            case WEST_SOUTH_WEST:
                rot = (byte) 11;
                break;
            case WEST:
                rot = (byte) 12;
                break;
            case WEST_NORTH_WEST:
                rot = (byte) 13;
                break;
            case NORTH_WEST:
                rot = (byte) 14;
                break;
            case NORTH_NORTH_WEST:
                rot = (byte) 15;
                break;
            }
            we.setRot(rot);
            we.setOwner(bukkit.hasOwner() ? bukkit.getOwner() : "");
            return true;

        return false;
    }

    /**
     * Gets the single block inventory for a potentially double chest.
     * Handles people who have an old version of Bukkit.
     * This should be replaced with {@link org.bukkit.block.Chest#getBlockInventory()}
     * in a few months (now = March 2012) // note from future dev - lol
     *
     * @param chest The chest to get a single block inventory for
     * @return The chest's inventory
     */
    private Inventory getBlockInventory(Chest chest) {
        try {
            return chest.getBlockInventory();
        } catch (Throwable t) {
            if (chest.getInventory() instanceof DoubleChestInventory) {
                DoubleChestInventory inven = (DoubleChestInventory) chest.getInventory();
                if (inven.getLeftSide().getHolder().equals(chest)) {
                    return inven.getLeftSide();
                } else if (inven.getRightSide().getHolder().equals(chest)) {
                    return inven.getRightSide();
                } else {
                    return inven;
                }
            } else {
                return chest.getInventory();
            }
    }

    /**
     * Clear a chest's contents.
     *
     * @param pt
     */
    @Override
    public boolean clearContainerBlockContents(Vector pt) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) {
            return false;
        }
        BlockState state = block.getState();
        if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
            return false;
        }

        org.bukkit.inventory.InventoryHolder chest = (org.bukkit.inventory.InventoryHolder) state;
        Inventory inven = chest.getInventory();
        if (chest instanceof Chest) {
            inven = getBlockInventory((Chest) chest);
        }
        inven.clear();
        return true;
    }

    /**
     * Generate a tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.TREE, editSession, pt);
    }

    /**
     * Generate a big tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateBigTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.BIG_TREE, editSession, pt);
    }

    /**
     * Generate a birch tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateBirchTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.BIRCH, editSession, pt);
    }

    /**
     * Generate a redwood tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateRedwoodTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.REDWOOD, editSession, pt);
    }

    /**
     * Generate a redwood tree at a location.
     *
     * @param pt
     * @return
     */
    @Override
    @Deprecated
    public boolean generateTallRedwoodTree(EditSession editSession, Vector pt) {
        return generateTree(TreeGenerator.TreeType.TALL_REDWOOD, editSession, pt);
    }

    /**
     * An EnumMap that stores which WorldEdit TreeTypes apply to which Bukkit TreeTypes
     */
    private static final EnumMap treeTypeMapping =
            new EnumMap(TreeGenerator.TreeType.class);

    static {
        // Mappings for new TreeType values not yet in Bukkit
        treeTypeMapping.put(TreeGenerator.TreeType.SWAMP, TreeType.TREE);
        treeTypeMapping.put(TreeGenerator.TreeType.JUNGLE_BUSH, TreeType.TREE);
        try {
            treeTypeMapping.put(TreeGenerator.TreeType.SHORT_JUNGLE, TreeType.valueOf("SMALL_JUNGLE"));
        } catch (IllegalArgumentException e) {
            treeTypeMapping.put(TreeGenerator.TreeType.SHORT_JUNGLE, TreeType.TREE);
        }
        for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
            try {
                TreeType bukkitType = TreeType.valueOf(type.name());
                treeTypeMapping.put(type, bukkitType);
            } catch (IllegalArgumentException e) {
                // Unhandled TreeType
            }
        }
        // Other mappings for WE-specific values
        treeTypeMapping.put(TreeGenerator.TreeType.RANDOM, TreeType.BROWN_MUSHROOM);
        treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_REDWOOD, TreeType.REDWOOD);
        treeTypeMapping.put(TreeGenerator.TreeType.PINE, TreeType.REDWOOD);
        for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
            if (treeTypeMapping.get(type) == null) {
                WorldEdit.logger.severe("No TreeType mapping for TreeGenerator.TreeType." + type);
            }
        }
    }

    public static TreeType toBukkitTreeType(TreeGenerator.TreeType type) {
        return treeTypeMapping.get(type);
    }

    @Override
    public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector pt) {
        TreeType bukkitType = toBukkitTreeType(type);
        return type != null && world.generateTree(BukkitUtils.toLocation(world, pt), bukkitType,
                new EditSessionBlockChangeDelegate(editSession));
    }

    /**
     * Drop an item.
     *
     * @param pt
     * @param item
     */
    @Override
    public void dropItem(Vector pt, BaseItemStack item) {
        ItemStack bukkitItem = new ItemStack(item.getType(), item.getAmount(),
                item.getData());
        world.dropItemNaturally(BukkitUtils.toLocation(world, pt), bukkitItem);
    }

    /**
     * Kill mobs in an area.
     *
     * @param origin The center of the area to kill mobs in.
     * @param radius Maximum distance to kill mobs at; radius < 0 means kill all mobs
     * @param flags various flags that determine what to kill
     * @return
     */
    @Override
    public int killMobs(Vector origin, double radius, int flags) {
        boolean killPets = (flags & KillFlags.PETS) != 0;
        boolean killNPCs = (flags & KillFlags.NPCS) != 0;
        boolean killAnimals = (flags & KillFlags.ANIMALS) != 0;
        boolean withLightning = (flags & KillFlags.WITH_LIGHTNING) != 0;
        boolean killGolems = (flags & KillFlags.GOLEMS) != 0;
        boolean killAmbient = (flags & KillFlags.AMBIENT) != 0;

        int num = 0;
        double radiusSq = radius * radius;

        Location bukkitOrigin = BukkitUtils.toLocation(world, origin);

        for (LivingEntity ent : world.getLivingEntities()) {
            if (ent instanceof HumanEntity) {
                continue;
            }

            if (!killAnimals && ent instanceof Animals) {
                continue;
            }

            if (!killPets && ent instanceof Tameable && ((Tameable) ent).isTamed()) {
                continue; // tamed pet
            }

            if (!killGolems && ent instanceof Golem) {
                continue;
            }

            if (!killNPCs && ent instanceof Villager) {
                continue;
            }

            if (!killAmbient && ent instanceof Ambient) {
                continue;
            }

            if (radius < 0 || bukkitOrigin.distanceSquared(ent.getLocation()) <= radiusSq) {
                if (withLightning) {
                    world.strikeLightningEffect(ent.getLocation());
                }
                ent.remove();
                ++num;
            }
        }

        return num;
    }

    /**
    }
     * Remove entities in an area.
     *
     * @param origin
     * @param radius
     * @return
     */
    @Override
    public int removeEntities(EntityType type, Vector origin, int radius) {
        int num = 0;
        double radiusSq = Math.pow(radius, 2);

        for (Entity ent : world.getEntities()) {
            if (radius != -1
                    && origin.distanceSq(BukkitUtils.toVector(ent.getLocation())) > radiusSq) {
                continue;
            }

            if (type == EntityType.ALL) {
                if (ent instanceof Projectile || ent instanceof Boat || ent instanceof Item
                        || ent instanceof FallingBlock || ent instanceof Minecart || ent instanceof Hanging
                        || ent instanceof TNTPrimed || ent instanceof ExperienceOrb) {
                    ent.remove();
                    num++;
                }
            } else if (type == EntityType.PROJECTILES || type == EntityType.ARROWS) {
                if (ent instanceof Projectile) {
                    // covers: arrow, egg, enderpearl, fireball, fish, snowball, throwpotion, thrownexpbottle
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.BOATS) {
                if (ent instanceof Boat) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.ITEMS) {
                if (ent instanceof Item) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.FALLING_BLOCKS) {
                if (ent instanceof FallingBlock) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.MINECARTS) {
                if (ent instanceof Minecart) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.PAINTINGS) {
                if (ent instanceof Painting) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.ITEM_FRAMES) {
                if (ent instanceof ItemFrame) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.TNT) {
                if (ent instanceof TNTPrimed || ent.getType() == tntMinecartType) {
                    ent.remove();
                    ++num;
                }
            } else if (type == EntityType.XP_ORBS) {
                if (ent instanceof ExperienceOrb) {
                    ent.remove();
                    ++num;
                }
            }
        }

        return num;
    }

    /**
     * Set a sign's text.
     *
     * @param pt
     * @param text
     * @return
     */
    private boolean setSignText(Vector pt, String[] text) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) return false;
        BlockState state = block.getState();
        if (state == null || !(state instanceof Sign)) return false;
        Sign sign = (Sign) state;
        sign.setLine(0, text[0]);
        sign.setLine(1, text[1]);
        sign.setLine(2, text[2]);
        sign.setLine(3, text[3]);
        sign.update();
        return true;
    }

    /**
     * Set a container block's contents.
     * Get a sign's text.
     *
     * @param pt
     * @return
     */
    private String[] getSignText(Vector pt) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) return new String[] { "", "", "", "" };
        BlockState state = block.getState();
        if (state == null || !(state instanceof Sign)) return new String[] { "", "", "", "" };
        Sign sign = (Sign) state;
        String line0 = sign.getLine(0);
        String line1 = sign.getLine(1);
        String line2 = sign.getLine(2);
        String line3 = sign.getLine(3);
        return new String[] {
                line0 != null ? line0 : "",
                line1 != null ? line1 : "",
                line2 != null ? line2 : "",
                line3 != null ? line3 : "",
            };
    }

    /**
     * Get a container block's contents.
     *
     * @param pt
     * @return
     */
    private BaseItemStack[] getContainerBlockContents(Vector pt) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) {
            return new BaseItemStack[0];
        }
        BlockState state = block.getState();
        if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
            return new BaseItemStack[0];
        }

        org.bukkit.inventory.InventoryHolder container = (org.bukkit.inventory.InventoryHolder) state;
        Inventory inven = container.getInventory();
        if (container instanceof Chest) {
            inven = getBlockInventory((Chest) container);
        }
        int size = inven.getSize();
        BaseItemStack[] contents = new BaseItemStack[size];

        for (int i = 0; i < size; ++i) {
            ItemStack bukkitStack = inven.getItem(i);
            if (bukkitStack != null && bukkitStack.getTypeId() > 0) {
                contents[i] = new BaseItemStack(
                        bukkitStack.getTypeId(),
                        bukkitStack.getAmount(),
                        bukkitStack.getDurability());
                try {
                    for (Map.Entry entry : bukkitStack.getEnchantments().entrySet()) {
                        contents[i].getEnchantments().put(entry.getKey().getId(), entry.getValue());
                    }
                } catch (Throwable ignore) {}
            }
        }

        return contents;
    }

    /**
     *
     * @param pt
     * @param contents
     * @return
     */
    private boolean setContainerBlockContents(Vector pt, BaseItemStack[] contents) {
        Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
        if (block == null) {
            return false;
        }
        BlockState state = block.getState();
        if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
            return false;
        }

        org.bukkit.inventory.InventoryHolder chest = (org.bukkit.inventory.InventoryHolder) state;
        Inventory inven = chest.getInventory();
        if (chest instanceof Chest) {
            inven = getBlockInventory((Chest) chest);
        }
        int size = inven.getSize();

        for (int i = 0; i < size; ++i) {
            if (i >= contents.length) {
                break;
            }

            if (contents[i] != null) {
                ItemStack toAdd = new ItemStack(contents[i].getType(),
                        contents[i].getAmount(),
    @Override
                        contents[i].getData());
                try {
                    for (Map.Entry entry : contents[i].getEnchantments().entrySet()) {
                        toAdd.addEnchantment(Enchantment.getById(entry.getKey()), entry.getValue());
                    }
                } catch (Throwable ignore) {}
                inven.setItem(i, toAdd);
            } else {
                inven.setItem(i, null);
            }
        }

        return true;
    }

    /**
     * Returns whether a block has a valid ID.
     *
     * @param type
     * @return
     */
    @Override
    public boolean isValidBlockType(int type) {
        if (!skipNmsValidBlockCheck) {
            try {
                return (Boolean) nmsValidBlockMethod.invoke(null, type);
            } catch (Throwable e) {
                skipNmsValidBlockCheck = true;
            }
        }
        return Material.getMaterial(type) != null && Material.getMaterial(type).isBlock();
    }

    @Override
    public void checkLoadedChunk(Vector pt) {
        if (!world.isChunkLoaded(pt.getBlockX() >> 4, pt.getBlockZ() >> 4)) {
            world.loadChunk(pt.getBlockX() >> 4, pt.getBlockZ() >> 4);
        }
    }

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof LoadedWorld)) {
            return false;
        }

        return ((LoadedWorld) other).world.equals(world);
    }

    @Override
    public int hashCode() {
        return world.hashCode();
    }

    @Override
    public int getMaxY() {
        return world.getMaxHeight() - 1;
    }

    @Override
    public void fixAfterFastMode(Iterable chunks) {
        for (BlockVector2D chunkPos : chunks) {
            world.refreshChunk(chunkPos.getBlockX(), chunkPos.getBlockZ());
        }
    }

    private static final Map effects = new HashMap();
    static {
        for (Effect effect : Effect.values()) {
            effects.put(effect.getId(), effect);
        }
    }

    public boolean playEffect(Vector position, int type, int data) {
        final Effect effect = effects.get(type);
        if (effect == null) {
            return false;
        }

        world.playEffect(BukkitUtils.toLocation(world, position), effect, data);

        return true;
    }

    @Override
    public void simulateBlockMine(Vector pt) {
        world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).breakNaturally();
    }

    @Override
    public LocalEntity[] getEntities(Region region) {
        List entities = new ArrayList();
        for (Vector2D pt : region.getChunks()) {
            if (world.isChunkLoaded(pt.getBlockX(), pt.getBlockZ())) {
                Entity[] ents = world.getChunkAt(pt.getBlockX(), pt.getBlockZ()).getEntities();
                for (Entity ent : ents) {
                    if (region.contains(BukkitUtils.toVector(ent.getLocation()))) {
                        entities.add(BukkitUtils.toLocalEntity(ent));
                    }
                }
            }
        }
        return entities.toArray(new BukkitEntity[entities.size()]);

    @Override
    public int killEntities(LocalEntity... entities) {
        int amount = 0;
        Set toKill = new HashSet();
        for (LocalEntity entity : entities) {
            toKill.add(((BukkitEntity) entity).getEntityId());
        }
        for (Entity entity : world.getEntities()) {
            if (toKill.contains(entity.getUniqueId())) {
                entity.remove();
                ++amount;
            }
        }
        return amount;
    }

    @Override
    public BaseBlock getBlock(Vector pt) {
        int type = getBlockType(pt);
        int data = getBlockData(pt);

        switch (type) {
        case BlockID.WALL_SIGN:
        case BlockID.SIGN_POST:
        //case BlockID.CHEST: // Prevent data loss for now
        //case BlockID.FURNACE:
        //case BlockID.BURNING_FURNACE:
        //case BlockID.DISPENSER:
        //case BlockID.MOB_SPAWNER:
        case BlockID.NOTE_BLOCK:
        case BlockID.HEAD:
            return super.getBlock(pt);
        default:
            if (!skipNmsAccess) {
                try {
                    BukkitBlock block = null;
                    block = (BukkitBlock) nmsGetMethod.invoke(null, getWorld(), pt, type, data);
                    if (block != null) {
                        return block;
                    }
                } catch (Throwable t) {
                    logger.log(Level.WARNING,
                            "WorldEdit: Failed to do NMS access for direct NBT data copy", t);
                    skipNmsAccess = true;
                }
            }
        }

        return super.getBlock(pt);
    }

    @Override
    public boolean setBlock(Vector pt, com.sk89q.worldedit.foundation.Block block, boolean notifyAdjacent) {
        if (!skipNmsSafeSet) {
            try {
                return (Boolean) nmsSetSafeMethod.invoke(null, this, pt, block, notifyAdjacent);
            } catch (Throwable t) {
                logger.log(Level.WARNING, "WorldEdit: Failed to do NMS safe block set", t);
                skipNmsSafeSet = true;
            }
        }

        return super.setBlock(pt, block, notifyAdjacent);
    }
}
File
LoadedWorld.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration