001 package org.bukkit.generator; 002 003 import java.util.ArrayList; 004 import java.util.List; 005 import java.util.Random; 006 import org.bukkit.Location; 007 import org.bukkit.Material; 008 import org.bukkit.World; 009 import org.bukkit.block.Biome; 010 import org.bukkit.block.Block; 011 012 /** 013 * A chunk generator is responsible for the initial shaping of an entire 014 * chunk. For example, the nether chunk generator should shape netherrack and 015 * soulsand 016 */ 017 public abstract class ChunkGenerator { 018 019 /** 020 * Interface to biome data for chunk to be generated: initialized with 021 * default values for world type and seed. 022 * <p> 023 * Custom generator is free to access and tailor values during 024 * generateBlockSections() or generateExtBlockSections(). 025 */ 026 public interface BiomeGrid { 027 028 /** 029 * Get biome at x, z within chunk being generated 030 * 031 * @param x - 0-15 032 * @param z - 0-15 033 * @return Biome value 034 */ 035 Biome getBiome(int x, int z); 036 037 /** 038 * Set biome at x, z within chunk being generated 039 * 040 * @param x - 0-15 041 * @param z - 0-15 042 * @param bio - Biome value 043 */ 044 void setBiome(int x, int z, Biome bio); 045 } 046 @Deprecated 047 /** 048 * Shapes the chunk for the given coordinates. 049 * <p> 050 * This method should return a byte[32768] in the following format: 051 * <pre> 052 * for (int x = 0; x < 16; x++) { 053 * for (int z = 0; z < 16; z++) { 054 * for (int y = 0; y < 128; y++) { 055 * // result[(x * 16 + z) * 128 + y] = ??; 056 * } 057 * } 058 * } 059 * </pre> 060 * <p> 061 * Note that this method should <b>never</b> attempt to get the Chunk at 062 * the passed coordinates, as doing so may cause an infinite loop 063 * <p> 064 * Note this deprecated method will only be called when both 065 * generateExtBlockSections() and generateBlockSections() are 066 * unimplemented and return null. 067 * 068 * @param world The world this chunk will be used for 069 * @param random The random generator to use 070 * @param x The X-coordinate of the chunk 071 * @param z The Z-coordinate of the chunk 072 * @return byte[] containing the types for each block created by this 073 * generator 074 */ 075 public byte[] generate(World world, Random random, int x, int z) { 076 throw new UnsupportedOperationException("Custom generator is missing required methods: generate(), generateBlockSections() and generateExtBlockSections()"); 077 } 078 079 /** 080 * Shapes the chunk for the given coordinates, with extended block IDs 081 * supported (0-4095). 082 * <p> 083 * As of 1.2, chunks are represented by a vertical array of chunk 084 * sections, each of which is 16 x 16 x 16 blocks. If a section is empty 085 * (all zero), the section does not need to be supplied, reducing memory 086 * usage. 087 * <p> 088 * This method must return a short[][] array in the following format: 089 * <pre> 090 * short[][] result = new short[world-height / 16][]; 091 * </pre> 092 * Each section (sectionID = (Y>>4)) that has blocks needs to be allocated 093 * space for the 4096 blocks in that section: 094 * <pre> 095 * result[sectionID] = new short[4096]; 096 * </pre> 097 * while sections that are not populated can be left null. 098 * <p> 099 * Setting a block at X, Y, Z within the chunk can be done with the 100 * following mapping function: 101 * <pre> 102 * void setBlock(short[][] result, int x, int y, int z, short blkid) { 103 * if (result[y >> 4] == null) { 104 * result[y >> 4] = new short[4096]; 105 * } 106 * result[y >> 4][((y & 0xF) << 8) | (z << 4) | x] = blkid; 107 * } 108 * </pre> 109 * while reading a block ID can be done with the following mapping 110 * function: 111 * <pre> 112 * short getBlock(short[][] result, int x, int y, int z) { 113 * if (result[y >> 4] == null) { 114 * return (short)0; 115 * } 116 * return result[y >> 4][((y & 0xF) << 8) | (z << 4) | x]; 117 * } 118 * </pre> 119 * while sections that are not populated can be left null. 120 * <p> 121 * Setting a block at X, Y, Z within the chunk can be done with the 122 * following mapping function: 123 * <pre> 124 * void setBlock(short[][] result, int x, int y, int z, short blkid) { 125 * if (result[y >> 4) == null) { 126 * result[y >> 4] = new short[4096]; 127 * } 128 * result[y >> 4][((y & 0xF) << 8) | (z << 4) | x] = blkid; 129 * } 130 * </pre> 131 * while reading a block ID can be done with the following mapping 132 * function: 133 * <pre> 134 * short getBlock(short[][] result, int x, int y, int z) { 135 * if (result[y >> 4) == null) { 136 * return (short)0; 137 * } 138 * return result[y >> 4][((y & 0xF) << 8) | (z << 4) | x]; 139 * } 140 * </pre> 141 * <p> 142 * Note that this method should <b>never</b> attempt to get the Chunk at 143 * the passed coordinates, as doing so may cause an infinite loop 144 * <p> 145 * Note generators that do not return block IDs above 255 should not 146 * implement this method, or should have it return null (which will result 147 * in the generateBlockSections() method being called). 148 * 149 * @param world The world this chunk will be used for 150 * @param random The random generator to use 151 * @param x The X-coordinate of the chunk 152 * @param z The Z-coordinate of the chunk 153 * @param biomes Proposed biome values for chunk - can be updated by 154 * generator 155 * @return short[][] containing the types for each block created by this 156 * generator 157 * @deprecated Magic value 158 */ 159 @Deprecated 160 public short[][] generateExtBlockSections(World world, Random random, int x, int z, BiomeGrid biomes) { 161 return null; // Default - returns null, which drives call to generateBlockSections() 162 } 163 164 /** 165 * Shapes the chunk for the given coordinates. 166 * <p> 167 * As of 1.2, chunks are represented by a vertical array of chunk 168 * sections, each of which is 16 x 16 x 16 blocks. If a section is empty 169 * (all zero), the section does not need to be supplied, reducing memory 170 * usage. 171 * <p> 172 * This method must return a byte[][] array in the following format: 173 * <pre> 174 * byte[][] result = new byte[world-height / 16][]; 175 * </pre> 176 * Each section (sectionID = (Y>>4)) that has blocks needs to be allocated 177 * space for the 4096 blocks in that section: 178 * <pre> 179 * result[sectionID] = new byte[4096]; 180 * </pre> 181 * while sections that are not populated can be left null. 182 * <p> 183 * Setting a block at X, Y, Z within the chunk can be done with the 184 * following mapping function: 185 * <pre> 186 * void setBlock(byte[][] result, int x, int y, int z, byte blkid) { 187 * if (result[y >> 4) == null) { 188 * result[y >> 4] = new byte[4096]; 189 * } 190 * result[y >> 4][((y & 0xF) << 8) | (z << 4) | x] = blkid; 191 * } 192 * </pre> 193 * while reading a block ID can be done with the following mapping 194 * function: 195 * <pre> 196 * byte getBlock(byte[][] result, int x, int y, int z) { 197 * if (result[y >> 4) == null) { 198 * return (byte)0; 199 * } 200 * return result[y >> 4][((y & 0xF) << 8) | (z << 4) | x]; 201 * } 202 * </pre> 203 * 204 * Note that this method should <b>never</b> attempt to get the Chunk at 205 * the passed coordinates, as doing so may cause an infinite loop 206 * 207 * @param world The world this chunk will be used for 208 * @param random The random generator to use 209 * @param x The X-coordinate of the chunk 210 * @param z The Z-coordinate of the chunk 211 * @param biomes Proposed biome values for chunk - can be updated by 212 * generator 213 * @return short[][] containing the types for each block created by this 214 * generator 215 * @deprecated Magic value 216 */ 217 @Deprecated 218 public byte[][] generateBlockSections(World world, Random random, int x, int z, BiomeGrid biomes) { 219 return null; // Default - returns null, which drives call to generate() 220 } 221 222 /** 223 * Tests if the specified location is valid for a natural spawn position 224 * 225 * @param world The world we're testing on 226 * @param x X-coordinate of the block to test 227 * @param z Z-coordinate of the block to test 228 * @return true if the location is valid, otherwise false 229 */ 230 public boolean canSpawn(World world, int x, int z) { 231 Block highest = world.getBlockAt(x, world.getHighestBlockYAt(x, z), z); 232 233 switch (world.getEnvironment()) { 234 case NETHER: 235 return true; 236 case THE_END: 237 return highest.getType() != Material.AIR && highest.getType() != Material.WATER && highest.getType() != Material.LAVA; 238 case NORMAL: 239 default: 240 return highest.getType() == Material.SAND || highest.getType() == Material.GRAVEL; 241 } 242 } 243 244 /** 245 * Gets a list of default {@link BlockPopulator}s to apply to a given 246 * world 247 * 248 * @param world World to apply to 249 * @return List containing any amount of BlockPopulators 250 */ 251 public List<BlockPopulator> getDefaultPopulators(World world) { 252 return new ArrayList<BlockPopulator>(); 253 } 254 255 /** 256 * Gets a fixed spawn location to use for a given world. 257 * <p> 258 * A null value is returned if a world should not use a fixed spawn point, 259 * and will instead attempt to find one randomly. 260 * 261 * @param world The world to locate a spawn point for 262 * @param random Random generator to use in the calculation 263 * @return Location containing a new spawn point, otherwise null 264 */ 265 public Location getFixedSpawnLocation(World world, Random random) { 266 return null; 267 } 268 }