diff --git a/patches/api/0318-Add-Feature-Stage-API.patch b/patches/api/0318-Add-Feature-Stage-API.patch new file mode 100644 index 000000000..4e3647529 --- /dev/null +++ b/patches/api/0318-Add-Feature-Stage-API.patch @@ -0,0 +1,306 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: dfsek +Date: Sat, 19 Jun 2021 20:15:29 -0700 +Subject: [PATCH] Add Feature Stage API + + +diff --git a/src/main/java/io/papermc/paper/world/gen/ProtoWorld.java b/src/main/java/io/papermc/paper/world/gen/ProtoWorld.java +new file mode 100644 +index 0000000000000000000000000000000000000000..63de080e945dfa0e63e6f3c63e498f20481dab27 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/world/gen/ProtoWorld.java +@@ -0,0 +1,270 @@ ++package io.papermc.paper.world.gen; ++ ++import org.bukkit.World; ++import org.bukkit.block.data.BlockData; ++import org.bukkit.entity.Entity; ++import org.bukkit.entity.EntityType; ++import org.bukkit.event.entity.CreatureSpawnEvent; ++import org.bukkit.util.Vector; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.function.Consumer; ++ ++ ++/** ++ * Represents a small grid of chunks in a {@link World} ++ * with rudimentary block and entity access, for use during world generation. ++ *

++ * A ProtoWorld is guaranteed read/write access to a 3x3 grid of chunks, ++ * but may have access to a grid as large as 17x17. It is safest to assume ++ * that there is only read/write access to 3x3 chunks. Some chunks outside ++ * of the 3x3 area may be readable but not writable. ++ *

++ * ProtoWorlds should not be stored! After they are used during ++ * chunk generation they should be disposed of. ++ */ ++public interface ProtoWorld { ++ /** ++ * Sets the block at (x, y, z) to the provided {@link BlockData}. ++ * ++ * @param x X coordinate in this ProtoWorld ++ * @param y Y coordinate in this ProtoWorld ++ * @param z Z coordinate in this ProtoWorld ++ * @param data {@link BlockData} to set the block at the provided coordinates to. ++ */ ++ void setBlockData(int x, int y, int z, @NotNull BlockData data); ++ ++ /** ++ * Sets the block at a vector location to the provided {@link BlockData}. ++ * ++ * @param vector {@link Vector} representing the position of the block to set. ++ * @param data {@link BlockData} to set the block at the provided coordinates to. ++ */ ++ default void setBlockData(@NotNull Vector vector, @NotNull BlockData data) { ++ setBlockData(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ(), data); ++ } ++ ++ /** ++ * Schedule a block update at (x, y, z). ++ * ++ * @param x X coordinate in this ProtoWorld ++ * @param y Y coordinate in this ProtoWorld ++ * @param z Z coordinate in this ProtoWorld ++ */ ++ void scheduleBlockUpdate(int x, int y, int z); ++ ++ /** ++ * Schedule a block update at a vector location ++ * ++ * @param location {@link Vector} representing the position of the block to update. ++ */ ++ default void scheduleBlockUpdate(@NotNull Vector location) { ++ scheduleBlockUpdate(location.getBlockX(), location.getBlockY(), location.getBlockZ()); ++ } ++ ++ /** ++ * Schedule a fluid update at (x, y, z). ++ * ++ * @param x X coordinate in this ProtoWorld ++ * @param y Y coordinate in this ProtoWorld ++ * @param z Z coordinate in this ProtoWorld ++ */ ++ void scheduleFluidUpdate(int x, int y, int z); ++ ++ /** ++ * Schedule a fluid update at a vector location ++ * ++ * @param location {@link Vector} representing the position of the block to update. ++ */ ++ default void scheduleFluidUpdate(@NotNull Vector location) { ++ scheduleFluidUpdate(location.getBlockX(), location.getBlockY(), location.getBlockZ()); ++ } ++ ++ /** ++ * Get the {@link World} object this ProtoWorld represents. ++ *

++ * Do not attempt to read from/write to this world! Doing so during generation will cause a deadlock! ++ * ++ * @return The {@link World} object that this ProtoWorld represents. ++ */ ++ @NotNull ++ World getWorld(); ++ ++ ++ /** ++ * Get the {@link BlockData} of the block at the provided coordinates. ++ * ++ * @param x X coordinate in this ProtoWorld ++ * @param y Y coordinate in this ProtoWorld ++ * @param z Z coordinate in this ProtoWorld ++ * @return {@link BlockData} at the coordinates ++ */ ++ @NotNull ++ BlockData getBlockData(int x, int y, int z); ++ ++ /** ++ * Get the {@link BlockData} of the block at the provided coordinates. ++ * ++ * @param vector {@link Vector} representing the position of the block to get. ++ * @return {@link BlockData} at the coordinates ++ */ ++ @NotNull ++ default BlockData getBlockData(@NotNull Vector vector) { ++ return getBlockData(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ()); ++ } ++ ++ /** ++ * Get the X-coordinate of the chunk in the center of this ProtoWorld ++ * ++ * @return The center chunk's X coordinate. ++ */ ++ int getCenterChunkX(); ++ ++ /** ++ * Get the X-coordinate of the block in the center of this ProtoWorld ++ * ++ * @return The center chunk's X coordinate. ++ */ ++ default int getCenterBlockX() { ++ return getCenterChunkX() << 4; ++ } ++ ++ /** ++ * Get the Z-coordinate of the chunk in the center of this ProtoWorld ++ * ++ * @return The center chunk's Z coordinate. ++ */ ++ int getCenterChunkZ(); ++ ++ /** ++ * Get the Z-coordinate of the block in the center of this ProtoWorld ++ * ++ * @return The center chunk's Z coordinate. ++ */ ++ default int getCenterBlockZ() { ++ return getCenterChunkZ() << 4; ++ } ++ ++ /** ++ * Creates a entity at the location represented by the given {@link Vector} ++ * ++ * @param loc The {@link Vector} representing the location to spawn the entity ++ * @param type The entity to spawn ++ * @return Resulting Entity of this method ++ */ ++ @NotNull ++ default Entity spawnEntity(@NotNull Vector loc, @NotNull EntityType type) { ++ return spawn(loc, type.getEntityClass(), CreatureSpawnEvent.SpawnReason.DEFAULT); ++ } ++ ++ /** ++ * Spawn an entity of a specific class at location represented by the given {@link Vector} ++ * ++ * @param location The {@link Vector} representing the 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 ++ default T spawn(@NotNull Vector location, @NotNull Class clazz) throws IllegalArgumentException { ++ return spawn(location, clazz, CreatureSpawnEvent.SpawnReason.DEFAULT, null); ++ } ++ ++ /** ++ * Spawn an entity of a specific class at location represented by the given {@link Vector} ++ * ++ * @param location The {@link Vector} representing the 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 ++ * @param reason The reason for the entity's 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 ++ default T spawn(@NotNull Vector location, @NotNull Class clazz, @NotNull CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException { ++ return spawn(location, clazz, reason, null); ++ } ++ ++ /** ++ * Spawn an entity of a specific class at the location represented by the given {@link Vector}, 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 Vector} representing the 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 ++ default T spawn(@NotNull Vector location, @NotNull Class clazz, @Nullable Consumer function) throws IllegalArgumentException { ++ return spawn(location, clazz, CreatureSpawnEvent.SpawnReason.CUSTOM, function); ++ } ++ ++ /** ++ * Spawn an entity of a specific class at the location represented by the given {@link Vector}, 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 Vector} representing the location to spawn the entity at ++ * @param clazz The class of the {@link Entity} to spawn ++ * @param reason The reason for the entity's 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 ++ default T spawn(@NotNull Vector location, @NotNull Class clazz, @NotNull CreatureSpawnEvent.SpawnReason reason, @Nullable Consumer function) throws IllegalArgumentException { ++ return spawn(location, clazz, function, reason); ++ } ++ ++ /** ++ * Creates a entity at the location represented by the given {@link Vector} ++ * ++ * @param loc The {@link Vector} representing the location to spawn the entity ++ * @param type The entity to spawn ++ * @param reason The reason for the entity's spawn. ++ * @return Resulting Entity of this method ++ */ ++ @SuppressWarnings("unchecked") ++ @NotNull ++ default Entity spawnEntity(@NotNull Vector loc, @NotNull EntityType type, @NotNull CreatureSpawnEvent.SpawnReason reason) { ++ return spawn(loc, (Class) type.getEntityClass(), reason, null); ++ } ++ ++ /** ++ * Creates a entity at the location represented by the given {@link Vector}, 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 loc The {@link Vector} representing the location to spawn the entity ++ * @param type The entity to spawn ++ * @param reason The reason for the entity's spawn. ++ * @param function The function to be run before the entity is spawned. ++ * @return Resulting Entity of this method ++ */ ++ @SuppressWarnings("unchecked") ++ @NotNull ++ default Entity spawnEntity(@NotNull Vector loc, @NotNull EntityType type, @NotNull CreatureSpawnEvent.SpawnReason reason, @Nullable Consumer function) { ++ return spawn(loc, (Class) type.getEntityClass(), reason, function); ++ } ++ ++ @NotNull T spawn(@NotNull Vector location, @NotNull Class clazz, @Nullable Consumer function, @NotNull CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException; ++} +diff --git a/src/main/java/org/bukkit/generator/ChunkGenerator.java b/src/main/java/org/bukkit/generator/ChunkGenerator.java +index 244a8a93c35d5a84e17672ff745051587bca384c..bb0bddaa6d88396f2226a51ae624472e7614aaa1 100644 +--- a/src/main/java/org/bukkit/generator/ChunkGenerator.java ++++ b/src/main/java/org/bukkit/generator/ChunkGenerator.java +@@ -161,6 +161,19 @@ public abstract class ChunkGenerator { + return new ArrayList(); + } + ++ ++ // Paper start ++ /** ++ * Generate decorations in a chunk, with quick access to its neighbors. ++ * ++ * @param world ProtoWorld to generate decorations with. ++ */ ++ @SuppressWarnings("unused") ++ public void generateDecorations(@NotNull io.papermc.paper.world.gen.ProtoWorld world) { ++ // Do nothing by default to maintain compatibility with existing generators. ++ } ++ // Paper end ++ + /** + * Gets a fixed spawn location to use for a given world. + *

diff --git a/patches/server/0714-Add-Feature-Generation-API.patch b/patches/server/0714-Add-Feature-Generation-API.patch new file mode 100644 index 000000000..df94051e6 --- /dev/null +++ b/patches/server/0714-Add-Feature-Generation-API.patch @@ -0,0 +1,123 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: dfsek +Date: Sat, 19 Jun 2021 20:15:59 -0700 +Subject: [PATCH] Add Feature Generation API + + +diff --git a/src/main/java/io/papermc/paper/world/gen/CraftProtoWorld.java b/src/main/java/io/papermc/paper/world/gen/CraftProtoWorld.java +new file mode 100644 +index 0000000000000000000000000000000000000000..33de2d14a90b405c6c0057e1f01d38a5c9e0d3f2 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/world/gen/CraftProtoWorld.java +@@ -0,0 +1,94 @@ ++package io.papermc.paper.world.gen; ++ ++import net.minecraft.core.BlockPos; ++import net.minecraft.server.level.WorldGenRegion; ++import net.minecraft.world.entity.Mob; ++import net.minecraft.world.entity.MobSpawnType; ++import net.minecraft.world.entity.SpawnGroupData; ++import org.bukkit.World; ++import org.bukkit.block.data.BlockData; ++import org.bukkit.craftbukkit.block.data.CraftBlockData; ++import org.bukkit.entity.Entity; ++import org.bukkit.event.entity.CreatureSpawnEvent; ++import org.bukkit.util.Vector; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.Objects; ++import java.util.function.Consumer; ++ ++public class CraftProtoWorld implements ProtoWorld { ++ private WorldGenRegion region; ++ ++ public CraftProtoWorld(WorldGenRegion region) { ++ this.region = region; ++ } ++ ++ public void clearReference() { ++ region = null; ++ } ++ ++ @Override ++ public void setBlockData(int x, int y, int z, @NotNull BlockData data) { ++ BlockPos position = new BlockPos(x, y, z); ++ getDelegate().setBlock(position, ((CraftBlockData) data).getState(), 3); ++ } ++ ++ @Override ++ public void scheduleBlockUpdate(int x, int y, int z) { ++ BlockPos position = new BlockPos(x, y, z); ++ getDelegate().getBlockTicks().scheduleTick(position, getDelegate().getBlockIfLoaded(position), 0); ++ } ++ ++ @Override ++ public void scheduleFluidUpdate(int x, int y, int z) { ++ BlockPos position = new BlockPos(x, y, z); ++ getDelegate().getLiquidTicks().scheduleTick(position, getDelegate().getFluidState(position).getType(), 0); ++ } ++ ++ @Override ++ public @NotNull World getWorld() { ++ // reading/writing the returned Minecraft world causes a deadlock. ++ // By implementing this, and covering it in warnings, we're assuming people won't be stupid, and ++ // if they are stupid, they'll figure it out pretty fast. ++ return getDelegate().getMinecraftWorld().getWorld(); ++ } ++ ++ @Override ++ public @NotNull BlockData getBlockData(int x, int y, int z) { ++ return CraftBlockData.fromData(getDelegate().getBlockState(new BlockPos(x, y, z))); ++ } ++ ++ @Override ++ public int getCenterChunkX() { ++ return getDelegate().getCenter().x; ++ } ++ ++ @Override ++ public int getCenterChunkZ() { ++ return getDelegate().getCenter().z; ++ } ++ ++ @SuppressWarnings({"unchecked", "deprecation"}) ++ @Override ++ public @NotNull T spawn(@NotNull Vector location, @NotNull Class clazz, @Nullable Consumer function, CreatureSpawnEvent.@NotNull SpawnReason reason) throws IllegalArgumentException { ++ net.minecraft.world.entity.Entity entity = getDelegate().getMinecraftWorld().getWorld().createEntity(location.toLocation(getWorld()), clazz); ++ Objects.requireNonNull(entity, "Cannot spawn null entity"); ++ if (entity instanceof Mob) { ++ ((Mob) entity).finalizeSpawn(getDelegate(), getDelegate().getCurrentDifficultyAt(entity.blockPosition()), MobSpawnType.COMMAND, (SpawnGroupData) null, null); ++ } ++ ++ if (function != null) { ++ function.accept((T) entity.getBukkitEntity()); ++ } ++ ++ getDelegate().addEntity(entity, reason); ++ return (T) entity.getBukkitEntity(); ++ } ++ ++ @NotNull ++ private WorldGenRegion getDelegate() { ++ return Objects.requireNonNull(region, "Cannot access ProtoWorld after generation!"); ++ } ++} ++ +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +index 46ce973ca68420825316352ad4df907edf52bfec..bd1794ac7c7d736457203b4773f89af23d21a63d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +@@ -191,6 +191,12 @@ public class CustomChunkGenerator extends InternalChunkGenerator { + if (this.generator.shouldGenerateDecorations()) { + this.delegate.applyBiomeDecoration(region, accessor); + } ++ ++ // Paper start ++ io.papermc.paper.world.gen.CraftProtoWorld protoWorld = new io.papermc.paper.world.gen.CraftProtoWorld(region); ++ generator.generateDecorations(protoWorld); ++ protoWorld.clearReference(); // make sure people dont try to use the ProtoWorld after we're done with it. ++ // Paper end + } + + @Override