diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java index 85da7a7c8..05ab1d5d0 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java @@ -28,7 +28,6 @@ import com.google.common.collect.Sets; import com.google.common.util.concurrent.Futures; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; -import com.sk89q.jnbt.NBTConstants; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; @@ -528,22 +527,6 @@ public final class PaperweightAdapter implements BukkitImplAdapter placed = TaskManager.taskManager().sync(() -> { serverLevel.captureTreeGeneration = true; @@ -658,7 +655,6 @@ public final class PaperweightFaweAdapter extends FaweAdapter { - loadedEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); - return loadedEntity; - }); + Entity createdEntity = createEntityFromId(state.getType().id(), craftWorld.getHandle()); if (createdEntity != null) { - worldServer.addFreshEntityWithPassengers(createdEntity, SpawnReason.CUSTOM); + LinCompoundTag nativeTag = state.getNbt(); + if (nativeTag != null) { + net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeLin(nativeTag); + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + readTagIntoEntity(tag, createdEntity); + } + + createdEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + worldServer.addFreshEntity(createdEntity, SpawnReason.CUSTOM); return createdEntity.getBukkitEntity(); } else { return null; } } - // This removes all unwanted tags from the main entity and all its passengers - private void removeUnwantedEntityTagsRecursively(net.minecraft.nbt.CompoundTag tag) { - for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { - tag.remove(name); - } - - // Adapted from net.minecraft.world.entity.EntityType#loadEntityRecursive - if (tag.contains("Passengers", LinTagId.LIST.id())) { - net.minecraft.nbt.ListTag nbttaglist = tag.getList("Passengers", LinTagId.COMPOUND.id()); - - for (int i = 0; i < nbttaglist.size(); ++i) { - removeUnwantedEntityTagsRecursively(nbttaglist.getCompound(i)); - } - } - } - @Override public Component getRichBlockName(BlockType blockType) { return TranslatableComponent.of(getBlockFromType(blockType).getDescriptionId()); @@ -884,6 +875,20 @@ public final class PaperweightAdapter implements BukkitImplAdapter biomeRegistry = server.registryAccess().lookupOrThrow(Registries.BIOME); biomeRegistry.getTags().forEach(tag -> { @@ -913,6 +918,39 @@ public final class PaperweightAdapter implements BukkitImplAdapter k = originalWorld.registryAccess().lookupOrThrow(Registries.CONFIGURED_FEATURE).getValue(ResourceLocation.tryParse(type.id())); + ServerChunkCache chunkManager = originalWorld.getChunkSource(); + WorldGenLevel proxyLevel = PaperweightServerLevelDelegateProxy.newInstance(session, originalWorld, this); + return k != null && k.place(proxyLevel, chunkManager.getGenerator(), random, new BlockPos(pt.x(), pt.y(), pt.z())); + } + + @Override + public boolean generateStructure(StructureType type, World world, EditSession session, BlockVector3 pt) { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + Structure k = originalWorld.registryAccess().lookupOrThrow(Registries.STRUCTURE).getValue(ResourceLocation.tryParse(type.id())); + if (k == null) { + return false; + } + + ServerChunkCache chunkManager = originalWorld.getChunkSource(); + WorldGenLevel proxyLevel = PaperweightServerLevelDelegateProxy.newInstance(session, originalWorld, this); + ChunkPos chunkPos = new ChunkPos(new BlockPos(pt.x(), pt.y(), pt.z())); + StructureStart structureStart = k.generate(originalWorld.registryAccess(), chunkManager.getGenerator(), chunkManager.getGenerator().getBiomeSource(), chunkManager.randomState(), originalWorld.getStructureManager(), originalWorld.getSeed(), chunkPos, 0, proxyLevel, biome -> true); + + if (!structureStart.isValid()) { + return false; + } else { + BoundingBox boundingBox = structureStart.getBoundingBox(); + ChunkPos min = new ChunkPos(SectionPos.blockToSectionCoord(boundingBox.minX()), SectionPos.blockToSectionCoord(boundingBox.minZ())); + ChunkPos max = new ChunkPos(SectionPos.blockToSectionCoord(boundingBox.maxX()), SectionPos.blockToSectionCoord(boundingBox.maxZ())); + ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk(proxyLevel, originalWorld.structureManager(), chunkManager.getGenerator(), originalWorld.getRandom(), new BoundingBox(chunkPosx.getMinBlockX(), originalWorld.getMinY(), chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), originalWorld.getMaxY(), chunkPosx.getMaxBlockZ()), chunkPosx)); + return true; + } + } + // ------------------------------------------------------------------------ // Code that is less likely to break // ------------------------------------------------------------------------ diff --git a/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_3/PaperweightServerLevelDelegateProxy.java b/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_3/PaperweightServerLevelDelegateProxy.java new file mode 100644 index 000000000..128824124 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_3/PaperweightServerLevelDelegateProxy.java @@ -0,0 +1,128 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_21_3; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BlockTypes; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +public class PaperweightServerLevelDelegateProxy implements InvocationHandler { + + private final EditSession editSession; + private final ServerLevel serverLevel; + private final PaperweightAdapter adapter; + + private PaperweightServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) { + this.editSession = editSession; + this.serverLevel = serverLevel; + this.adapter = adapter; + } + + public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) { + return (WorldGenLevel) Proxy.newProxyInstance( + serverLevel.getClass().getClassLoader(), + serverLevel.getClass().getInterfaces(), + new PaperweightServerLevelDelegateProxy(editSession, serverLevel, adapter) + ); + } + + @Nullable + private BlockEntity getBlockEntity(BlockPos blockPos) { + BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos); + if (tileEntity == null) { + return null; + } + BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos)); + newEntity.loadWithComponents( + (CompoundTag) adapter.fromNativeLin(this.editSession.getFullBlock(BlockVector3.at( + blockPos.getX(), + blockPos.getY(), + blockPos.getZ() + )).getNbtReference().getValue()), + MinecraftServer.getServer().registryAccess() + ); + + return newEntity; + } + + private BlockState getBlockState(BlockPos blockPos) { + return adapter.adapt(this.editSession.getBlock(BlockVector3.at(blockPos.getX(), blockPos.getY(), blockPos.getZ()))); + } + + private boolean setBlock(BlockPos blockPos, BlockState blockState) { + try { + return editSession.setBlock(BlockVector3.at(blockPos.getX(), blockPos.getY(), blockPos.getZ()), adapter.adapt(blockState)); + } catch (MaxChangedBlocksException e) { + throw new RuntimeException(e); + } + } + + private boolean removeBlock(BlockPos blockPos, boolean bl) { + try { + return editSession.setBlock(BlockVector3.at(blockPos.getX(), blockPos.getY(), blockPos.getZ()), BlockTypes.AIR.getDefaultState()); + } catch (MaxChangedBlocksException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + switch (method.getName()) { + case "a_", "getBlockState" -> { + if (args.length == 1 && args[0] instanceof BlockPos blockPos) { + // getBlockState + return getBlockState(blockPos); + } + } + case "c_", "getBlockEntity" -> { + if (args.length == 1 && args[0] instanceof BlockPos blockPos) { + // getBlockEntity + return getBlockEntity(blockPos); + } + } + case "a", "setBlock", "removeBlock", "destroyBlock" -> { + if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) { + // setBlock + return setBlock(blockPos, blockState); + } else if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) { + // removeBlock (and also matches destroyBlock) + return removeBlock(blockPos, bl); + } + } + default -> { } + } + + return method.invoke(this.serverLevel, args); + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/FaweBlockStateListPopulator.java b/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/FaweBlockStateListPopulator.java new file mode 100644 index 000000000..e8c212e03 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/FaweBlockStateListPopulator.java @@ -0,0 +1,133 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_3; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.FluidState; +import org.bukkit.craftbukkit.util.BlockStateListPopulator; + +import javax.annotation.Nullable; + +public class FaweBlockStateListPopulator extends BlockStateListPopulator { + + private final ServerLevel world; + + public FaweBlockStateListPopulator(ServerLevel world) { + super(world); + this.world = world; + } + + @Override + public long getSeed() { + return world.getSeed(); + } + + @Override + public ServerLevel getLevel() { + return world.getLevel(); + } + + @Override + public MinecraftServer getServer() { + return world.getServer(); + } + + @Override + public ChunkSource getChunkSource() { + return world.getChunkSource(); + } + + @Override + public ChunkAccess getChunk(final int chunkX, final int chunkZ, final ChunkStatus leastStatus, final boolean create) { + return world.getChunk(chunkX, chunkZ, leastStatus, create); + } + + @Override + public BiomeManager getBiomeManager() { + return world.getBiomeManager(); + } + + @Override + public Holder getUncachedNoiseBiome(final int biomeX, final int biomeY, final int biomeZ) { + return world.getUncachedNoiseBiome(biomeX, biomeY, biomeZ); + } + + @Override + public int getSeaLevel() { + return world.getSeaLevel(); + } + + @Override + public FeatureFlagSet enabledFeatures() { + return world.enabledFeatures(); + } + + @Override + public float getShade(final Direction direction, final boolean shaded) { + return world.getShade(direction, shaded); + } + + @Override + public LevelLightEngine getLightEngine() { + return world.getLightEngine(); + } + + @Nullable + @Override + public ChunkAccess getChunkIfLoadedImmediately(final int x, final int z) { + return world.getChunkIfLoadedImmediately(x, z); + } + + @Override + public BlockState getBlockStateIfLoaded(final BlockPos blockposition) { + return world.getBlockStateIfLoaded(blockposition); + } + + @Override + public FluidState getFluidIfLoaded(final BlockPos blockposition) { + return world.getFluidIfLoaded(blockposition); + } + + @Override + public WorldBorder getWorldBorder() { + return world.getWorldBorder(); + } + + @Override + public boolean setBlock(final BlockPos pos, final BlockState state, final int flags, final int maxUpdateDepth) { + return world.setBlock(pos, state, flags, maxUpdateDepth); + } + + @Override + public boolean removeBlock(final BlockPos pos, final boolean move) { + return world.removeBlock(pos, move); + } + + @Override + public boolean destroyBlock(final BlockPos pos, final boolean drop, final Entity breakingEntity, final int maxUpdateDepth) { + return world.destroyBlock(pos, drop, breakingEntity, maxUpdateDepth); + } + + @Override + public BlockState getBlockState(final BlockPos pos) { + return world.getBlockState(pos); + } + + @Override + public boolean setBlock(final BlockPos pos, final BlockState state, final int flags) { + return world.setBlock(pos, state, flags); + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightFaweAdapter.java index 40e6ead32..b776692ac 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightFaweAdapter.java @@ -10,11 +10,12 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.util.NbtUtils; +import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.mojang.serialization.Codec; -import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -45,33 +46,44 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; +import com.sk89q.worldedit.world.generation.StructureType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import io.papermc.lib.PaperLib; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; +import net.minecraft.core.SectionPos; import net.minecraft.core.WritableRegistry; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.StringRepresentable; import net.minecraft.world.entity.Entity; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.StructureStart; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -80,6 +92,7 @@ import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.CraftBlockState; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.entity.CraftEntity; import org.bukkit.craftbukkit.entity.CraftPlayer; @@ -110,7 +123,7 @@ import java.util.stream.Stream; import static net.minecraft.core.registries.Registries.BIOME; -public final class PaperweightFaweAdapter extends FaweAdapter { +public final class PaperweightFaweAdapter extends FaweAdapter { private static final Logger LOGGER = LogManagerCompat.getLogger(); private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE; @@ -163,12 +176,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter getParent() { + public BukkitImplAdapter getParent() { return parent; } @@ -271,7 +284,7 @@ public final class PaperweightFaweAdapter extends FaweAdapter createWorldNativeAccess(org.bukkit.World world) { + public WorldNativeAccess createWorldNativeAccess(World world) { return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world))); } @@ -299,7 +312,7 @@ public final class PaperweightFaweAdapter extends FaweAdapter saveTag = () -> { - final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); + final CompoundTag minecraftTag = new CompoundTag(); readEntityIntoTag(mcEntity, minecraftTag); //add Id for AbstractChangeSet to work final LinCompoundTag tag = (LinCompoundTag) toNativeLin(minecraftTag); @@ -426,7 +439,7 @@ public final class PaperweightFaweAdapter extends FaweAdapter configuredFeature = serverLevel + .registryAccess() + .lookupOrThrow(Registries.CONFIGURED_FEATURE) + .getValue(ResourceLocation.tryParse(feature.id())); + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + + Map placed = TaskManager.taskManager().sync(() -> { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + try { + if (!configuredFeature.place( + populator, + generator, + serverLevel.random, + new BlockPos(pt.x(), pt.y(), pt.z()) + )) { + return null; + } + Map placedBlocks = populator.getList().stream().collect(Collectors.toMap( + CraftBlockState::getPosition, + craftBlockState -> craftBlockState + )); + placedBlocks.putAll(serverLevel.capturedBlockStates); + return placedBlocks; + } finally { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } + }); + + return placeFeatureIntoSession(editSession, populator, placed); + } + + @Override + public boolean generateStructure(StructureType type, World world, EditSession editSession, BlockVector3 pt) { + ServerLevel serverLevel = ((CraftWorld) world).getHandle(); + Structure k = serverLevel + .registryAccess() + .lookupOrThrow(Registries.STRUCTURE) + .getValue(ResourceLocation.tryParse(type.id())); + if (k == null) { + return false; + } + + ServerChunkCache chunkManager = serverLevel.getChunkSource(); + + ChunkPos chunkPos = new ChunkPos(new BlockPos(pt.x(), pt.y(), pt.z())); + + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + Map placed = TaskManager.taskManager().sync(() -> { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + try { + StructureStart structureStart = k.generate( + serverLevel.registryAccess(), + chunkManager.getGenerator(), + chunkManager.getGenerator().getBiomeSource(), + chunkManager.randomState(), + serverLevel.getStructureManager(), + serverLevel.getSeed(), + chunkPos, + 0, + populator, + biome -> true + ); + if (!structureStart.isValid()) { + return null; + } else { + BoundingBox boundingBox = structureStart.getBoundingBox(); + ChunkPos min = new ChunkPos( + SectionPos.blockToSectionCoord(boundingBox.minX()), + SectionPos.blockToSectionCoord(boundingBox.minZ()) + ); + ChunkPos max = new ChunkPos( + SectionPos.blockToSectionCoord(boundingBox.maxX()), + SectionPos.blockToSectionCoord(boundingBox.maxZ()) + ); + ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk( + populator, + serverLevel.structureManager(), + chunkManager.getGenerator(), + serverLevel.getRandom(), + new BoundingBox( + chunkPosx.getMinBlockX(), + serverLevel.getMinY(), + chunkPosx.getMinBlockZ(), + chunkPosx.getMaxBlockX(), + serverLevel.getMaxY(), + chunkPosx.getMaxBlockZ() + ), + chunkPosx + )); + Map placedBlocks = populator.getList().stream().collect(Collectors.toMap( + CraftBlockState::getPosition, + craftBlockState -> craftBlockState + )); + placedBlocks.putAll(serverLevel.capturedBlockStates); + return placedBlocks; + } + } finally { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } + }); + + return placeFeatureIntoSession(editSession, populator, placed); + } + + private boolean placeFeatureIntoSession( + final EditSession editSession, + final FaweBlockStateListPopulator populator, + final Map placed + ) { + if (placed == null || placed.isEmpty()) { + return false; + } + + for (Map.Entry entry : placed.entrySet()) { + CraftBlockState craftBlockState = entry.getValue(); + if (entry.getValue() == null) { + continue; + } + BlockPos pos = entry.getKey(); + editSession.setBlock(pos.getX(), pos.getY(), pos.getZ(), BukkitAdapter.adapt(craftBlockState.getBlockData())); + BlockEntity blockEntity = populator.getBlockEntity(pos); + if (blockEntity != null) { + CompoundTag tag = blockEntity.saveWithId(DedicatedServer.getServer().registryAccess()); + editSession.setTile(pos.getX(), pos.getY(), pos.getZ(), (com.sk89q.jnbt.CompoundTag) toNative(tag)); + } + } + return true; + } + + @Override + public void setupFeatures() { + DedicatedServer server = ((CraftServer) Bukkit.getServer()).getServer(); + + // Features + for (ResourceLocation name : server.registryAccess().lookupOrThrow(Registries.CONFIGURED_FEATURE).keySet()) { + if (ConfiguredFeatureType.REGISTRY.get(name.toString()) == null) { + ConfiguredFeatureType.REGISTRY.register(name.toString(), new ConfiguredFeatureType(name.toString())); + } + } + + // Structures + for (ResourceLocation name : server.registryAccess().lookupOrThrow(Registries.STRUCTURE).keySet()) { + if (StructureType.REGISTRY.get(name.toString()) == null) { + StructureType.REGISTRY.register(name.toString(), new StructureType(name.toString())); + } + } + } + @Override public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final RegistryAccess.Frozen registryAccess = DedicatedServer.getServer().registryAccess(); final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - final net.minecraft.nbt.Tag tag = COMPONENTS_CODEC.encodeStart( + final Tag tag = COMPONENTS_CODEC.encodeStart( registryAccess.createSerializationContext(NbtOps.INSTANCE), nmsStack.getComponentsPatch() ).getOrThrow(); @@ -528,22 +700,22 @@ public final class PaperweightFaweAdapter extends FaweAdapter