Newer
Older
ultramine_bukkit / src / main / java / org / bukkit / craftbukkit / block / CraftBlock.java
@vlad20012 vlad20012 on 24 Feb 2017 22 KB initial
package org.bukkit.craftbukkit.block;

import cpw.mods.fml.common.FMLLog;
import net.minecraft.block.BlockCocoa;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.Item;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntitySkull;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.cauldron.block.CraftCustomContainer;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
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.PistonMoveReaction;
import org.bukkit.craftbukkit.CraftChunk;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.BlockVector;
import org.ultramine.mods.bukkit.util.BukkitEnumHelper;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

// Cauldron start
// Cauldron end

public class CraftBlock implements Block
{
	private final CraftChunk chunk;
	private final int x;
	private final int y;
	private final int z;
	// Cauldron start - add support for custom biomes
	private static final Biome[] BIOME_MAPPING = new Biome[BiomeGenBase.getBiomeGenArray().length];
	private static final BiomeGenBase[] BIOMEBASE_MAPPING = new BiomeGenBase[BiomeGenBase.getBiomeGenArray().length];
	// Cauldron end

	public CraftBlock(CraftChunk chunk, int x, int y, int z)
	{
		this.x = x;
		this.y = y;
		this.z = z;
		this.chunk = chunk;
	}

	public net.minecraft.block.Block getNMSBlock()
	{
		return CraftMagicNumbers.getBlock(this); // TODO: UPDATE THIS
	}

	private static net.minecraft.block.Block getNMSBlock(int type)
	{
		return CraftMagicNumbers.getBlock(type);
	}

	public World getWorld()
	{
		return chunk.getWorld();
	}

	public Location getLocation()
	{
		return new Location(getWorld(), x, y, z);
	}

	public Location getLocation(Location loc)
	{
		if(loc != null)
		{
			loc.setWorld(getWorld());
			loc.setX(x);
			loc.setY(y);
			loc.setZ(z);
			loc.setYaw(0);
			loc.setPitch(0);
		}

		return loc;
	}

	public BlockVector getVector()
	{
		return new BlockVector(x, y, z);
	}

	public int getX()
	{
		return x;
	}

	public int getY()
	{
		return y;
	}

	public int getZ()
	{
		return z;
	}

	public Chunk getChunk()
	{
		return chunk;
	}

	public void setData(final byte data)
	{
		chunk.getHandle().worldObj.setBlockMetadataWithNotify(x, y, z, data, 3);
	}

	public void setData(final byte data, boolean applyPhysics)
	{
		if(applyPhysics)
		{
			chunk.getHandle().worldObj.setBlockMetadataWithNotify(x, y, z, data, 3);
		}
		else
		{
			chunk.getHandle().worldObj.setBlockMetadataWithNotify(x, y, z, data, 2);
		}
	}

	public byte getData()
	{
		return (byte) chunk.getHandle().getBlockMetadata(this.x & 0xF, this.y & 0xFF, this.z & 0xF);
	}

	public void setType(final Material type)
	{
		setTypeId(type.getId());
	}

	public boolean setTypeId(final int type)
	{
		return setTypeId(type, true);
	}

	public boolean setTypeId(final int type, final boolean applyPhysics)
	{
		return setTypeIdAndData(type, getData(), applyPhysics);
	}

	public boolean setTypeIdAndData(final int type, final byte data, final boolean applyPhysics)
	{
		if(applyPhysics)
		{
			return chunk.getHandle().worldObj.setBlockSilently(x, y, z, getNMSBlock(type), data, 3);
		}
		else
		{
			boolean success = chunk.getHandle().worldObj.setBlockSilently(x, y, z, getNMSBlock(type), data, 2);
			if(success)
			{
				chunk.getHandle().worldObj.markBlockForUpdate(x, y, z);
			}
			return success;
		}
	}

	public Material getType()
	{
		return Material.getMaterial(getTypeId());
	}

	@Deprecated
	@Override
	public int getTypeId()
	{
		return CraftMagicNumbers.getId(chunk.getHandle().getBlock(this.x & 0xF, this.y & 0xFF, this.z & 0xF));
	}

	public byte getLightLevel()
	{
		return (byte) chunk.getHandle().worldObj.getBlockLightValue(this.x, this.y, this.z);
	}

	public byte getLightFromSky()
	{
		return (byte) chunk.getHandle().getSavedLightValue(EnumSkyBlock.Sky, this.x & 0xF, this.y & 0xFF, this.z & 0xF);
	}

	public byte getLightFromBlocks()
	{
		return (byte) chunk.getHandle().getSavedLightValue(EnumSkyBlock.Block, this.x & 0xF, this.y & 0xFF, this.z & 0xF);
	}


	public Block getFace(final BlockFace face)
	{
		return getRelative(face, 1);
	}

	public Block getFace(final BlockFace face, final int distance)
	{
		return getRelative(face, distance);
	}

	public Block getRelative(final int modX, final int modY, final int modZ)
	{
		return getWorld().getBlockAt(getX() + modX, getY() + modY, getZ() + modZ);
	}

	public Block getRelative(BlockFace face)
	{
		return getRelative(face, 1);
	}

	public Block getRelative(BlockFace face, int distance)
	{
		return getRelative(face.getModX() * distance, face.getModY() * distance, face.getModZ() * distance);
	}

	public BlockFace getFace(final Block block)
	{
		BlockFace[] values = BlockFace.values();

		for(BlockFace face : values)
		{
			if((this.getX() + face.getModX() == block.getX()) &&
					(this.getY() + face.getModY() == block.getY()) &&
					(this.getZ() + face.getModZ() == block.getZ())
					)
			{
				return face;
			}
		}

		return null;
	}

	@Override
	public String toString()
	{
		return "CraftBlock{" + "chunk=" + chunk + ",x=" + x + ",y=" + y + ",z=" + z + ",type=" + getType() + ",data=" + getData() + '}';
	}

	/**
	 * Notch uses a 0-5 to mean DOWN, UP, NORTH, SOUTH, WEST, EAST
	 * in that order all over. This method is convenience to convert for us.
	 *
	 * @return BlockFace the BlockFace represented by this number
	 */
	public static BlockFace notchToBlockFace(int notch)
	{
		switch(notch)
		{
		case 0:
			return BlockFace.DOWN;
		case 1:
			return BlockFace.UP;
		case 2:
			return BlockFace.NORTH;
		case 3:
			return BlockFace.SOUTH;
		case 4:
			return BlockFace.WEST;
		case 5:
			return BlockFace.EAST;
		default:
			return BlockFace.SELF;
		}
	}

	public static int blockFaceToNotch(BlockFace face)
	{
		switch(face)
		{
		case DOWN:
			return 0;
		case UP:
			return 1;
		case NORTH:
			return 2;
		case SOUTH:
			return 3;
		case WEST:
			return 4;
		case EAST:
			return 5;
		default:
			return 7; // Good as anything here, but technically invalid
		}
	}

	public BlockState getState()
	{
		Material material = getType();
		// Cauldron start - if null, check for TE that implements IInventory
		if(material == null)
		{
			TileEntity te = ((CraftWorld) this.getWorld()).getHandle().getTileEntity(this.getX(), this.getY(), this.getZ());
			if(te != null && te instanceof IInventory)
			{
				// In order to allow plugins to properly grab the container location, we must pass a class that extends CraftBlockState and implements InventoryHolder.
				// Note: This will be returned when TileEntity.getOwner() is called
				return new CraftCustomContainer(this);
			}
			// pass default state
			return new CraftBlockState(this);
		}
		// Cauldron end
		switch(material)
		{
		case SIGN:
		case SIGN_POST:
		case WALL_SIGN:
			return new CraftSign(this);
		case CHEST:
		case TRAPPED_CHEST:
			return new CraftChest(this);
		case BURNING_FURNACE:
		case FURNACE:
			return new CraftFurnace(this);
		case DISPENSER:
			return new CraftDispenser(this);
		case DROPPER:
			return new CraftDropper(this);
		case HOPPER:
			return new CraftHopper(this);
		case MOB_SPAWNER:
			return new CraftCreatureSpawner(this);
		case NOTE_BLOCK:
			return new CraftNoteBlock(this);
		case JUKEBOX:
			return new CraftJukebox(this);
		case BREWING_STAND:
			return new CraftBrewingStand(this);
		case SKULL:
			return new CraftSkull(this);
		case COMMAND:
			return new CraftCommandBlock(this);
		case BEACON:
			return new CraftBeacon(this);
		default:
			// Cauldron start
			TileEntity te = ((CraftWorld) this.getWorld()).getHandle().getTileEntity(this.getX(), this.getY(), this.getZ());
			if(te != null && te instanceof IInventory)
			{
				// In order to allow plugins to properly grab the container location, we must pass a class that extends CraftBlockState and implements InventoryHolder.
				// Note: This will be returned when TileEntity.getOwner() is called
				return new CraftCustomContainer(this);
			}
			// pass default state
			// Cauldron end
			return new CraftBlockState(this);
		}
	}

	public Biome getBiome()
	{
		return getWorld().getBiome(x, z);
	}

	public void setBiome(Biome bio)
	{
		getWorld().setBiome(x, z, bio);
	}

	public static Biome biomeBaseToBiome(BiomeGenBase base)
	{
		if(base == null)
		{
			return null;
		}

		return BIOME_MAPPING[base.biomeID];
	}

	public static BiomeGenBase biomeToBiomeBase(Biome bio)
	{
		if(bio == null)
		{
			return null;
		}
		return BIOMEBASE_MAPPING[bio.ordinal()];
	}

	public double getTemperature()
	{
		return getWorld().getTemperature(x, z);
	}

	public double getHumidity()
	{
		return getWorld().getHumidity(x, z);
	}

	public boolean isBlockPowered()
	{
		return chunk.getHandle().worldObj.getBlockPowerInput(x, y, z) > 0;
	}

	public boolean isBlockIndirectlyPowered()
	{
		return chunk.getHandle().worldObj.isBlockIndirectlyGettingPowered(x, y, z);
	}

	@Override
	public boolean equals(Object o)
	{
		if(o == this) return true;
		if(!(o instanceof CraftBlock)) return false;
		CraftBlock other = (CraftBlock) o;

		return this.x == other.x && this.y == other.y && this.z == other.z && this.getWorld().equals(other.getWorld());
	}

	@Override
	public int hashCode()
	{
		return this.y << 24 ^ this.x ^ this.z ^ this.getWorld().hashCode();
	}

	public boolean isBlockFacePowered(BlockFace face)
	{
		return chunk.getHandle().worldObj.getIndirectPowerOutput(x, y, z, blockFaceToNotch(face));
	}

	public boolean isBlockFaceIndirectlyPowered(BlockFace face)
	{
		int power = chunk.getHandle().worldObj.getIndirectPowerLevelTo(x, y, z, blockFaceToNotch(face));

		Block relative = getRelative(face);
		if(relative.getType() == Material.REDSTONE_WIRE)
		{
			return Math.max(power, relative.getData()) > 0;
		}

		return power > 0;
	}

	public int getBlockPower(BlockFace face)
	{
		int power = 0;
		net.minecraft.world.World world = chunk.getHandle().worldObj;
		if((face == BlockFace.DOWN || face == BlockFace.SELF) && world.getIndirectPowerOutput(x, y - 1, z, 0)) power = func_150178_a(world, x, y - 1, z, power);
		if((face == BlockFace.UP || face == BlockFace.SELF) && world.getIndirectPowerOutput(x, y + 1, z, 1)) power = func_150178_a(world, x, y + 1, z, power);
		if((face == BlockFace.EAST || face == BlockFace.SELF) && world.getIndirectPowerOutput(x + 1, y, z, 2)) power = func_150178_a(world, x + 1, y, z, power);
		if((face == BlockFace.WEST || face == BlockFace.SELF) && world.getIndirectPowerOutput(x - 1, y, z, 3)) power = func_150178_a(world, x - 1, y, z, power);
		if((face == BlockFace.NORTH || face == BlockFace.SELF) && world.getIndirectPowerOutput(x, y, z - 1, 4)) power = func_150178_a(world, x, y, z - 1, power);
		if((face == BlockFace.SOUTH || face == BlockFace.SELF) && world.getIndirectPowerOutput(x, y, z + 1, 5)) power = func_150178_a(world, x, y, z - 1, power);
		return power > 0 ? power : (face == BlockFace.SELF ? isBlockIndirectlyPowered() : isBlockFaceIndirectlyPowered(face)) ? 15 : 0;
	}

	private static int func_150178_a(net.minecraft.world.World p_150178_1_, int p_150178_2_, int p_150178_3_, int p_150178_4_, int p_150178_5_)
	{
		if(p_150178_1_.getBlock(p_150178_2_, p_150178_3_, p_150178_4_) != Blocks.redstone_wire)
		{
			return p_150178_5_;
		}
		else
		{
			int i1 = p_150178_1_.getBlockMetadata(p_150178_2_, p_150178_3_, p_150178_4_);
			return i1 > p_150178_5_ ? i1 : p_150178_5_;
		}
	}

	public int getBlockPower()
	{
		return getBlockPower(BlockFace.SELF);
	}

	public boolean isEmpty()
	{
		// Cauldron start - support custom air blocks (Railcraft player aura tracking block)
		//return getType() == Material.AIR;
		if(getType() == Material.AIR) return true;
		if(!(getWorld() instanceof CraftWorld)) return false;
		return ((CraftWorld) getWorld()).getHandle().isAirBlock(getX(), getY(), getZ());
		// Cauldron end
	}

	public boolean isLiquid()
	{
		return (getType() == Material.WATER) || (getType() == Material.STATIONARY_WATER) || (getType() == Material.LAVA) || (getType() == Material.STATIONARY_LAVA);
	}

	public PistonMoveReaction getPistonMoveReaction()
	{
		return PistonMoveReaction.getById(getNMSBlock().getMaterial().getMaterialMobility());
	}

	private boolean itemCausesDrops(ItemStack item)
	{
		net.minecraft.block.Block block = this.getNMSBlock();
		net.minecraft.item.Item itemType = item != null ? net.minecraft.item.Item.getItemById(item.getTypeId()) : null;
		return block != null && (block.getMaterial().isToolNotRequired() || (itemType != null && itemType.func_150897_b(block)));
	}

	public boolean breakNaturally()
	{
		// Order matters here, need to drop before setting to air so skulls can get their data
		net.minecraft.block.Block block = this.getNMSBlock();
		byte data = getData();
		boolean result = false;

		if(block != null && block != Blocks.air)
		{
			block.dropBlockAsItemWithChance(chunk.getHandle().worldObj, x, y, z, data, 1.0F, 0);
			result = true;
		}

		setTypeId(Material.AIR.getId());
		return result;
	}

	public boolean breakNaturally(ItemStack item)
	{
		if(itemCausesDrops(item))
		{
			return breakNaturally();
		}
		else
		{
			return setTypeId(Material.AIR.getId());
		}
	}

	public Collection<ItemStack> getDrops()
	{
		List<ItemStack> drops = new ArrayList<ItemStack>();

		net.minecraft.block.Block block = this.getNMSBlock();
		if(block != Blocks.air)
		{
			byte data = getData();
			// based on nms.Block.dropNaturally
			int count = block.quantityDroppedWithBonus(0, chunk.getHandle().worldObj.rand);
			for(int i = 0; i < count; ++i)
			{
				Item item = block.getItemDropped(data, chunk.getHandle().worldObj.rand, 0);
				if(item != null)
				{
					// Skulls are special, their data is based on the tile entity
					if(Blocks.skull == block)
					{
						net.minecraft.item.ItemStack nmsStack = new net.minecraft.item.ItemStack(item, 1, block.getDamageValue(chunk.getHandle().worldObj, x, y, z));
						TileEntitySkull tileentityskull = (TileEntitySkull) chunk.getHandle().worldObj.getTileEntity(x, y, z);

						if(tileentityskull.func_145904_a() == 3 && tileentityskull.func_152108_a() != null)
						{
							nmsStack.setTagCompound(new NBTTagCompound());
							NBTTagCompound nbttagcompound = new NBTTagCompound();

							NBTUtil.func_152460_a(nbttagcompound, tileentityskull.func_152108_a());
							nmsStack.getTagCompound().setTag("SkullOwner", nbttagcompound);
						}

						drops.add(CraftItemStack.asBukkitCopy(nmsStack));
						// We don't want to drop cocoa blocks, we want to drop cocoa beans.
					}
					else if(Blocks.cocoa == block)
					{
						int dropAmount = (BlockCocoa.func_149987_c(data) >= 2 ? 3 : 1);
						for(int j = 0; j < dropAmount; ++j)
						{
							drops.add(new ItemStack(Material.INK_SACK, 1, (short) 3));
						}
					}
					else
					{
						drops.add(new ItemStack(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(item), 1, (short) block.damageDropped(data)));
					}
				}
			}
		}
		return drops;
	}

	public Collection<ItemStack> getDrops(ItemStack item)
	{
		if(itemCausesDrops(item))
		{
			return getDrops();
		}
		else
		{
			return Collections.emptyList();
		}
	}

	/* Build biome index based lookup table for BiomeBase to Biome mapping */
	public static void initMappings()
	{ // Cauldron - initializer to initMappings() method; called in CraftServer
		//BIOME_MAPPING = new Biome[BiomeGenBase.biomeList.length]; // Cauldron - move up
		//BIOMEBASE_MAPPING = new BiomeGenBase[Biome.values().length]; // Cauldron - move up
		BIOME_MAPPING[BiomeGenBase.swampland.biomeID] = Biome.SWAMPLAND;
		BIOME_MAPPING[BiomeGenBase.forest.biomeID] = Biome.FOREST;
		BIOME_MAPPING[BiomeGenBase.taiga.biomeID] = Biome.TAIGA;
		BIOME_MAPPING[BiomeGenBase.desert.biomeID] = Biome.DESERT;
		BIOME_MAPPING[BiomeGenBase.plains.biomeID] = Biome.PLAINS;
		BIOME_MAPPING[BiomeGenBase.hell.biomeID] = Biome.HELL;
		BIOME_MAPPING[BiomeGenBase.sky.biomeID] = Biome.SKY;
		BIOME_MAPPING[BiomeGenBase.river.biomeID] = Biome.RIVER;
		BIOME_MAPPING[BiomeGenBase.extremeHills.biomeID] = Biome.EXTREME_HILLS;
		BIOME_MAPPING[BiomeGenBase.ocean.biomeID] = Biome.OCEAN;
		BIOME_MAPPING[BiomeGenBase.frozenOcean.biomeID] = Biome.FROZEN_OCEAN;
		BIOME_MAPPING[BiomeGenBase.frozenRiver.biomeID] = Biome.FROZEN_RIVER;
		BIOME_MAPPING[BiomeGenBase.icePlains.biomeID] = Biome.ICE_PLAINS;
		BIOME_MAPPING[BiomeGenBase.iceMountains.biomeID] = Biome.ICE_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.mushroomIsland.biomeID] = Biome.MUSHROOM_ISLAND;
		BIOME_MAPPING[BiomeGenBase.mushroomIslandShore.biomeID] = Biome.MUSHROOM_SHORE;
		BIOME_MAPPING[BiomeGenBase.beach.biomeID] = Biome.BEACH;
		BIOME_MAPPING[BiomeGenBase.desertHills.biomeID] = Biome.DESERT_HILLS;
		BIOME_MAPPING[BiomeGenBase.forestHills.biomeID] = Biome.FOREST_HILLS;
		BIOME_MAPPING[BiomeGenBase.taigaHills.biomeID] = Biome.TAIGA_HILLS;
		BIOME_MAPPING[BiomeGenBase.extremeHillsEdge.biomeID] = Biome.SMALL_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.jungle.biomeID] = Biome.JUNGLE;
		BIOME_MAPPING[BiomeGenBase.jungleHills.biomeID] = Biome.JUNGLE_HILLS;
		BIOME_MAPPING[BiomeGenBase.jungleEdge.biomeID] = Biome.JUNGLE_EDGE;
		BIOME_MAPPING[BiomeGenBase.deepOcean.biomeID] = Biome.DEEP_OCEAN;
		BIOME_MAPPING[BiomeGenBase.stoneBeach.biomeID] = Biome.STONE_BEACH;
		BIOME_MAPPING[BiomeGenBase.coldBeach.biomeID] = Biome.COLD_BEACH;
		BIOME_MAPPING[BiomeGenBase.birchForest.biomeID] = Biome.BIRCH_FOREST;
		BIOME_MAPPING[BiomeGenBase.birchForestHills.biomeID] = Biome.BIRCH_FOREST_HILLS;
		BIOME_MAPPING[BiomeGenBase.roofedForest.biomeID] = Biome.ROOFED_FOREST;
		BIOME_MAPPING[BiomeGenBase.coldTaiga.biomeID] = Biome.COLD_TAIGA;
		BIOME_MAPPING[BiomeGenBase.coldTaigaHills.biomeID] = Biome.COLD_TAIGA_HILLS;
		BIOME_MAPPING[BiomeGenBase.megaTaiga.biomeID] = Biome.MEGA_TAIGA;
		BIOME_MAPPING[BiomeGenBase.megaTaigaHills.biomeID] = Biome.MEGA_TAIGA_HILLS;
		BIOME_MAPPING[BiomeGenBase.extremeHillsPlus.biomeID] = Biome.EXTREME_HILLS_PLUS;
		BIOME_MAPPING[BiomeGenBase.savanna.biomeID] = Biome.SAVANNA;
		BIOME_MAPPING[BiomeGenBase.savannaPlateau.biomeID] = Biome.SAVANNA_PLATEAU;
		BIOME_MAPPING[BiomeGenBase.mesa.biomeID] = Biome.MESA;
		BIOME_MAPPING[BiomeGenBase.mesaPlateau_F.biomeID] = Biome.MESA_PLATEAU_FOREST;
		BIOME_MAPPING[BiomeGenBase.mesaPlateau.biomeID] = Biome.MESA_PLATEAU;

		// Extended Biomes
		BIOME_MAPPING[BiomeGenBase.plains.biomeID + 128] = Biome.SUNFLOWER_PLAINS;
		BIOME_MAPPING[BiomeGenBase.desert.biomeID + 128] = Biome.DESERT_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.forest.biomeID + 128] = Biome.FLOWER_FOREST;
		BIOME_MAPPING[BiomeGenBase.taiga.biomeID + 128] = Biome.TAIGA_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.swampland.biomeID + 128] = Biome.SWAMPLAND_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.icePlains.biomeID + 128] = Biome.ICE_PLAINS_SPIKES;
		BIOME_MAPPING[BiomeGenBase.jungle.biomeID + 128] = Biome.JUNGLE_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.jungleEdge.biomeID + 128] = Biome.JUNGLE_EDGE_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.coldTaiga.biomeID + 128] = Biome.COLD_TAIGA_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.savanna.biomeID + 128] = Biome.SAVANNA_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.savannaPlateau.biomeID + 128] = Biome.SAVANNA_PLATEAU_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.mesa.biomeID + 128] = Biome.MESA_BRYCE;
		BIOME_MAPPING[BiomeGenBase.mesaPlateau_F.biomeID + 128] = Biome.MESA_PLATEAU_FOREST_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.mesaPlateau.biomeID + 128] = Biome.MESA_PLATEAU_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.birchForest.biomeID + 128] = Biome.BIRCH_FOREST_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.birchForestHills.biomeID + 128] = Biome.BIRCH_FOREST_HILLS_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.roofedForest.biomeID + 128] = Biome.ROOFED_FOREST_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.megaTaiga.biomeID + 128] = Biome.MEGA_SPRUCE_TAIGA;
		BIOME_MAPPING[BiomeGenBase.extremeHills.biomeID + 128] = Biome.EXTREME_HILLS_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.extremeHillsPlus.biomeID + 128] = Biome.EXTREME_HILLS_PLUS_MOUNTAINS;
		BIOME_MAPPING[BiomeGenBase.megaTaigaHills.biomeID + 128] = Biome.MEGA_SPRUCE_TAIGA_HILLS;

		//add support for mod biomes
		for(int i = 0; i < BIOME_MAPPING.length; i++)
		{
			if((BiomeGenBase.getBiome(i) != null) && (BIOME_MAPPING[i] == null))
			{
				String name = BiomeGenBase.getBiome(i).biomeName;
				int id = BiomeGenBase.getBiome(i).biomeID;

				FMLLog.fine("Adding biome mapping " + BiomeGenBase.getBiome(i).biomeID + " " + name + " at BiomeBase[" + i + "]");
				BukkitEnumHelper.addBukkitBiome(name); // Forge
				BIOME_MAPPING[BiomeGenBase.getBiome(i).biomeID] = ((Biome) Enum.valueOf(Biome.class, name));
			}
			if(BIOME_MAPPING[i] != null)
			{  /* Build reverse mapping for setBiome */
				BIOMEBASE_MAPPING[BIOME_MAPPING[i].ordinal()] = BiomeGenBase.getBiome(i);
			}
		}
	}

	// Cauldron start - if cauldron.dump-materials is true, dump all materials with their corresponding id's
	public static void dumpMaterials()
	{
//        if (MinecraftServer.getServer().cauldronConfig.dumpMaterials.getValue())
//        {
//            FMLLog.info("Cauldron Dump Materials is ENABLED. Starting dump...");
//            for (int i = 0; i < 32000; i++)
//            {
//                Material material = Material.getMaterial(i);
//                if (material != null)
//                {
//                    FMLLog.info("Found material " + material + " with ID " + i);
//                }
//            }
//            FMLLog.info("Cauldron Dump Materials complete.");
//            FMLLog.info("To disable these dumps, set cauldron.dump-materials to false in bukkit.yml.");
//        }
	}
	// Cauldron end

	public void setMetadata(String metadataKey, MetadataValue newMetadataValue)
	{
		chunk.getCraftWorld().getBlockMetadata().setMetadata(this, metadataKey, newMetadataValue);
	}

	public List<MetadataValue> getMetadata(String metadataKey)
	{
		return chunk.getCraftWorld().getBlockMetadata().getMetadata(this, metadataKey);
	}

	public boolean hasMetadata(String metadataKey)
	{
		return chunk.getCraftWorld().getBlockMetadata().hasMetadata(this, metadataKey);
	}

	public void removeMetadata(String metadataKey, Plugin owningPlugin)
	{
		chunk.getCraftWorld().getBlockMetadata().removeMetadata(this, metadataKey, owningPlugin);
	}
}