From b5be382d00338d954100f2b0514a0d46caa8d7d1 Mon Sep 17 00:00:00 2001 From: dfsek Date: Wed, 7 Jul 2021 03:23:16 -0700 Subject: [PATCH] Add methods to `ProtoWorld` for working with `BlockState`s (#5929) --- patches/api/0319-Add-Feature-Stage-API.patch | 59 +++- .../0714-Add-Feature-Generation-API.patch | 279 +++++++++++++++++- 2 files changed, 328 insertions(+), 10 deletions(-) diff --git a/patches/api/0319-Add-Feature-Stage-API.patch b/patches/api/0319-Add-Feature-Stage-API.patch index d5b9c3c176..3bd221105e 100644 --- a/patches/api/0319-Add-Feature-Stage-API.patch +++ b/patches/api/0319-Add-Feature-Stage-API.patch @@ -6,13 +6,14 @@ 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..cf9443f1cd3039f0e53c645c894483027455465b +index 0000000000000000000000000000000000000000..f3b465d3883547d53e55183c2a4e22c63525a787 --- /dev/null +++ b/src/main/java/io/papermc/paper/world/generation/ProtoWorld.java -@@ -0,0 +1,270 @@ +@@ -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; @@ -58,6 +59,48 @@ index 0000000000000000000000000000000000000000..cf9443f1cd3039f0e53c645c89448302 + } + + /** ++ * 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 @@ -134,7 +177,7 @@ index 0000000000000000000000000000000000000000..cf9443f1cd3039f0e53c645c89448302 + int getCenterChunkX(); + + /** -+ * Get the X-coordinate of the block in the center of this ProtoWorld ++ * Get the X-coordinate of the block in the center of this {@link ProtoWorld} + * + * @return The center chunk's X coordinate. + */ @@ -143,14 +186,14 @@ index 0000000000000000000000000000000000000000..cf9443f1cd3039f0e53c645c89448302 + } + + /** -+ * Get the Z-coordinate of the chunk in the center of this ProtoWorld ++ * 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 ProtoWorld ++ * Get the Z-coordinate of the block in the center of this {@link ProtoWorld} + * + * @return The center chunk's Z coordinate. + */ @@ -159,7 +202,7 @@ index 0000000000000000000000000000000000000000..cf9443f1cd3039f0e53c645c89448302 + } + + /** -+ * Creates a entity at the location represented by the given {@link Vector} ++ * 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 @@ -245,7 +288,7 @@ index 0000000000000000000000000000000000000000..cf9443f1cd3039f0e53c645c89448302 + } + + /** -+ * Creates a entity at the location represented by the given {@link Vector} ++ * 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 @@ -259,7 +302,7 @@ index 0000000000000000000000000000000000000000..cf9443f1cd3039f0e53c645c89448302 + } + + /** -+ * Creates a entity at the location represented by the given {@link Vector}, with ++ * 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. + *
+ * Note that when the function is run, the entity will not be actually in diff --git a/patches/server/0714-Add-Feature-Generation-API.patch b/patches/server/0714-Add-Feature-Generation-API.patch index 92a219777d..8b674e5ad9 100644 --- a/patches/server/0714-Add-Feature-Generation-API.patch +++ b/patches/server/0714-Add-Feature-Generation-API.patch @@ -6,20 +6,26 @@ Subject: [PATCH] Add Feature Generation API diff --git a/src/main/java/io/papermc/paper/world/generation/CraftProtoWorld.java b/src/main/java/io/papermc/paper/world/generation/CraftProtoWorld.java new file mode 100644 -index 0000000000000000000000000000000000000000..90a7c87d859d5d4f948a416eda0afbdf802e86ed +index 0000000000000000000000000000000000000000..69b17fe56f28f8978abc3f363a9e805d7c7b007a --- /dev/null +++ b/src/main/java/io/papermc/paper/world/generation/CraftProtoWorld.java -@@ -0,0 +1,94 @@ +@@ -0,0 +1,115 @@ +package io.papermc.paper.world.generation; + +import net.minecraft.core.BlockPos; ++import net.minecraft.nbt.CompoundTag; +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 net.minecraft.world.level.block.entity.BlockEntity; +import org.bukkit.World; ++import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; ++import org.bukkit.craftbukkit.block.CraftBlockEntityState; ++import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.block.data.CraftBlockData; ++import org.bukkit.craftbukkit.inventory.CraftMetaBlockState; +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.util.Vector; @@ -47,6 +53,21 @@ index 0000000000000000000000000000000000000000..90a7c87d859d5d4f948a416eda0afbdf + } + + @Override ++ public void setBlockState(int x, int y, int z, @NotNull BlockState state) { ++ BlockPos pos = new BlockPos(x, y, z); ++ if(!state.getBlockData().matches(getDelegate().getBlockState(pos).createCraftBlockData())) { ++ throw new IllegalArgumentException("BlockData does not match! Expected " + state.getBlockData().getAsString(false) + ", got " + getDelegate().getBlockState(pos).createCraftBlockData().getAsString(false)); ++ } ++ getDelegate().getBlockEntity(pos).load(((CraftBlockEntityState) state).getSnapshotNBT()); ++ } ++ ++ @Override ++ public @NotNull BlockState getBlockState(int x, int y, int z) { ++ BlockEntity entity = getDelegate().getBlockEntity(new BlockPos(x, y, z)); ++ return CraftMetaBlockState.createBlockState(entity.getBlockState().getBukkitMaterial(), entity.save(new CompoundTag())); ++ } ++ ++ @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); @@ -121,3 +142,257 @@ index 46ce973ca68420825316352ad4df907edf52bfec..65e9ecda0df8a22f93f5979a8c9ac74e } @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java +index bfdeb4f2971e65c38334f2f90c66ef62564e83e6..5ec7a3903838ce2f60926782965107e84b44643c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java +@@ -271,13 +271,18 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + return this.blockEntityTag != null; + } + ++ // Paper start - Delegate to utility method + @Override + public BlockState getBlockState() { +- Material stateMaterial = (this.material != Material.SHIELD) ? this.material : CraftMetaBlockState.shieldToBannerHack(this.blockEntityTag); // Only actually used for jigsaws +- if (this.blockEntityTag != null) { +- switch (this.material) { ++ return createBlockState(this.material, this.blockEntityTag); ++ } ++ ++ public static BlockState createBlockState(Material material, CompoundTag blockEntityTag) { ++ Material stateMaterial = (material != Material.SHIELD) ? material : CraftMetaBlockState.shieldToBannerHack(blockEntityTag); // Only actually used for jigsaws ++ if (blockEntityTag != null) { ++ switch (material) { + case SHIELD: +- this.blockEntityTag.putString("id", "banner"); ++ blockEntityTag.putString("id", "banner"); + break; + case SHULKER_BOX: + case WHITE_SHULKER_BOX: +@@ -296,19 +301,19 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + case GREEN_SHULKER_BOX: + case RED_SHULKER_BOX: + case BLACK_SHULKER_BOX: +- this.blockEntityTag.putString("id", "shulker_box"); ++ blockEntityTag.putString("id", "shulker_box"); + break; + case BEE_NEST: + case BEEHIVE: +- this.blockEntityTag.putString("id", "beehive"); ++ blockEntityTag.putString("id", "beehive"); + break; + } + } + BlockPos blockposition = BlockPos.ZERO; + net.minecraft.world.level.block.state.BlockState iblockdata = CraftMagicNumbers.getBlock(stateMaterial).defaultBlockState(); +- BlockEntity te = (this.blockEntityTag == null) ? null : BlockEntity.loadStatic(blockposition, iblockdata, blockEntityTag); ++ BlockEntity te = (blockEntityTag == null) ? null : BlockEntity.loadStatic(blockposition, iblockdata, blockEntityTag); + +- switch (this.material) { ++ switch (material) { + case ACACIA_SIGN: + case ACACIA_WALL_SIGN: + case BIRCH_SIGN: +@@ -328,53 +333,53 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + if (te == null) { + te = new SignBlockEntity(blockposition, iblockdata); + } +- return new CraftSign(this.material, (SignBlockEntity) te); ++ return new CraftSign(material, (SignBlockEntity) te); + case CHEST: + case TRAPPED_CHEST: + if (te == null) { + te = new ChestBlockEntity(blockposition, iblockdata); + } +- return new CraftChest(this.material, (ChestBlockEntity) te); ++ return new CraftChest(material, (ChestBlockEntity) te); + case FURNACE: + if (te == null) { + te = new FurnaceBlockEntity(blockposition, iblockdata); + } +- return new CraftFurnaceFurnace(this.material, (FurnaceBlockEntity) te); ++ return new CraftFurnaceFurnace(material, (FurnaceBlockEntity) te); + case DISPENSER: + if (te == null) { + te = new DispenserBlockEntity(blockposition, iblockdata); + } +- return new CraftDispenser(this.material, (DispenserBlockEntity) te); ++ return new CraftDispenser(material, (DispenserBlockEntity) te); + case DROPPER: + if (te == null) { + te = new DropperBlockEntity(blockposition, iblockdata); + } +- return new CraftDropper(this.material, (DropperBlockEntity) te); ++ return new CraftDropper(material, (DropperBlockEntity) te); + case END_GATEWAY: + if (te == null) { + te = new TheEndGatewayBlockEntity(blockposition, iblockdata); + } +- return new CraftEndGateway(this.material, (TheEndGatewayBlockEntity) te); ++ return new CraftEndGateway(material, (TheEndGatewayBlockEntity) te); + case HOPPER: + if (te == null) { + te = new HopperBlockEntity(blockposition, iblockdata); + } +- return new CraftHopper(this.material, (HopperBlockEntity) te); ++ return new CraftHopper(material, (HopperBlockEntity) te); + case SPAWNER: + if (te == null) { + te = new SpawnerBlockEntity(blockposition, iblockdata); + } +- return new CraftCreatureSpawner(this.material, (SpawnerBlockEntity) te); ++ return new CraftCreatureSpawner(material, (SpawnerBlockEntity) te); + case JUKEBOX: + if (te == null) { + te = new JukeboxBlockEntity(blockposition, iblockdata); + } +- return new CraftJukebox(this.material, (JukeboxBlockEntity) te); ++ return new CraftJukebox(material, (JukeboxBlockEntity) te); + case BREWING_STAND: + if (te == null) { + te = new BrewingStandBlockEntity(blockposition, iblockdata); + } +- return new CraftBrewingStand(this.material, (BrewingStandBlockEntity) te); ++ return new CraftBrewingStand(material, (BrewingStandBlockEntity) te); + case CREEPER_HEAD: + case CREEPER_WALL_HEAD: + case DRAGON_HEAD: +@@ -390,24 +395,24 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + if (te == null) { + te = new SkullBlockEntity(blockposition, iblockdata); + } +- return new CraftSkull(this.material, (SkullBlockEntity) te); ++ return new CraftSkull(material, (SkullBlockEntity) te); + case COMMAND_BLOCK: + case REPEATING_COMMAND_BLOCK: + case CHAIN_COMMAND_BLOCK: + if (te == null) { + te = new CommandBlockEntity(blockposition, iblockdata); + } +- return new CraftCommandBlock(this.material, (CommandBlockEntity) te); ++ return new CraftCommandBlock(material, (CommandBlockEntity) te); + case BEACON: + if (te == null) { + te = new BeaconBlockEntity(blockposition, iblockdata); + } +- return new CraftBeacon(this.material, (BeaconBlockEntity) te); ++ return new CraftBeacon(material, (BeaconBlockEntity) te); + case SHIELD: + if (te == null) { + te = new BannerBlockEntity(blockposition, iblockdata); + } +- ((BannerBlockEntity) te).baseColor = (this.blockEntityTag == null) ? DyeColor.WHITE : DyeColor.byId(this.blockEntityTag.getInt(CraftMetaBanner.BASE.NBT)); ++ ((BannerBlockEntity) te).baseColor = (blockEntityTag == null) ? DyeColor.WHITE : DyeColor.byId(blockEntityTag.getInt(CraftMetaBanner.BASE.NBT)); + case BLACK_BANNER: + case BLACK_WALL_BANNER: + case BLUE_BANNER: +@@ -443,12 +448,12 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + if (te == null) { + te = new BannerBlockEntity(blockposition, iblockdata); + } +- return new CraftBanner(this.material == Material.SHIELD ? CraftMetaBlockState.shieldToBannerHack(this.blockEntityTag) : this.material, (BannerBlockEntity) te); ++ return new CraftBanner(material == Material.SHIELD ? CraftMetaBlockState.shieldToBannerHack(blockEntityTag) : material, (BannerBlockEntity) te); + case STRUCTURE_BLOCK: + if (te == null) { + te = new StructureBlockEntity(blockposition, iblockdata); + } +- return new CraftStructureBlock(this.material, (StructureBlockEntity) te); ++ return new CraftStructureBlock(material, (StructureBlockEntity) te); + case SHULKER_BOX: + case WHITE_SHULKER_BOX: + case ORANGE_SHULKER_BOX: +@@ -469,78 +474,79 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + if (te == null) { + te = new ShulkerBoxBlockEntity(blockposition, iblockdata); + } +- return new CraftShulkerBox(this.material, (ShulkerBoxBlockEntity) te); ++ return new CraftShulkerBox(material, (ShulkerBoxBlockEntity) te); + case ENCHANTING_TABLE: + if (te == null) { + te = new EnchantmentTableBlockEntity(blockposition, iblockdata); + } +- return new CraftEnchantingTable(this.material, (EnchantmentTableBlockEntity) te); ++ return new CraftEnchantingTable(material, (EnchantmentTableBlockEntity) te); + case ENDER_CHEST: + if (te == null) { + te = new EnderChestBlockEntity(blockposition, iblockdata); + } +- return new CraftEnderChest(this.material, (EnderChestBlockEntity) te); ++ return new CraftEnderChest(material, (EnderChestBlockEntity) te); + case DAYLIGHT_DETECTOR: + if (te == null) { + te = new DaylightDetectorBlockEntity(blockposition, iblockdata); + } +- return new CraftDaylightDetector(this.material, (DaylightDetectorBlockEntity) te); ++ return new CraftDaylightDetector(material, (DaylightDetectorBlockEntity) te); + case COMPARATOR: + if (te == null) { + te = new ComparatorBlockEntity(blockposition, iblockdata); + } +- return new CraftComparator(this.material, (ComparatorBlockEntity) te); ++ return new CraftComparator(material, (ComparatorBlockEntity) te); + case BARREL: + if (te == null) { + te = new BarrelBlockEntity(blockposition, iblockdata); + } +- return new CraftBarrel(this.material, (BarrelBlockEntity) te); ++ return new CraftBarrel(material, (BarrelBlockEntity) te); + case BELL: + if (te == null) { + te = new BellBlockEntity(blockposition, iblockdata); + } +- return new CraftBell(this.material, (BellBlockEntity) te); ++ return new CraftBell(material, (BellBlockEntity) te); + case BLAST_FURNACE: + if (te == null) { + te = new BlastFurnaceBlockEntity(blockposition, iblockdata); + } +- return new CraftBlastFurnace(this.material, (BlastFurnaceBlockEntity) te); ++ return new CraftBlastFurnace(material, (BlastFurnaceBlockEntity) te); + case CAMPFIRE: + case SOUL_CAMPFIRE: + if (te == null) { + te = new CampfireBlockEntity(blockposition, iblockdata); + } +- return new CraftCampfire(this.material, (CampfireBlockEntity) te); ++ return new CraftCampfire(material, (CampfireBlockEntity) te); + case JIGSAW: + if (te == null) { + te = new JigsawBlockEntity(blockposition, iblockdata); + } +- return new CraftJigsaw(this.material, (JigsawBlockEntity) te); ++ return new CraftJigsaw(material, (JigsawBlockEntity) te); + case LECTERN: + if (te == null) { + te = new LecternBlockEntity(blockposition, iblockdata); + } +- return new CraftLectern(this.material, (LecternBlockEntity) te); ++ return new CraftLectern(material, (LecternBlockEntity) te); + case SMOKER: + if (te == null) { + te = new SmokerBlockEntity(blockposition, iblockdata); + } +- return new CraftSmoker(this.material, (SmokerBlockEntity) te); ++ return new CraftSmoker(material, (SmokerBlockEntity) te); + case BEE_NEST: + case BEEHIVE: + if (te == null) { + te = new BeehiveBlockEntity(blockposition, iblockdata); + } +- return new CraftBeehive(this.material, (BeehiveBlockEntity) te); ++ return new CraftBeehive(material, (BeehiveBlockEntity) te); + case SCULK_SENSOR: + if (te == null) { + te = new SculkSensorBlockEntity(blockposition, iblockdata); + } +- return new CraftSculkSensor(this.material, (SculkSensorBlockEntity) te); ++ return new CraftSculkSensor(material, (SculkSensorBlockEntity) te); + default: +- throw new IllegalStateException("Missing blockState for " + this.material); ++ throw new IllegalStateException("Missing blockState for " + material); + } + } ++ // Paper end + + @Override + public void setBlockState(BlockState blockState) {