diff --git a/paper-api/src/main/java/org/bukkit/RegionAccessor.java b/paper-api/src/main/java/org/bukkit/RegionAccessor.java new file mode 100644 index 0000000000..a5539309e0 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/RegionAccessor.java @@ -0,0 +1,266 @@ +package org.bukkit; + +import java.util.Collection; +import java.util.List; +import java.util.Random; +import org.bukkit.block.Biome; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.util.Consumer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A RegionAccessor gives access to getting, modifying and spawning {@link Biome}, {@link BlockState} and {@link Entity}, + * as well as generating some basic structures. + */ +public interface RegionAccessor { + + /** + * Gets the {@link Biome} at the given {@link Location}. + * + * @param location the location of the biome + * @return Biome at the given location + */ + @NotNull + Biome getBiome(@NotNull Location location); + + /** + * Gets the {@link Biome} at the given coordinates. + * + * @param x X-coordinate of the block + * @param y Y-coordinate of the block + * @param z Z-coordinate of the block + * @return Biome at the given coordinates + */ + @NotNull + Biome getBiome(int x, int y, int z); + + /** + * Sets the {@link Biome} at the given {@link Location}. + * + * @param location the location of the biome + * @param biome New Biome type for this block + */ + void setBiome(@NotNull Location location, @NotNull Biome biome); + + /** + * Sets the {@link Biome} for the given block coordinates + * + * @param x X-coordinate of the block + * @param y Y-coordinate of the block + * @param z Z-coordinate of the block + * @param biome New Biome type for this block + */ + void setBiome(int x, int y, int z, @NotNull Biome biome); + + /** + * Gets the {@link BlockState} at the given {@link Location}. + * + * @param location The location of the block state + * @return Block state at the given location + */ + @NotNull + BlockState getBlockState(@NotNull Location location); + + /** + * Gets the {@link BlockState} at the given coordinates. + * + * @param x X-coordinate of the block state + * @param y Y-coordinate of the block state + * @param z Z-coordinate of the block state + * @return Block state at the given coordinates + */ + @NotNull + BlockState getBlockState(int x, int y, int z); + + /** + * Gets the {@link BlockData} at the given {@link Location}. + * + * @param location The location of the block data + * @return Block data at the given location + */ + @NotNull + BlockData getBlockData(@NotNull Location location); + + /** + * Gets the {@link BlockData} at the given coordinates. + * + * @param x X-coordinate of the block data + * @param y Y-coordinate of the block data + * @param z Z-coordinate of the block data + * @return Block data at the given coordinates + */ + @NotNull + BlockData getBlockData(int x, int y, int z); + + /** + * Gets the type of the block at the given {@link Location}. + * + * @param location The location of the block + * @return Material at the given coordinates + */ + @NotNull + Material getType(@NotNull Location location); + + /** + * Gets the type of the block at the given coordinates. + * + * @param x X-coordinate of the block + * @param y Y-coordinate of the block + * @param z Z-coordinate of the block + * @return Material at the given coordinates + */ + @NotNull + Material getType(int x, int y, int z); + + /** + * Sets the {@link BlockData} at the given {@link Location}. + * + * @param location The location of the block + * @param blockData The block data to set the block to + */ + void setBlockData(@NotNull Location location, @NotNull BlockData blockData); + + /** + * Sets the {@link BlockData} at the given coordinates. + * + * @param x X-coordinate of the block + * @param y Y-coordinate of the block + * @param z Z-coordinate of the block + * @param blockData The block data to set the block to + */ + void setBlockData(int x, int y, int z, @NotNull BlockData blockData); + + /** + * Sets the {@link Material} at the given {@link Location}. + * + * @param location The location of the block + * @param material The type to set the block to + */ + void setType(@NotNull Location location, @NotNull Material material); + + /** + * Sets the {@link Material} at the given coordinates. + * + * @param x X-coordinate of the block + * @param y Y-coordinate of the block + * @param z Z-coordinate of the block + * @param material The type to set the block to + */ + void setType(int x, int y, int z, @NotNull Material material); + + /** + * Creates a tree at the given {@link Location} + * + * @param location Location to spawn the tree + * @param random Random to use to generated the tree + * @param type Type of the tree to create + * @return true if the tree was created successfully, otherwise false + */ + boolean generateTree(@NotNull Location location, @NotNull Random random, @NotNull TreeType type); + + /** + * Creates a tree at the given {@link Location} + *

+ * The provided consumer gets called for every block which gets changed + * as a result of the tree generation. When the consumer gets called no + * modifications to the world are done yet. Which means, that calling + * {@link #getBlockState(Location)} in the consumer while return the state + * of the block before the generation. + *

+ * Modifications done to the {@link BlockState} in the consumer are respected, + * which means that it is not necessary to call {@link BlockState#update()} + * + * @param location Location to spawn the tree + * @param random Random to use to generated the tree + * @param type Type of the tree to create + * @param stateConsumer The consumer which should get called for every block which gets changed + * @return true if the tree was created successfully, otherwise false + */ + boolean generateTree(@NotNull Location location, @NotNull Random random, @NotNull TreeType type, @Nullable Consumer stateConsumer); + + /** + * Creates a entity at the given {@link Location} + * + * @param location The location to spawn the entity + * @param type The entity to spawn + * @return Resulting Entity of this method + */ + @NotNull + Entity spawnEntity(@NotNull Location location, @NotNull EntityType type); + + /** + * Get a list of all entities in this RegionAccessor + * + * @return A List of all Entities currently residing in this world accessor + */ + @NotNull + List getEntities(); + + /** + * Get a list of all living entities in this RegionAccessor + * + * @return A List of all LivingEntities currently residing in this world accessor + */ + @NotNull + List getLivingEntities(); + + /** + * Get a collection of all entities in this RegionAccessor matching the given + * class/interface + * + * @param an entity subclass + * @param cls The class representing the type of entity to match + * @return A List of all Entities currently residing in this world accessor + * that match the given class/interface + */ + @NotNull + Collection getEntitiesByClass(@NotNull Class cls); + + /** + * Get a collection of all entities in this RegionAccessor matching any of the + * given classes/interfaces + * + * @param classes The classes representing the types of entity to match + * @return A List of all Entities currently residing in this world accessor + * that match one or more of the given classes/interfaces + */ + @NotNull + Collection getEntitiesByClasses(@NotNull Class... classes); + + /** + * Spawn an entity of a specific class at the given {@link Location} + * + * @param location the {@link Location} to spawn the entity at + * @param clazz the class of the {@link Entity} to spawn + * @param the class of the {@link Entity} to spawn + * @return an instance of the spawned {@link Entity} + * @throws IllegalArgumentException if either parameter is null or the + * {@link Entity} requested cannot be spawned + */ + @NotNull + T spawn(@NotNull Location location, @NotNull Class clazz) throws IllegalArgumentException; + + /** + * Spawn an entity of a specific class at the given {@link Location}, with + * the supplied function run before the entity is added to the world. + *
+ * Note that when the function is run, the entity will not be actually in + * the world. Any operation involving such as teleporting the entity is undefined + * until after this function returns. + * + * @param location the {@link Location} to spawn the entity at + * @param clazz the class of the {@link Entity} to spawn + * @param function the function to be run before the entity is spawned. + * @param the class of the {@link Entity} to spawn + * @return an instance of the spawned {@link Entity} + * @throws IllegalArgumentException if either parameter is null or the + * {@link Entity} requested cannot be spawned + */ + @NotNull + T spawn(@NotNull Location location, @NotNull Class clazz, @Nullable Consumer function) throws IllegalArgumentException; +} diff --git a/paper-api/src/main/java/org/bukkit/World.java b/paper-api/src/main/java/org/bukkit/World.java index 0cbc74a885..1deb7c09c1 100644 --- a/paper-api/src/main/java/org/bukkit/World.java +++ b/paper-api/src/main/java/org/bukkit/World.java @@ -5,7 +5,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.function.Predicate; import org.bukkit.block.Biome; import org.bukkit.block.Block; @@ -14,14 +13,15 @@ import org.bukkit.boss.DragonBattle; import org.bukkit.entity.AbstractArrow; import org.bukkit.entity.Arrow; import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; import org.bukkit.entity.FallingBlock; import org.bukkit.entity.Item; import org.bukkit.entity.LightningStrike; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; +import org.bukkit.generator.WorldInfo; import org.bukkit.inventory.ItemStack; import org.bukkit.material.MaterialData; import org.bukkit.metadata.Metadatable; @@ -38,7 +38,7 @@ import org.jetbrains.annotations.Nullable; /** * Represents a world, which may contain entities, chunks and blocks */ -public interface World extends PluginMessageRecipient, Metadatable { +public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient, Metadatable { /** * Gets the {@link Block} at the given coordinates @@ -552,16 +552,6 @@ public interface World extends PluginMessageRecipient, Metadatable { */ public boolean generateTree(@NotNull Location loc, @NotNull TreeType type, @NotNull BlockChangeDelegate delegate); - /** - * Creates a entity at the given {@link Location} - * - * @param loc The location to spawn the entity - * @param type The entity to spawn - * @return Resulting Entity of this method - */ - @NotNull - public Entity spawnEntity(@NotNull Location loc, @NotNull EntityType type); - /** * Strikes lightning at the given {@link Location} * @@ -879,22 +869,6 @@ public interface World extends PluginMessageRecipient, Metadatable { @Nullable public RayTraceResult rayTrace(@NotNull Location start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, @Nullable Predicate filter); - /** - * Gets the unique name of this world - * - * @return Name of this world - */ - @NotNull - public String getName(); - - /** - * Gets the Unique ID of this world - * - * @return Unique ID of this world. - */ - @NotNull - public UUID getUID(); - /** * Gets the default spawn {@link Location} of this world * @@ -1179,21 +1153,6 @@ public interface World extends PluginMessageRecipient, Metadatable { */ public boolean createExplosion(@NotNull Location loc, float power, boolean setFire, boolean breakBlocks, @Nullable Entity source); - /** - * Gets the {@link Environment} type of this world - * - * @return This worlds Environment type - */ - @NotNull - public Environment getEnvironment(); - - /** - * Gets the Seed for this world. - * - * @return This worlds Seed - */ - public long getSeed(); - /** * Gets the current PVP setting for this world. * @@ -1216,6 +1175,14 @@ public interface World extends PluginMessageRecipient, Metadatable { @Nullable public ChunkGenerator getGenerator(); + /** + * Gets the biome provider for this world + * + * @return BiomeProvider associated with this world + */ + @Nullable + public BiomeProvider getBiomeProvider(); + /** * Saves world to disk */ @@ -1229,38 +1196,6 @@ public interface World extends PluginMessageRecipient, Metadatable { @NotNull public List getPopulators(); - /** - * Spawn an entity of a specific class at the given {@link Location} - * - * @param location the {@link Location} to spawn the entity at - * @param clazz the class of the {@link Entity} to spawn - * @param the class of the {@link Entity} to spawn - * @return an instance of the spawned {@link Entity} - * @throws IllegalArgumentException if either parameter is null or the - * {@link Entity} requested cannot be spawned - */ - @NotNull - public T spawn(@NotNull Location location, @NotNull Class clazz) throws IllegalArgumentException; - - /** - * Spawn an entity of a specific class at the given {@link Location}, with - * the supplied function run before the entity is added to the world. - *
- * Note that when the function is run, the entity will not be actually in - * the world. Any operation involving such as teleporting the entity is undefined - * until after this function returns. - * - * @param location the {@link Location} to spawn the entity at - * @param clazz the class of the {@link Entity} to spawn - * @param function the function to be run before the entity is spawned. - * @param the class of the {@link Entity} to spawn - * @return an instance of the spawned {@link Entity} - * @throws IllegalArgumentException if either parameter is null or the - * {@link Entity} requested cannot be spawned - */ - @NotNull - public T spawn(@NotNull Location location, @NotNull Class clazz, @Nullable Consumer function) throws IllegalArgumentException; - /** * Spawn a {@link FallingBlock} entity at the given {@link Location} of * the specified {@link Material}. The material dictates what is falling. @@ -1413,17 +1348,6 @@ public interface World extends PluginMessageRecipient, Metadatable { @Deprecated Biome getBiome(int x, int z); - /** - * Gets the biome for the given block coordinates. - * - * @param x X coordinate of the block - * @param y Y coordinate of the block - * @param z Z coordinate of the block - * @return Biome of the requested block - */ - @NotNull - Biome getBiome(int x, int y, int z); - /** * Sets the biome for the given block coordinates * @@ -1435,16 +1359,6 @@ public interface World extends PluginMessageRecipient, Metadatable { @Deprecated void setBiome(int x, int z, @NotNull Biome bio); - /** - * Sets the biome for the given block coordinates - * - * @param x X coordinate of the block - * @param y Y coordinate of the block - * @param z Z coordinate of the block - * @param bio new Biome type for this block - */ - void setBiome(int x, int y, int z, @NotNull Biome bio); - /** * Gets the temperature for the given block coordinates. *

@@ -1505,24 +1419,6 @@ public interface World extends PluginMessageRecipient, Metadatable { */ public double getHumidity(int x, int y, int z); - /** - * Gets the minimum height of this world. - *

- * If the min height is 0, there are only blocks from y=0. - * - * @return Minimum height of the world - */ - public int getMinHeight(); - - /** - * Gets the maximum height of this world. - *

- * If the max height is 100, there are only blocks from y=0 to y=99. - * - * @return Maximum height of the world - */ - public int getMaxHeight(); - /** * Gets the maximum height to which chorus fruits and nether portals can * bring players within this dimension. diff --git a/paper-api/src/main/java/org/bukkit/WorldCreator.java b/paper-api/src/main/java/org/bukkit/WorldCreator.java index 6774c8176c..60bed20c28 100644 --- a/paper-api/src/main/java/org/bukkit/WorldCreator.java +++ b/paper-api/src/main/java/org/bukkit/WorldCreator.java @@ -2,6 +2,7 @@ package org.bukkit; import java.util.Random; import org.bukkit.command.CommandSender; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; @@ -15,6 +16,7 @@ public class WorldCreator { private long seed; private World.Environment environment = World.Environment.NORMAL; private ChunkGenerator generator = null; + private BiomeProvider biomeProvider = null; private WorldType type = WorldType.NORMAL; private boolean generateStructures = true; private String generatorSettings = ""; @@ -49,6 +51,7 @@ public class WorldCreator { seed = world.getSeed(); environment = world.getEnvironment(); generator = world.getGenerator(); + biomeProvider = world.getBiomeProvider(); type = world.getWorldType(); generateStructures = world.canGenerateStructures(); hardcore = world.isHardcore(); @@ -71,6 +74,7 @@ public class WorldCreator { seed = creator.seed(); environment = creator.environment(); generator = creator.generator(); + biomeProvider = creator.biomeProvider(); type = creator.type(); generateStructures = creator.generateStructures(); generatorSettings = creator.generatorSettings(); @@ -228,6 +232,84 @@ public class WorldCreator { return this; } + /** + * Gets the biome provider that will be used to create or load the world. + *

+ * This may be null, in which case the biome provider from the {@link ChunkGenerator} + * will be used. If no {@link ChunkGenerator} is specific the "natural" biome provider + * for this environment will be used. + * + * @return Biome provider + */ + @Nullable + public BiomeProvider biomeProvider() { + return biomeProvider; + } + + /** + * Sets the biome provider that will be used to create or load the world. + *

+ * This may be null, in which case the biome provider from the + * {@link ChunkGenerator} will be used. If no {@link ChunkGenerator} is + * specific the "natural" biome provider for this environment will be used. + * + * @param biomeProvider Biome provider + * @return This object, for chaining + */ + @NotNull + public WorldCreator biomeProvider(@Nullable BiomeProvider biomeProvider) { + this.biomeProvider = biomeProvider; + + return this; + } + + /** + * Sets the biome provider that will be used to create or load the world. + *

+ * This may be null, in which case the biome provider from the + * {@link ChunkGenerator} will be used. If no {@link ChunkGenerator} is + * specific the "natural" biome provider for this environment will be used. + *

+ * If the biome provider cannot be found for the given name and no + * {@link ChunkGenerator} is specific, the natural environment biome + * provider will be used instead and a warning will be printed to the + * specified output + * + * @param biomeProvider Name of the biome provider to use, in "plugin:id" + * notation + * @return This object, for chaining + */ + @NotNull + public WorldCreator biomeProvider(@Nullable String biomeProvider) { + this.biomeProvider = getBiomeProviderForName(name, biomeProvider, Bukkit.getConsoleSender()); + + return this; + } + + /** + * Sets the biome provider that will be used to create or load the world. + *

+ * This may be null, in which case the biome provider from the + * {@link ChunkGenerator} will be used. If no {@link ChunkGenerator} is + * specific the "natural" biome provider for this environment will be used. + *

+ * If the biome provider cannot be found for the given name and no + * {@link ChunkGenerator} is specific, the natural environment biome + * provider will be used instead and a warning will be printed to the + * specified output + * + * @param biomeProvider Name of the biome provider to use, in "plugin:id" + * notation + * @param output {@link CommandSender} that will receive any error messages + * @return This object, for chaining + */ + @NotNull + public WorldCreator biomeProvider(@Nullable String biomeProvider, @Nullable CommandSender output) { + this.biomeProvider = getBiomeProviderForName(name, biomeProvider, output); + + return this; + } + /** * Sets the generator settings of the world that will be created or loaded. *

@@ -380,4 +462,49 @@ public class WorldCreator { return result; } + + /** + * Attempts to get the {@link BiomeProvider} with the given name. + *

+ * If the biome provider is not found, null will be returned and a message + * will be printed to the specified {@link CommandSender} explaining why. + *

+ * The name must be in the "plugin:id" notation, or optionally just + * "plugin", where "plugin" is the safe-name of a plugin and "id" is an + * optional unique identifier for the biome provider you wish to request + * from the plugin. + * + * @param world Name of the world this will be used for + * @param name Name of the biome provider to retrieve + * @param output Where to output if errors are present + * @return Resulting biome provider, or null + */ + @Nullable + public static BiomeProvider getBiomeProviderForName(@NotNull String world, @Nullable String name, @Nullable CommandSender output) { + BiomeProvider result = null; + + if (world == null) { + throw new IllegalArgumentException("World name must be specified"); + } + + if (output == null) { + output = Bukkit.getConsoleSender(); + } + + if (name != null) { + String[] split = name.split(":", 2); + String id = (split.length > 1) ? split[1] : null; + Plugin plugin = Bukkit.getPluginManager().getPlugin(split[0]); + + if (plugin == null) { + output.sendMessage("Could not set biome provider for world '" + world + "': Plugin '" + split[0] + "' does not exist"); + } else if (!plugin.isEnabled()) { + output.sendMessage("Could not set set biome provider for world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' is not enabled"); + } else { + result = plugin.getDefaultBiomeProvider(world, id); + } + } + + return result; + } } diff --git a/paper-api/src/main/java/org/bukkit/generator/BiomeProvider.java b/paper-api/src/main/java/org/bukkit/generator/BiomeProvider.java new file mode 100644 index 0000000000..9f6d75af20 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/generator/BiomeProvider.java @@ -0,0 +1,52 @@ +package org.bukkit.generator; + +import java.util.List; +import org.bukkit.block.Biome; +import org.jetbrains.annotations.NotNull; + +/** + * Class for providing biomes. + */ +public abstract class BiomeProvider { + + /** + * Return the Biome which should be present at the provided location. + *

+ * Notes: + *

+ * This method must be completely thread safe and able to handle + * multiple concurrent callers. + *

+ * This method should only return biomes which are present in the list + * returned by {@link #getBiomes(WorldInfo)} + *

+ * This method should never return {@link Biome#CUSTOM}. + * + * @param worldInfo The world info of the world the biome will be used for + * @param x The X-coordinate from world origin + * @param y The Y-coordinate from world origin + * @param z The Z-coordinate from world origin + * @return Biome for the given location + */ + @NotNull + public abstract Biome getBiome(@NotNull WorldInfo worldInfo, int x, int y, int z); + + /** + * Returns a list with every biome the {@link BiomeProvider} will use for + * the given world. + *

+ * Notes: + *

+ * This method only gets called once, when the world is loaded. Returning + * another list or modifying the values from the initial returned list later + * one, are not respected. + *

+ * This method should never return a list which contains + * {@link Biome#CUSTOM}. + * + * @param worldInfo The world info of the world the list will be used for + * @return A list with every biome the {@link BiomeProvider} uses + */ + @NotNull + public abstract List getBiomes(@NotNull WorldInfo worldInfo); +} diff --git a/paper-api/src/main/java/org/bukkit/generator/BlockPopulator.java b/paper-api/src/main/java/org/bukkit/generator/BlockPopulator.java index e5e3a6a548..ea3efd92ca 100644 --- a/paper-api/src/main/java/org/bukkit/generator/BlockPopulator.java +++ b/paper-api/src/main/java/org/bukkit/generator/BlockPopulator.java @@ -25,6 +25,39 @@ public abstract class BlockPopulator { * @param world The world to generate in * @param random The random generator to use * @param source The chunk to generate for + * @deprecated Use {@link #populate(WorldInfo, Random, int, int, LimitedRegion)} */ - public abstract void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk source); + @Deprecated + public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk source) { + } + + /** + * Populates an area of blocks at or around the given chunk. + *

+ * Notes: + *

+ * This method should never attempt to get the Chunk at the passed + * coordinates, as doing so may cause an infinite loop + *

+ * This method should never modify a {@link LimitedRegion} at a later + * point of time. + *

+ * This method must be completely thread safe and able to handle + * multiple concurrent callers. + *

+ * No physics are applied, whether or not it is set to true in + * {@link org.bukkit.block.BlockState#update(boolean, boolean)} + *

+ * Only use the {@link org.bukkit.block.BlockState} returned by + * {@link LimitedRegion}, + * never use methods from a {@link World} to modify the chunk. + * + * @param worldInfo The world info of the world to generate in + * @param random The random generator to use + * @param x The X-coordinate of the chunk + * @param z The Z-coordinate of the chunk + * @param limitedRegion The chunk region to populate + */ + public void populate(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull LimitedRegion limitedRegion) { + } } diff --git a/paper-api/src/main/java/org/bukkit/generator/ChunkGenerator.java b/paper-api/src/main/java/org/bukkit/generator/ChunkGenerator.java index 9d7592988a..80fcd02e9c 100644 --- a/paper-api/src/main/java/org/bukkit/generator/ChunkGenerator.java +++ b/paper-api/src/main/java/org/bukkit/generator/ChunkGenerator.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; import org.bukkit.Bukkit; +import org.bukkit.HeightMap; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -19,28 +20,205 @@ import org.jetbrains.annotations.Nullable; * chunk. For example, the nether chunk generator should shape netherrack and * soulsand. * - * By default only one thread will call - * {@link #generateChunkData(org.bukkit.World, java.util.Random, int, int, org.bukkit.generator.ChunkGenerator.BiomeGrid)} - * at a time, although this may not necessarily be the main server thread. + * A chunk is generated in multiple steps, those steps are always in the same + * order. Between those steps however an unlimited time may pass. This means, a + * chunk may generated until the surface step and continue with the bedrock step + * after one or multiple server restarts or even after multiple Minecraft + * versions. * - * If your generator is capable of fully asynchronous generation, then - * {@link #isParallelCapable()} should be overridden accordingly to allow - * multiple concurrent callers. + * The order of generation is as follows + *

    + *
  1. {@link #generateNoise(WorldInfo, Random, int, int, ChunkData)}
  2. + *
  3. {@link #generateSurface(WorldInfo, Random, int, int, ChunkData)}
  4. + *
  5. {@link #generateBedrock(WorldInfo, Random, int, int, ChunkData)}
  6. + *
  7. {@link #generateCaves(WorldInfo, Random, int, int, ChunkData)}
  8. + *
+ * + * Every method listed above as well as + * {@link #getBaseHeight(WorldInfo, Random, int, int, HeightMap)} + * must be completely thread safe and able to handle multiple concurrent + * callers. * * Some aspects of world generation can be delegated to the Vanilla generator. - * The methods {@link ChunkGenerator#shouldGenerateCaves()}, {@link ChunkGenerator#shouldGenerateDecorations()}, - * {@link ChunkGenerator#shouldGenerateMobs()} and {@link ChunkGenerator#shouldGenerateStructures()} can be - * overridden to enable this. + * The following methods can be overridden to enable this: + * */ public abstract class ChunkGenerator { + /** + * Shapes the Chunk noise for the given coordinates. + *

+ * Notes: + *

+ * This method should never attempt to get the Chunk at the passed + * coordinates, as doing so may cause an infinite loop. + *

+ * This method should never modify the {@link ChunkData} at a later + * point of time. + *

+ * The Y-coordinate range should never be hardcoded, to get the + * Y-coordinate range use the methods {@link ChunkData#getMinHeight()} and + * {@link ChunkData#getMaxHeight()}. + *

+ * If {@link #shouldGenerateNoise()} is set to true, the given + * {@link ChunkData} contains already the Vanilla noise generation. + * + * @param worldInfo The world info of the world this chunk will be used for + * @param random The random generator to use + * @param x The X-coordinate of the chunk + * @param z The Z-coordinate of the chunk + * @param chunkData To modify + */ + public void generateNoise(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull ChunkData chunkData) { + } + + /** + * Shapes the Chunk surface for the given coordinates. + *

+ * Notes: + *

+ * This method should never attempt to get the Chunk at the passed + * coordinates, as doing so may cause an infinite loop. + *

+ * This method should never modify the {@link ChunkData} at a later + * point of time. + *

+ * The Y-coordinate range should never be hardcoded, to get the + * Y-coordinate range use the methods {@link ChunkData#getMinHeight()} and + * {@link ChunkData#getMaxHeight()}. + *

+ * If {@link #shouldGenerateSurface()} is set to true, the given + * {@link ChunkData} contains already the Vanilla surface generation. + * + * @param worldInfo The world info of the world this chunk will be used for + * @param random The random generator to use + * @param x The X-coordinate of the chunk + * @param z The Z-coordinate of the chunk + * @param chunkData To modify + */ + public void generateSurface(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull ChunkData chunkData) { + } + + /** + * Shapes the Chunk bedrock layer for the given coordinates. + *

+ * Notes: + *

+ * This method should never attempt to get the Chunk at the passed + * coordinates, as doing so may cause an infinite loop. + *

+ * This method should never modify the {@link ChunkData} at a later + * point of time. + *

+ * The Y-coordinate range should never be hardcoded, to get the + * Y-coordinate range use the methods {@link ChunkData#getMinHeight()} and + * {@link ChunkData#getMaxHeight()}. + *

+ * If {@link #shouldGenerateBedrock()} is set to true, the given + * {@link ChunkData} contains already the Vanilla bedrock generation. + * + * @param worldInfo The world info of the world this chunk will be used for + * @param random The random generator to use + * @param x The X-coordinate of the chunk + * @param z The Z-coordinate of the chunk + * @param chunkData To modify + */ + public void generateBedrock(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull ChunkData chunkData) { + } + + /** + * Shapes the Chunk caves for the given coordinates. + *

+ * Notes: + *

+ * This method should never attempt to get the Chunk at the passed + * coordinates, as doing so may cause an infinite loop. + *

+ * This method should never modify the {@link ChunkData} at a later + * point of time. + *

+ * The Y-coordinate range should never be hardcoded, to get the + * Y-coordinate range use the methods {@link ChunkData#getMinHeight()} and + * {@link ChunkData#getMaxHeight()}. + *

+ * If {@link #shouldGenerateCaves()} is set to true, the given + * {@link ChunkData} contains already the Vanilla cave generation. + * + * @param worldInfo The world info of the world this chunk will be used for + * @param random The random generator to use + * @param x The X-coordinate of the chunk + * @param z The Z-coordinate of the chunk + * @param chunkData To modify + */ + public void generateCaves(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull ChunkData chunkData) { + } + + /** + * Gets called when no {@link BiomeProvider} is set in + * {@link org.bukkit.WorldCreator} or via the server configuration files. It + * is therefore possible that one plugin can provide the Biomes and another + * one the generation. + *

+ * Notes: + *

+ * If null is returned, than Vanilla biomes are used. + *

+ * This method only gets called once when the world is loaded. Returning + * another {@link BiomeProvider} later one is not respected. + * + * @param worldInfo The world info of the world the biome provider will be + * used for + * @return BiomeProvider to use to fill the biomes of a chunk + */ + @Nullable + public BiomeProvider getDefaultBiomeProvider(@NotNull WorldInfo worldInfo) { + return null; + } + + /** + * This method is similar to + * {@link World#getHighestBlockAt(int, int, HeightMap)}. With the difference + * being, that the highest y coordinate should be the block before any + * surface, bedrock, caves or decoration is applied. Or in other words the + * highest block when only the noise is present at the chunk. + *

+ * Notes: + *

+ * When this method is not overridden, the Vanilla base height is used. + *

+ * This method should never attempt to get the Chunk at the passed + * coordinates, or use the method + * {@link World#getHighestBlockAt(int, int, HeightMap)}, as doing so may + * cause an infinite loop. + * + * @param worldInfo The world info of the world this chunk will be used for + * @param random The random generator to use + * @param x The X-coordinate from world origin + * @param z The Z-coordinate from world origin + * @param heightMap From the highest block should be get + * @return The y coordinate of the highest block at the given location + */ + public int getBaseHeight(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull HeightMap heightMap) { + throw new UnsupportedOperationException("Not implemented"); + } + /** * Interface to biome section for chunk to be generated: initialized with * default values for world type and seed. *

* Custom generator is free to access and tailor values during * generateBlockSections() or generateExtBlockSections(). + * @deprecated Biomes are now set with {@link BiomeProvider} */ + @Deprecated public interface BiomeGrid { /** @@ -111,8 +289,10 @@ public abstract class ChunkGenerator { * generator * @return ChunkData containing the types for each block created by this * generator + * @deprecated The generation is now split up */ @NotNull + @Deprecated public ChunkData generateChunkData(@NotNull World world, @NotNull Random random, int x, int z, @NotNull BiomeGrid biome) { throw new UnsupportedOperationException("Custom generator " + getClass().getName() + " is missing required method generateChunkData"); } @@ -121,8 +301,10 @@ public abstract class ChunkGenerator { * Create a ChunkData for a world. * @param world the world the ChunkData is for * @return a new ChunkData for world + * @deprecated {@link ChunkData} are now directly provided */ @NotNull + @Deprecated protected final ChunkData createChunkData(@NotNull World world) { return Bukkit.getServer().createChunkData(world); } @@ -182,14 +364,56 @@ public abstract class ChunkGenerator { * See {@link ChunkGenerator} for more information. * * @return parallel capable status + * @deprecated the chunk generation code should be thread safe */ + @Deprecated public boolean isParallelCapable() { return false; } /** - * Gets if the server should generate Vanilla caves after this - * ChunkGenerator. + * Gets if the server should generate Vanilla noise. + *

+ * The Vanilla noise is generated before + * {@link #generateNoise(WorldInfo, Random, int, int, ChunkData)} is called. + * + * @return true if the server should generate Vanilla noise + */ + public boolean shouldGenerateNoise() { + return false; + } + + /** + * Gets if the server should generate Vanilla surface. + *

+ * The Vanilla surface is generated before + * {@link #generateSurface(WorldInfo, Random, int, int, ChunkData)} is + * called. + * + * @return true if the server should generate Vanilla surface + */ + public boolean shouldGenerateSurface() { + return false; + } + + /** + * Gets if the server should generate Vanilla bedrock. + *

+ * The Vanilla bedrock is generated before + * {@link #generateBedrock(WorldInfo, Random, int, int, ChunkData)} is + * called. + * + * @return true if the server should generate Vanilla bedrock + */ + public boolean shouldGenerateBedrock() { + return false; + } + + /** + * Gets if the server should generate Vanilla caves. + *

+ * The Vanilla caves are generated before + * {@link #generateCaves(WorldInfo, Random, int, int, ChunkData)} is called. * * @return true if the server should generate Vanilla caves */ @@ -200,6 +424,9 @@ public abstract class ChunkGenerator { /** * Gets if the server should generate Vanilla decorations after this * ChunkGenerator. + *

+ * The Vanilla decoration are generated before any + * {@link BlockPopulator} are called. * * @return true if the server should generate Vanilla decorations */ @@ -232,8 +459,11 @@ public abstract class ChunkGenerator { */ public static interface ChunkData { /** - * Get the minimum height for the chunk. - * + * Get the minimum height for this ChunkData. + *

+ * It is not guaranteed that this method will return the same value as + * {@link World#getMinHeight()}. + *

* Setting blocks below this height will do nothing. * * @return the minimum height @@ -241,14 +471,29 @@ public abstract class ChunkGenerator { public int getMinHeight(); /** - * Get the maximum height for the chunk. - * + * Get the maximum height for this ChunkData. + *

+ * It is not guaranteed that this method will return the same value as + * {@link World#getMaxHeight()}. + *

* Setting blocks at or above this height will do nothing. * * @return the maximum height */ public int getMaxHeight(); + /** + * Get the biome at x, y, z within chunk being generated + * + * @param x the x location in the chunk from 0-15 inclusive + * @param y the y location in the chunk from minimum (inclusive) - + * maxHeight (exclusive) + * @param z the z location in the chunk from 0-15 inclusive + * @return Biome value + */ + @NotNull + public Biome getBiome(int x, int y, int z); + /** * Set the block at x,y,z in the chunk data to material. * diff --git a/paper-api/src/main/java/org/bukkit/generator/LimitedRegion.java b/paper-api/src/main/java/org/bukkit/generator/LimitedRegion.java new file mode 100644 index 0000000000..0428f21086 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/generator/LimitedRegion.java @@ -0,0 +1,45 @@ +package org.bukkit.generator; + +import org.bukkit.Location; +import org.bukkit.RegionAccessor; +import org.jetbrains.annotations.NotNull; + +/** + * A limited region is used in world generation for features which are + * going over a chunk. For example, trees or ores. + * + * Use {@link #getBuffer()} to know how much you can go beyond the central + * chunk. The buffer zone may or may not be already populated. + * + * The coordinates are absolute from the world origin. + */ +public interface LimitedRegion extends RegionAccessor { + + /** + * Gets the buffer around the central chunk which is accessible. + * The returned value is in normal world coordinate scale. + *

+ * For example: If the method returns 16 you have a working area of 48x48. + * + * @return The buffer in X and Z direction + */ + int getBuffer(); + + /** + * Checks if the given {@link Location} is in the region. + * + * @param location the location to check + * @return true if the location is in the region, otherwise false. + */ + boolean isInRegion(@NotNull Location location); + + /** + * Checks if the given coordinates are in the region. + * + * @param x X-coordinate to check + * @param y Y-coordinate to check + * @param z Z-coordinate to check + * @return true if the coordinates are in the region, otherwise false. + */ + boolean isInRegion(int x, int y, int z); +} diff --git a/paper-api/src/main/java/org/bukkit/generator/WorldInfo.java b/paper-api/src/main/java/org/bukkit/generator/WorldInfo.java new file mode 100644 index 0000000000..5bf00f46e8 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/generator/WorldInfo.java @@ -0,0 +1,60 @@ +package org.bukkit.generator; + +import java.util.UUID; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; + +/** + * Holds various information of a World + */ +public interface WorldInfo { + + /** + * Gets the unique name of this world + * + * @return Name of this world + */ + @NotNull + String getName(); + + /** + * Gets the Unique ID of this world + * + * @return Unique ID of this world. + */ + @NotNull + UUID getUID(); + + /** + * Gets the {@link World.Environment} type of this world + * + * @return This worlds Environment type + */ + @NotNull + World.Environment getEnvironment(); + + /** + * Gets the Seed for this world. + * + * @return This worlds Seed + */ + long getSeed(); + + /** + * Gets the minimum height of this world. + *

+ * If the min height is 0, there are only blocks from y=0. + * + * @return Minimum height of the world + */ + int getMinHeight(); + + /** + * Gets the maximum height of this world. + *

+ * If the max height is 100, there are only blocks from y=0 to y=99. + * + * @return Maximum height of the world + */ + int getMaxHeight(); +} diff --git a/paper-api/src/main/java/org/bukkit/plugin/Plugin.java b/paper-api/src/main/java/org/bukkit/plugin/Plugin.java index febfec6efa..03ca87a1cb 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/Plugin.java +++ b/paper-api/src/main/java/org/bukkit/plugin/Plugin.java @@ -6,6 +6,7 @@ import java.util.logging.Logger; import org.bukkit.Server; import org.bukkit.command.TabExecutor; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -156,6 +157,18 @@ public interface Plugin extends TabExecutor { @Nullable public ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, @Nullable String id); + /** + * Gets a {@link BiomeProvider} for use in a default world, as specified + * in the server configuration + * + * @param worldName Name of the world that this will be applied to + * @param id Unique ID, if any, that was specified to indicate which + * biome provider was requested + * @return BiomeProvider for use in the default world generation + */ + @Nullable + public BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id); + /** * Returns the plugin logger associated with this server's logger. The * returned logger automatically tags all log messages with the plugin's diff --git a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java index 2231900549..50a4794889 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java +++ b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java @@ -20,6 +20,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; import org.bukkit.plugin.PluginBase; import org.bukkit.plugin.PluginDescriptionFile; @@ -335,6 +336,12 @@ public abstract class JavaPlugin extends PluginBase { return null; } + @Nullable + @Override + public BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) { + return null; + } + @Override public final boolean isNaggable() { return naggable; diff --git a/paper-api/src/test/java/org/bukkit/plugin/TestPlugin.java b/paper-api/src/test/java/org/bukkit/plugin/TestPlugin.java index 6d45089718..a8be3e23e3 100644 --- a/paper-api/src/test/java/org/bukkit/plugin/TestPlugin.java +++ b/paper-api/src/test/java/org/bukkit/plugin/TestPlugin.java @@ -7,6 +7,7 @@ import org.bukkit.Server; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; public class TestPlugin extends PluginBase { @@ -112,6 +113,11 @@ public class TestPlugin extends PluginBase { throw new UnsupportedOperationException("Not supported."); } + @Override + public BiomeProvider getDefaultBiomeProvider(String worldName, String id) { + throw new UnsupportedOperationException("Not supported."); + } + @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { throw new UnsupportedOperationException("Not supported.");