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 &lt; 16; x++) {
053         *     for (int z = 0; z &lt; 16; z++) {
054         *         for (int y = 0; y &lt; 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    }