mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-22 00:47:38 +01:00
Worldgen Feature API (#5727)
This commit is contained in:
parent
ce6610254f
commit
c4c6e26c00
306
patches/api/0318-Add-Feature-Stage-API.patch
Normal file
306
patches/api/0318-Add-Feature-Stage-API.patch
Normal file
@ -0,0 +1,306 @@
|
||||
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/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.
|
||||
+ * <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);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * 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 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 <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 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<Entity>) 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.
|
||||
+ * <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 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<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.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.
|
||||
* <p>
|
123
patches/server/0714-Add-Feature-Generation-API.patch
Normal file
123
patches/server/0714-Add-Feature-Generation-API.patch
Normal file
@ -0,0 +1,123 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: dfsek <dfsek@protonmail.com>
|
||||
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 <T extends Entity> @NotNull T spawn(@NotNull Vector location, @NotNull Class<T> clazz, @Nullable Consumer<T> 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
|
Loading…
Reference in New Issue
Block a user