AlttdGriefPrevention/src/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java
ryanhamshire 8f489cff16 Stopped trying to fill big holes in creative.
This means admins will have to do repair when players leave gaping holes
in the ground, but also means that having the wrong sea level set in
your config file will no longer create "random" islands in the sky.
Payoffs in better out of the box experience for custom worlds and fewer
instances of helping customers troubleshoot.
2015-05-16 19:32:26 -07:00

737 lines
26 KiB
Java

/*
GriefPrevention Server Plugin for Minecraft
Copyright (C) 2012 Ryan Hamshire
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 <http://www.gnu.org/licenses/>.
*/
package me.ryanhamshire.GriefPrevention;
import java.util.ArrayList;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World.Environment;
import org.bukkit.block.Biome;
import org.bukkit.entity.Player;
//non-main-thread task which processes world data to repair the unnatural
//after processing is complete, creates a main thread task to make the necessary changes to the world
class RestoreNatureProcessingTask implements Runnable
{
//world information captured from the main thread
//will be updated and sent back to main thread to be applied to the world
private BlockSnapshot[][][] snapshots;
//other information collected from the main thread.
//not to be updated, only to be passed back to main thread to provide some context about the operation
private int miny;
private Environment environment;
private Location lesserBoundaryCorner;
private Location greaterBoundaryCorner;
private Player player; //absolutely must not be accessed. not thread safe.
private Biome biome;
private boolean creativeMode;
private int seaLevel;
private boolean aggressiveMode;
//two lists of materials
private ArrayList<Integer> notAllowedToHang; //natural blocks which don't naturally hang in their air
private ArrayList<Integer> playerBlocks; //a "complete" list of player-placed blocks. MUST BE MAINTAINED as patches introduce more
public RestoreNatureProcessingTask(BlockSnapshot[][][] snapshots, int miny, Environment environment, Biome biome, Location lesserBoundaryCorner, Location greaterBoundaryCorner, int seaLevel, boolean aggressiveMode, boolean creativeMode, Player player)
{
this.snapshots = snapshots;
this.miny = miny;
if(this.miny < 0) this.miny = 0;
this.environment = environment;
this.lesserBoundaryCorner = lesserBoundaryCorner;
this.greaterBoundaryCorner = greaterBoundaryCorner;
this.biome = biome;
this.seaLevel = seaLevel;
this.aggressiveMode = aggressiveMode;
this.player = player;
this.creativeMode = creativeMode;
this.notAllowedToHang = new ArrayList<Integer>();
this.notAllowedToHang.add(Material.DIRT.getId());
this.notAllowedToHang.add(Material.LONG_GRASS.getId());
this.notAllowedToHang.add(Material.SNOW.getId());
this.notAllowedToHang.add(Material.LOG.getId());
if(this.aggressiveMode)
{
this.notAllowedToHang.add(Material.GRASS.getId());
this.notAllowedToHang.add(Material.STONE.getId());
}
this.playerBlocks = new ArrayList<Integer>();
this.playerBlocks.addAll(RestoreNatureProcessingTask.getPlayerBlocks(this.environment, this.biome));
//in aggressive or creative world mode, also treat these blocks as user placed, to be removed
//this is helpful in the few cases where griefers intentionally use natural blocks to grief,
//like a single-block tower of iron ore or a giant penis constructed with melons
if(this.aggressiveMode || this.creativeMode)
{
this.playerBlocks.add(Material.IRON_ORE.getId());
this.playerBlocks.add(Material.GOLD_ORE.getId());
this.playerBlocks.add(Material.DIAMOND_ORE.getId());
this.playerBlocks.add(Material.MELON_BLOCK.getId());
this.playerBlocks.add(Material.MELON_STEM.getId());
this.playerBlocks.add(Material.BEDROCK.getId());
this.playerBlocks.add(Material.COAL_ORE.getId());
this.playerBlocks.add(Material.PUMPKIN.getId());
this.playerBlocks.add(Material.PUMPKIN_STEM.getId());
this.playerBlocks.add(Material.MELON.getId());
}
if(this.aggressiveMode)
{
this.playerBlocks.add(Material.LEAVES.getId());
this.playerBlocks.add(Material.LOG.getId());
this.playerBlocks.add(Material.LOG_2.getId());
this.playerBlocks.add(Material.VINE.getId());
}
}
@Override
public void run()
{
//order is important!
//remove sandstone which appears to be unnatural
this.removeSandstone();
//remove any blocks which are definitely player placed
this.removePlayerBlocks();
//reduce large outcroppings of stone, sandstone
this.reduceStone();
//reduce logs, except in jungle biomes
this.reduceLogs();
//remove natural blocks which are unnaturally hanging in the air
this.removeHanging();
//remove natural blocks which are unnaturally stacked high
this.removeWallsAndTowers();
//fill unnatural thin trenches and single-block potholes
this.fillHolesAndTrenches();
//fill water depressions and fix unnatural surface ripples
this.fixWater();
//remove water/lava above sea level
this.removeDumpedFluids();
//cover surface stone and gravel with sand or grass, as the biome requires
this.coverSurfaceStone();
//remove any player-placed leaves
this.removePlayerLeaves();
//schedule main thread task to apply the result to the world
RestoreNatureExecutionTask task = new RestoreNatureExecutionTask(this.snapshots, this.miny, this.lesserBoundaryCorner, this.greaterBoundaryCorner, this.player);
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task);
}
private void removePlayerLeaves()
{
if(this.seaLevel < 1) return;
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
for(int y = this.seaLevel - 1; y < snapshots[0].length; y++)
{
//note: see minecraft wiki data values for leaves
BlockSnapshot block = snapshots[x][y][z];
if(block.typeId == Material.LEAVES.getId() && (block.data & 0x4) != 0)
{
block.typeId = Material.AIR.getId();
}
}
}
}
}
//converts sandstone adjacent to sand to sand, and any other sandstone to air
private void removeSandstone()
{
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
for(int y = snapshots[0].length - 2; y > miny; y--)
{
if(snapshots[x][y][z].typeId != Material.SANDSTONE.getId()) continue;
BlockSnapshot leftBlock = this.snapshots[x + 1][y][z];
BlockSnapshot rightBlock = this.snapshots[x - 1][y][z];
BlockSnapshot upBlock = this.snapshots[x][y][z + 1];
BlockSnapshot downBlock = this.snapshots[x][y][z - 1];
BlockSnapshot underBlock = this.snapshots[x][y - 1][z];
BlockSnapshot aboveBlock = this.snapshots[x][y + 1][z];
//skip blocks which may cause a cave-in
if(aboveBlock.typeId == Material.SAND.getId() && underBlock.typeId == Material.AIR.getId()) continue;
//count adjacent non-air/non-leaf blocks
if( leftBlock.typeId == Material.SAND.getId() ||
rightBlock.typeId == Material.SAND.getId() ||
upBlock.typeId == Material.SAND.getId() ||
downBlock.typeId == Material.SAND.getId() ||
aboveBlock.typeId == Material.SAND.getId() ||
underBlock.typeId == Material.SAND.getId())
{
snapshots[x][y][z].typeId = Material.SAND.getId();
}
else
{
snapshots[x][y][z].typeId = Material.AIR.getId();
}
}
}
}
}
private void reduceStone()
{
if(this.seaLevel < 1) return;
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
int thisy = this.highestY(x, z, true);
while(thisy > this.seaLevel - 1 && (this.snapshots[x][thisy][z].typeId == Material.STONE.getId() || this.snapshots[x][thisy][z].typeId == Material.SANDSTONE.getId()))
{
BlockSnapshot leftBlock = this.snapshots[x + 1][thisy][z];
BlockSnapshot rightBlock = this.snapshots[x - 1][thisy][z];
BlockSnapshot upBlock = this.snapshots[x][thisy][z + 1];
BlockSnapshot downBlock = this.snapshots[x][thisy][z - 1];
//count adjacent non-air/non-leaf blocks
byte adjacentBlockCount = 0;
if(leftBlock.typeId != Material.AIR.getId() && leftBlock.typeId != Material.LEAVES.getId() && leftBlock.typeId != Material.VINE.getId())
{
adjacentBlockCount++;
}
if(rightBlock.typeId != Material.AIR.getId() && rightBlock.typeId != Material.LEAVES.getId() && rightBlock.typeId != Material.VINE.getId())
{
adjacentBlockCount++;
}
if(downBlock.typeId != Material.AIR.getId() && downBlock.typeId != Material.LEAVES.getId() && downBlock.typeId != Material.VINE.getId())
{
adjacentBlockCount++;
}
if(upBlock.typeId != Material.AIR.getId() && upBlock.typeId != Material.LEAVES.getId() && upBlock.typeId != Material.VINE.getId())
{
adjacentBlockCount++;
}
if(adjacentBlockCount < 3)
{
this.snapshots[x][thisy][z].typeId = Material.AIR.getId();
}
thisy--;
}
}
}
}
private void reduceLogs()
{
if(this.seaLevel < 1) return;
boolean jungleBiome = this.biome == Biome.JUNGLE || this.biome == Biome.JUNGLE_HILLS;
//scan all blocks above sea level
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
for(int y = this.seaLevel - 1; y < snapshots[0].length; y++)
{
BlockSnapshot block = snapshots[x][y][z];
//skip non-logs
if(block.typeId != Material.LOG.getId()) continue;
if(block.typeId != Material.LOG_2.getId()) continue;
//if in jungle biome, skip jungle logs
if(jungleBiome && block.data == 3) continue;
//examine adjacent blocks for logs
BlockSnapshot leftBlock = this.snapshots[x + 1][y][z];
BlockSnapshot rightBlock = this.snapshots[x - 1][y][z];
BlockSnapshot upBlock = this.snapshots[x][y][z + 1];
BlockSnapshot downBlock = this.snapshots[x][y][z - 1];
//if any, remove the log
if(leftBlock.typeId == Material.LOG.getId() || rightBlock.typeId == Material.LOG.getId() || upBlock.typeId == Material.LOG.getId() || downBlock.typeId == Material.LOG.getId())
{
this.snapshots[x][y][z].typeId = Material.AIR.getId();
}
}
}
}
}
private void removePlayerBlocks()
{
int miny = this.miny;
if(miny < 1) miny = 1;
//remove all player blocks
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
for(int y = miny; y < snapshots[0].length - 1; y++)
{
BlockSnapshot block = snapshots[x][y][z];
if(this.playerBlocks.contains(block.typeId))
{
block.typeId = Material.AIR.getId();
}
}
}
}
}
private void removeHanging()
{
int miny = this.miny;
if(miny < 1) miny = 1;
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
for(int y = miny; y < snapshots[0].length - 1; y++)
{
BlockSnapshot block = snapshots[x][y][z];
BlockSnapshot underBlock = snapshots[x][y - 1][z];
if(underBlock.typeId == Material.AIR.getId() || underBlock.typeId == Material.STATIONARY_WATER.getId() || underBlock.typeId == Material.STATIONARY_LAVA.getId() || underBlock.typeId == Material.LEAVES.getId())
{
if(this.notAllowedToHang.contains(block.typeId))
{
block.typeId = Material.AIR.getId();
}
}
}
}
}
}
private void removeWallsAndTowers()
{
int [] excludedBlocksArray = new int []
{
Material.CACTUS.getId(),
Material.LONG_GRASS.getId(),
Material.RED_MUSHROOM.getId(),
Material.BROWN_MUSHROOM.getId(),
Material.DEAD_BUSH.getId(),
Material.SAPLING.getId(),
Material.YELLOW_FLOWER.getId(),
Material.RED_ROSE.getId(),
Material.SUGAR_CANE_BLOCK.getId(),
Material.VINE.getId(),
Material.PUMPKIN.getId(),
Material.WATER_LILY.getId(),
Material.LEAVES.getId()
};
ArrayList<Integer> excludedBlocks = new ArrayList<Integer>();
for(int i = 0; i < excludedBlocksArray.length; i++) excludedBlocks.add(excludedBlocksArray[i]);
boolean changed;
do
{
changed = false;
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
int thisy = this.highestY(x, z, false);
if(excludedBlocks.contains(this.snapshots[x][thisy][z].typeId)) continue;
int righty = this.highestY(x + 1, z, false);
int lefty = this.highestY(x - 1, z, false);
while(lefty < thisy && righty < thisy)
{
this.snapshots[x][thisy--][z].typeId = Material.AIR.getId();
changed = true;
}
int upy = this.highestY(x, z + 1, false);
int downy = this.highestY(x, z - 1, false);
while(upy < thisy && downy < thisy)
{
this.snapshots[x][thisy--][z].typeId = Material.AIR.getId();
changed = true;
}
}
}
}while(changed);
}
private void coverSurfaceStone()
{
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
int y = this.highestY(x, z, true);
BlockSnapshot block = snapshots[x][y][z];
if(block.typeId == Material.STONE.getId() || block.typeId == Material.GRAVEL.getId() || block.typeId == Material.SOIL.getId() || block.typeId == Material.DIRT.getId() || block.typeId == Material.SANDSTONE.getId())
{
if(this.biome == Biome.DESERT || this.biome == Biome.DESERT_HILLS || this.biome == Biome.BEACH)
{
this.snapshots[x][y][z].typeId = Material.SAND.getId();
}
else
{
this.snapshots[x][y][z].typeId = Material.GRASS.getId();
}
}
}
}
}
private void fillHolesAndTrenches()
{
ArrayList<Integer> fillableBlocks = new ArrayList<Integer>();
fillableBlocks.add(Material.AIR.getId());
fillableBlocks.add(Material.STATIONARY_WATER.getId());
fillableBlocks.add(Material.STATIONARY_LAVA.getId());
fillableBlocks.add(Material.LONG_GRASS.getId());
ArrayList<Integer> notSuitableForFillBlocks = new ArrayList<Integer>();
notSuitableForFillBlocks.add(Material.LONG_GRASS.getId());
notSuitableForFillBlocks.add(Material.CACTUS.getId());
notSuitableForFillBlocks.add(Material.STATIONARY_WATER.getId());
notSuitableForFillBlocks.add(Material.STATIONARY_LAVA.getId());
notSuitableForFillBlocks.add(Material.LOG.getId());
notSuitableForFillBlocks.add(Material.LOG_2.getId());
boolean changed;
do
{
changed = false;
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
for(int y = 0; y < snapshots[0].length - 1; y++)
{
BlockSnapshot block = this.snapshots[x][y][z];
if(!fillableBlocks.contains(block.typeId)) continue;
BlockSnapshot leftBlock = this.snapshots[x + 1][y][z];
BlockSnapshot rightBlock = this.snapshots[x - 1][y][z];
if(!fillableBlocks.contains(leftBlock.typeId) && !fillableBlocks.contains(rightBlock.typeId))
{
if(!notSuitableForFillBlocks.contains(rightBlock.typeId))
{
block.typeId = rightBlock.typeId;
changed = true;
}
}
BlockSnapshot upBlock = this.snapshots[x][y][z + 1];
BlockSnapshot downBlock = this.snapshots[x][y][z - 1];
if(!fillableBlocks.contains(upBlock.typeId) && !fillableBlocks.contains(downBlock.typeId))
{
if(!notSuitableForFillBlocks.contains(downBlock.typeId))
{
block.typeId = downBlock.typeId;
changed = true;
}
}
}
}
}
}while(changed);
}
private void fixWater()
{
int miny = this.miny;
if(miny < 1) miny = 1;
boolean changed;
//remove hanging water or lava
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
for(int y = miny; y < snapshots[0].length - 1; y++)
{
BlockSnapshot block = this.snapshots[x][y][z];
BlockSnapshot underBlock = this.snapshots[x][y][z];
if(block.typeId == Material.STATIONARY_WATER.getId() || block.typeId == Material.STATIONARY_LAVA.getId())
{
if(underBlock.typeId == Material.AIR.getId() || (underBlock.data != 0))
{
block.typeId = Material.AIR.getId();
}
}
}
}
}
//fill water depressions
do
{
changed = false;
for(int y = Math.max(this.seaLevel - 10, 0); y <= this.seaLevel; y++)
{
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
BlockSnapshot block = snapshots[x][y][z];
//only consider air blocks and flowing water blocks for upgrade to water source blocks
if(block.typeId == Material.AIR.getId() || (block.typeId == Material.STATIONARY_WATER.getId() && block.data != 0))
{
BlockSnapshot leftBlock = this.snapshots[x + 1][y][z];
BlockSnapshot rightBlock = this.snapshots[x - 1][y][z];
BlockSnapshot upBlock = this.snapshots[x][y][z + 1];
BlockSnapshot downBlock = this.snapshots[x][y][z - 1];
BlockSnapshot underBlock = this.snapshots[x][y - 1][z];
//block underneath MUST be source water
if(underBlock.typeId != Material.STATIONARY_WATER.getId() || underBlock.data != 0) continue;
//count adjacent source water blocks
byte adjacentSourceWaterCount = 0;
if(leftBlock.typeId == Material.STATIONARY_WATER.getId() && leftBlock.data == 0)
{
adjacentSourceWaterCount++;
}
if(rightBlock.typeId == Material.STATIONARY_WATER.getId() && rightBlock.data == 0)
{
adjacentSourceWaterCount++;
}
if(upBlock.typeId == Material.STATIONARY_WATER.getId() && upBlock.data == 0)
{
adjacentSourceWaterCount++;
}
if(downBlock.typeId == Material.STATIONARY_WATER.getId() && downBlock.data == 0)
{
adjacentSourceWaterCount++;
}
//at least two adjacent blocks must be source water
if(adjacentSourceWaterCount >= 2)
{
block.typeId = Material.STATIONARY_WATER.getId();
block.data = 0;
changed = true;
}
}
}
}
}
}while(changed);
}
private void removeDumpedFluids()
{
if(this.seaLevel < 1) return;
//remove any surface water or lava above sea level, presumed to be placed by players
//sometimes, this is naturally generated. but replacing it is very easy with a bucket, so overall this is a good plan
if(this.environment == Environment.NETHER) return;
for(int x = 1; x < snapshots.length - 1; x++)
{
for(int z = 1; z < snapshots[0][0].length - 1; z++)
{
for(int y = this.seaLevel - 1; y < snapshots[0].length - 1; y++)
{
BlockSnapshot block = snapshots[x][y][z];
if(block.typeId == Material.STATIONARY_WATER.getId() || block.typeId == Material.STATIONARY_LAVA.getId() ||
block.typeId == Material.WATER.getId() || block.typeId == Material.LAVA.getId())
{
block.typeId = Material.AIR.getId();
}
}
}
}
}
private int highestY(int x, int z, boolean ignoreLeaves)
{
int y;
for(y = snapshots[0].length - 1; y > 0; y--)
{
BlockSnapshot block = this.snapshots[x][y][z];
if(block.typeId != Material.AIR.getId() &&
!(ignoreLeaves && block.typeId == Material.SNOW.getId()) &&
!(ignoreLeaves && block.typeId == Material.LEAVES.getId()) &&
!(block.typeId == Material.STATIONARY_WATER.getId()) &&
!(block.typeId == Material.WATER.getId()) &&
!(block.typeId == Material.LAVA.getId()) &&
!(block.typeId == Material.STATIONARY_LAVA.getId()))
{
return y;
}
}
return y;
}
static ArrayList<Integer> getPlayerBlocks(Environment environment, Biome biome)
{
//NOTE on this list. why not make a list of natural blocks?
//answer: better to leave a few player blocks than to remove too many natural blocks. remember we're "restoring nature"
//a few extra player blocks can be manually removed, but it will be impossible to guess exactly which natural materials to use in manual repair of an overzealous block removal
ArrayList<Integer> playerBlocks = new ArrayList<Integer>();
playerBlocks.add(Material.FIRE.getId());
playerBlocks.add(Material.BED_BLOCK.getId());
playerBlocks.add(Material.WOOD.getId());
playerBlocks.add(Material.BOOKSHELF.getId());
playerBlocks.add(Material.BREWING_STAND.getId());
playerBlocks.add(Material.BRICK.getId());
playerBlocks.add(Material.COBBLESTONE.getId());
playerBlocks.add(Material.GLASS.getId());
playerBlocks.add(Material.LAPIS_BLOCK.getId());
playerBlocks.add(Material.DISPENSER.getId());
playerBlocks.add(Material.NOTE_BLOCK.getId());
playerBlocks.add(Material.POWERED_RAIL.getId());
playerBlocks.add(Material.DETECTOR_RAIL.getId());
playerBlocks.add(Material.PISTON_STICKY_BASE.getId());
playerBlocks.add(Material.PISTON_BASE.getId());
playerBlocks.add(Material.PISTON_EXTENSION.getId());
playerBlocks.add(Material.WOOL.getId());
playerBlocks.add(Material.PISTON_MOVING_PIECE.getId());
playerBlocks.add(Material.GOLD_BLOCK.getId());
playerBlocks.add(Material.IRON_BLOCK.getId());
playerBlocks.add(Material.DOUBLE_STEP.getId());
playerBlocks.add(Material.STEP.getId());
playerBlocks.add(Material.CROPS.getId());
playerBlocks.add(Material.TNT.getId());
playerBlocks.add(Material.MOSSY_COBBLESTONE.getId());
playerBlocks.add(Material.TORCH.getId());
playerBlocks.add(Material.FIRE.getId());
playerBlocks.add(Material.WOOD_STAIRS.getId());
playerBlocks.add(Material.CHEST.getId());
playerBlocks.add(Material.REDSTONE_WIRE.getId());
playerBlocks.add(Material.DIAMOND_BLOCK.getId());
playerBlocks.add(Material.WORKBENCH.getId());
playerBlocks.add(Material.FURNACE.getId());
playerBlocks.add(Material.BURNING_FURNACE.getId());
playerBlocks.add(Material.WOODEN_DOOR.getId());
playerBlocks.add(Material.SIGN_POST.getId());
playerBlocks.add(Material.LADDER.getId());
playerBlocks.add(Material.RAILS.getId());
playerBlocks.add(Material.COBBLESTONE_STAIRS.getId());
playerBlocks.add(Material.WALL_SIGN.getId());
playerBlocks.add(Material.STONE_PLATE.getId());
playerBlocks.add(Material.LEVER.getId());
playerBlocks.add(Material.IRON_DOOR_BLOCK.getId());
playerBlocks.add(Material.WOOD_PLATE.getId());
playerBlocks.add(Material.REDSTONE_TORCH_ON.getId());
playerBlocks.add(Material.REDSTONE_TORCH_OFF.getId());
playerBlocks.add(Material.STONE_BUTTON.getId());
playerBlocks.add(Material.SNOW_BLOCK.getId());
playerBlocks.add(Material.JUKEBOX.getId());
playerBlocks.add(Material.FENCE.getId());
playerBlocks.add(Material.PORTAL.getId());
playerBlocks.add(Material.JACK_O_LANTERN.getId());
playerBlocks.add(Material.CAKE_BLOCK.getId());
playerBlocks.add(Material.DIODE_BLOCK_ON.getId());
playerBlocks.add(Material.DIODE_BLOCK_OFF.getId());
playerBlocks.add(Material.TRAP_DOOR.getId());
playerBlocks.add(Material.SMOOTH_BRICK.getId());
playerBlocks.add(Material.HUGE_MUSHROOM_1.getId());
playerBlocks.add(Material.HUGE_MUSHROOM_2.getId());
playerBlocks.add(Material.IRON_FENCE.getId());
playerBlocks.add(Material.THIN_GLASS.getId());
playerBlocks.add(Material.MELON_STEM.getId());
playerBlocks.add(Material.FENCE_GATE.getId());
playerBlocks.add(Material.BRICK_STAIRS.getId());
playerBlocks.add(Material.SMOOTH_STAIRS.getId());
playerBlocks.add(Material.ENCHANTMENT_TABLE.getId());
playerBlocks.add(Material.BREWING_STAND.getId());
playerBlocks.add(Material.CAULDRON.getId());
playerBlocks.add(Material.DIODE_BLOCK_ON.getId());
playerBlocks.add(Material.DIODE_BLOCK_ON.getId());
playerBlocks.add(Material.WEB.getId());
playerBlocks.add(Material.SPONGE.getId());
playerBlocks.add(Material.GRAVEL.getId());
playerBlocks.add(Material.EMERALD_BLOCK.getId());
playerBlocks.add(Material.SANDSTONE.getId());
playerBlocks.add(Material.WOOD_STEP.getId());
playerBlocks.add(Material.WOOD_DOUBLE_STEP.getId());
playerBlocks.add(Material.ENDER_CHEST.getId());
playerBlocks.add(Material.SANDSTONE_STAIRS.getId());
playerBlocks.add(Material.SPRUCE_WOOD_STAIRS.getId());
playerBlocks.add(Material.JUNGLE_WOOD_STAIRS.getId());
playerBlocks.add(Material.COMMAND.getId());
playerBlocks.add(Material.BEACON.getId());
playerBlocks.add(Material.COBBLE_WALL.getId());
playerBlocks.add(Material.FLOWER_POT.getId());
playerBlocks.add(Material.CARROT.getId());
playerBlocks.add(Material.POTATO.getId());
playerBlocks.add(Material.WOOD_BUTTON.getId());
playerBlocks.add(Material.SKULL.getId());
playerBlocks.add(Material.ANVIL.getId());
//these are unnatural in the standard world, but not in the nether
if(environment != Environment.NETHER)
{
playerBlocks.add(Material.NETHERRACK.getId());
playerBlocks.add(Material.SOUL_SAND.getId());
playerBlocks.add(Material.GLOWSTONE.getId());
playerBlocks.add(Material.NETHER_BRICK.getId());
playerBlocks.add(Material.NETHER_FENCE.getId());
playerBlocks.add(Material.NETHER_BRICK_STAIRS.getId());
}
//these are unnatural in the standard and nether worlds, but not in the end
if(environment != Environment.THE_END)
{
playerBlocks.add(Material.OBSIDIAN.getId());
playerBlocks.add(Material.ENDER_STONE.getId());
playerBlocks.add(Material.ENDER_PORTAL_FRAME.getId());
}
//these are unnatural in sandy biomes, but not elsewhere
if(biome == Biome.DESERT || biome == Biome.DESERT_HILLS || biome == Biome.BEACH || environment != Environment.NORMAL)
{
playerBlocks.add(Material.LEAVES.getId());
playerBlocks.add(Material.LOG.getId());
playerBlocks.add(Material.LOG_2.getId());
}
return playerBlocks;
}
}