From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: dfsek <dfsek@protonmail.com>
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/generation/ProtoWorld.java b/src/main/java/io/papermc/paper/world/generation/ProtoWorld.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3b465d3883547d53e55183c2a4e22c63525a787
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/generation/ProtoWorld.java
@@ -0,0 +1,313 @@
+package io.papermc.paper.world.generation;
+
+import org.bukkit.World;
+import org.bukkit.block.BlockState;
+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.
+ * <p>
+ * 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.
+ * <p>
+ * 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);
+    }
+
+    /**
+     * Sets the {@link BlockState} at a location.
+     *
+     * @param x X coordinate.
+     * @param y Y coordinate.
+     * @param z Z coordinate.
+     * @param state The block state.
+     */
+    void setBlockState(int x, int y, int z, @NotNull BlockState state);
+
+    /**
+     * Sets the {@link BlockState} at a location.
+     *
+     * @param location Location to set block state.
+     * @param state The block state.
+     */
+    default void setBlockState(@NotNull Vector location, @NotNull BlockState state) {
+        setBlockState(location.getBlockX(), location.getBlockY(), location.getBlockZ(), state);
+    }
+
+    /**
+     * Gets the {@link BlockState} at a location.
+     *
+     * @param x X coordinate.
+     * @param y Y coordinate.
+     * @param z Z coordinate.
+     * @return The block state.
+     */
+    @NotNull
+    BlockState getBlockState(int x, int y, int z);
+
+    /**
+     * Gets the {@link BlockState} at a location.
+     *
+     * @param location Location to get block state from.
+     * @return The block state.
+     */
+    @NotNull
+    default BlockState getBlockState(@NotNull Vector location) {
+        return getBlockState(location.getBlockX(), location.getBlockY(), location.getBlockZ());
+    }
+
+    /**
+     * 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.
+     * <p>
+     * Do <b>not</b> attempt to read from/write to this world! Doing so during generation <b>will cause a deadlock!</b>
+     *
+     * @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 {@link 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 {@link ProtoWorld}
+     *
+     * @return The center chunk's Z coordinate.
+     */
+    int getCenterChunkZ();
+
+    /**
+     * Get the Z-coordinate of the block in the center of this {@link ProtoWorld}
+     *
+     * @return The center chunk's Z coordinate.
+     */
+    default int getCenterBlockZ() {
+        return getCenterChunkZ() << 4;
+    }
+
+    /**
+     * Creates an 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 <T>      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 extends Entity> T spawn(@NotNull Vector location, @NotNull Class<T> 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 <T>      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 extends Entity> T spawn(@NotNull Vector location, @NotNull Class<T> 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.
+     * <br>
+     * 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 <T>      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 extends Entity> T spawn(@NotNull Vector location, @NotNull Class<T> clazz, @Nullable Consumer<T> 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.
+     * <br>
+     * 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 <T>      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 extends Entity> T spawn(@NotNull Vector location, @NotNull Class<T> clazz, @NotNull CreatureSpawnEvent.SpawnReason reason, @Nullable Consumer<T> function) throws IllegalArgumentException {
+        return spawn(location, clazz, function, reason);
+    }
+
+    /**
+     * Creates an 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<Entity>) type.getEntityClass(), reason, null);
+    }
+
+    /**
+     * Creates an entity at the location represented by the given {@link Vector}, with
+     * the supplied function run before the entity is added to the world.
+     * <br>
+     * 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<Entity> function) {
+        return spawn(loc, (Class<Entity>) type.getEntityClass(), reason, function);
+    }
+
+    @NotNull <T extends Entity> T spawn(@NotNull Vector location, @NotNull Class<T> clazz, @Nullable Consumer<T> 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 0667315e2bd10254aef59c2a6bcceee9d927b6d5..5cf3f8875753b0293ea56b65a64f0075b03257c3 100644
--- a/src/main/java/org/bukkit/generator/ChunkGenerator.java
+++ b/src/main/java/org/bukkit/generator/ChunkGenerator.java
@@ -343,6 +343,19 @@ public abstract class ChunkGenerator {
         return new ArrayList<BlockPopulator>();
     }
 
+
+    // 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.generation.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.
      * <p>
