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 }