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/generation/CraftProtoWorld.java b/src/main/java/io/papermc/paper/world/generation/CraftProtoWorld.java new file mode 100644 index 0000000000000000000000000000000000000000..69b17fe56f28f8978abc3f363a9e805d7c7b007a --- /dev/null +++ b/src/main/java/io/papermc/paper/world/generation/CraftProtoWorld.java @@ -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; +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 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); + } + + @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 6450dbe4e4e9a5f2b5dc3d783aa97e69e296033d..cffb0586bf6c98e90588185c10e8f5dd76bc489b 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java @@ -196,6 +196,12 @@ public class CustomChunkGenerator extends InternalChunkGenerator { if (this.generator.shouldGenerateDecorations()) { this.delegate.applyBiomeDecoration(region, accessor); } + + // Paper start + io.papermc.paper.world.generation.CraftProtoWorld protoWorld = new io.papermc.paper.world.generation.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 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) {