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 28391ee583..ce2332b925 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 @@ -68,10 +68,12 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderSet; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; @@ -113,6 +115,9 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.CoralTreeFeature; +import net.minecraft.world.level.levelgen.feature.TreeFeature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -918,6 +923,20 @@ public void initializeRegistries() { } } + // Trees + HolderLookup.RegistryLookup placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); + placedFeatureRegistry.listElements() + .filter(feature -> { + var underlyingFeature = feature.value().feature().value().feature(); + return underlyingFeature instanceof TreeFeature || underlyingFeature instanceof CoralTreeFeature; + }) + .forEach(feature -> { + String key = feature.key().toString(); + if (TreeType.REGISTRY.get(key) == null) { + TreeType.REGISTRY.register(key, new TreeType(key)); + } + }); + // BiomeCategories Registry biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME); biomeRegistry.getTagNames().forEach(tagKey -> { diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java index dadd3f1139..46d7d0b93e 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java @@ -19,6 +19,7 @@ import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -51,6 +52,7 @@ 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.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import io.papermc.lib.PaperLib; @@ -88,6 +90,7 @@ 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.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -679,6 +682,44 @@ public boolean generateStructure(StructureType type, World world, EditSession ed //FAWE end } + @Override + public boolean generateTree( + final TreeType treeType, + final World world, + final EditSession session, + final BlockVector3 pt + ) throws MaxChangedBlocksException { + ServerLevel serverLevel = getServerLevel(world); + ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); + + PlacedFeature placedFeature = serverLevel + .registryAccess() + .registryOrThrow(Registries.PLACED_FEATURE) + .get(ResourceLocation.tryParse(treeType.id())); + + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + List placed = TaskManager.taskManager().sync(() -> { + preCaptureStates(serverLevel); + try { + if (!placedFeature.place( + populator, + generator, + serverLevel.random, + new BlockPos(pt.x(), pt.y(), pt.z()) + )) { + return null; + } + List placedBlocks = new ArrayList<>(populator.getList()); + placedBlocks.addAll(serverLevel.capturedBlockStates.values()); + return placedBlocks; + } finally { + postCaptureBlockStates(serverLevel); + } + }); + + return placeFeatureIntoSession(session, populator, placed); + } + private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R3/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R3/PaperweightAdapter.java index f524676068..f4562764bc 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R3/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R3/PaperweightAdapter.java @@ -68,10 +68,12 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderSet; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; @@ -113,6 +115,9 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.CoralTreeFeature; +import net.minecraft.world.level.levelgen.feature.TreeFeature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -917,6 +922,20 @@ public void initializeRegistries() { } } + // Trees + HolderLookup.RegistryLookup placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); + placedFeatureRegistry.listElements() + .filter(feature -> { + var underlyingFeature = feature.value().feature().value().feature(); + return underlyingFeature instanceof TreeFeature || underlyingFeature instanceof CoralTreeFeature; + }) + .forEach(feature -> { + String key = feature.key().toString(); + if (TreeType.REGISTRY.get(key) == null) { + TreeType.REGISTRY.register(key, new TreeType(key)); + } + }); + // BiomeCategories Registry biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME); biomeRegistry.getTagNames().forEach(tagKey -> { diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java index c622b25cb9..3b69d37248 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java @@ -19,6 +19,7 @@ import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -50,6 +51,7 @@ 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.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import io.papermc.lib.PaperLib; @@ -87,6 +89,7 @@ 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.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -678,6 +681,44 @@ public boolean generateStructure(StructureType type, World world, EditSession ed //FAWE end } + @Override + public boolean generateTree( + final TreeType treeType, + final World world, + final EditSession session, + final BlockVector3 pt + ) throws MaxChangedBlocksException { + ServerLevel serverLevel = getServerLevel(world); + ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); + + PlacedFeature placedFeature = serverLevel + .registryAccess() + .registryOrThrow(Registries.PLACED_FEATURE) + .get(ResourceLocation.tryParse(treeType.id())); + + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + List placed = TaskManager.taskManager().sync(() -> { + preCaptureStates(serverLevel); + try { + if (!placedFeature.place( + populator, + generator, + serverLevel.random, + new BlockPos(pt.x(), pt.y(), pt.z()) + )) { + return null; + } + List placedBlocks = new ArrayList<>(populator.getList()); + placedBlocks.addAll(serverLevel.capturedBlockStates.values()); + return placedBlocks; + } finally { + postCaptureBlockStates(serverLevel); + } + }); + + return placeFeatureIntoSession(session, populator, placed); + } + private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightAdapter.java index cb6da03c34..47785f4a9e 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R4/PaperweightAdapter.java @@ -68,10 +68,12 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderSet; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; @@ -117,6 +119,9 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.CoralTreeFeature; +import net.minecraft.world.level.levelgen.feature.TreeFeature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -941,6 +946,20 @@ public void initializeRegistries() { } } + // Trees + HolderLookup.RegistryLookup placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); + placedFeatureRegistry.listElements() + .filter(feature -> { + var underlyingFeature = feature.value().feature().value().feature(); + return underlyingFeature instanceof TreeFeature || underlyingFeature instanceof CoralTreeFeature; + }) + .forEach(feature -> { + String key = feature.key().toString(); + if (TreeType.REGISTRY.get(key) == null) { + TreeType.REGISTRY.register(key, new TreeType(key)); + } + }); + // BiomeCategories Registry biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME); biomeRegistry.getTagNames().forEach(tagKey -> { diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java index 44bb4c6ec5..3526440b07 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightFaweAdapter.java @@ -20,6 +20,7 @@ import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -52,6 +53,7 @@ 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.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import io.papermc.lib.PaperLib; @@ -92,6 +94,7 @@ 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.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -692,6 +695,44 @@ public boolean generateStructure(StructureType type, World world, EditSession ed //FAWE end } + @Override + public boolean generateTree( + final TreeType treeType, + final World world, + final EditSession session, + final BlockVector3 pt + ) throws MaxChangedBlocksException { + ServerLevel serverLevel = getServerLevel(world); + ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); + + PlacedFeature placedFeature = serverLevel + .registryAccess() + .registryOrThrow(Registries.PLACED_FEATURE) + .get(ResourceLocation.tryParse(treeType.id())); + + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + List placed = TaskManager.taskManager().sync(() -> { + preCaptureStates(serverLevel); + try { + if (!placedFeature.place( + populator, + generator, + serverLevel.random, + new BlockPos(pt.x(), pt.y(), pt.z()) + )) { + return null; + } + List placedBlocks = new ArrayList<>(populator.getList()); + placedBlocks.addAll(serverLevel.capturedBlockStates.values()); + return placedBlocks; + } finally { + postCaptureBlockStates(serverLevel); + } + }); + + return placeFeatureIntoSession(session, populator, placed); + } + private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java index 5e8ed03d7e..6f6cd435a2 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_R1/PaperweightAdapter.java @@ -29,6 +29,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.Lifecycle; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; @@ -68,11 +69,13 @@ import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.SharedConstants; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderSet; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponentPatch; @@ -117,6 +120,10 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.CoralTreeFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.TreeFeature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -927,6 +934,20 @@ public void initializeRegistries() { } } + // Trees + HolderLookup.RegistryLookup placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); + placedFeatureRegistry.listElements() + .filter(feature -> { + var underlyingFeature = feature.value().feature().value().feature(); + return underlyingFeature instanceof TreeFeature || underlyingFeature instanceof CoralTreeFeature; + }) + .forEach(feature -> { + String key = feature.key().toString(); + if (TreeType.REGISTRY.get(key) == null) { + TreeType.REGISTRY.register(key, new TreeType(key)); + } + }); + // BiomeCategories Registry biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME); biomeRegistry.getTagNames().forEach(tagKey -> { @@ -957,6 +978,16 @@ public void sendBiomeUpdates(World world, Iterable chunks) { } @Override + public boolean generateTree(TreeType treeType, World world, EditSession session, BlockVector3 pt) throws MaxChangedBlocksException { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + PlacedFeature k = originalWorld.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE) + .getOrThrow(ResourceKey.create(Registries.PLACED_FEATURE, ResourceLocation.tryParse(treeType.id()))) + .value(); + 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())); + } + public boolean generateFeature(ConfiguredFeatureType type, World world, EditSession session, BlockVector3 pt) { ServerLevel originalWorld = ((CraftWorld) world).getHandle(); ConfiguredFeature k = originalWorld.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).get(ResourceLocation.tryParse(type.id())); diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java index 033d2983c0..e6a542a607 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightFaweAdapter.java @@ -20,6 +20,7 @@ import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -52,6 +53,7 @@ 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.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import io.papermc.lib.PaperLib; @@ -92,6 +94,7 @@ 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.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -693,6 +696,44 @@ public boolean generateStructure(StructureType type, World world, EditSession ed //FAWE end } + @Override + public boolean generateTree( + final TreeType treeType, + final World world, + final EditSession session, + final BlockVector3 pt + ) throws MaxChangedBlocksException { + ServerLevel serverLevel = getServerLevel(world); + ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); + + PlacedFeature placedFeature = serverLevel + .registryAccess() + .registryOrThrow(Registries.PLACED_FEATURE) + .get(ResourceLocation.tryParse(treeType.id())); + + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + List placed = TaskManager.taskManager().sync(() -> { + preCaptureStates(serverLevel); + try { + if (!placedFeature.place( + populator, + generator, + serverLevel.random, + new BlockPos(pt.x(), pt.y(), pt.z()) + )) { + return null; + } + List placedBlocks = new ArrayList<>(populator.getList()); + placedBlocks.addAll(serverLevel.capturedBlockStates.values()); + return placedBlocks; + } finally { + postCaptureBlockStates(serverLevel); + } + }); + + return placeFeatureIntoSession(session, populator, placed); + } + private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, diff --git a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_11/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_11/PaperweightAdapter.java index 57e5a6cc8a..f1d65c8da8 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_11/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_11/PaperweightAdapter.java @@ -70,6 +70,7 @@ import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; @@ -130,6 +131,10 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.CoralTreeFeature; +import net.minecraft.world.level.levelgen.feature.FallenTreeFeature; +import net.minecraft.world.level.levelgen.feature.TreeFeature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -902,6 +907,19 @@ public void initializeRegistries() { } } + // Trees + Registry placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); + for (Identifier name : placedFeatureRegistry.keySet()) { + // Do some hackery to make sure this is a tree + var underlyingFeature = placedFeatureRegistry.get(name).get().value().feature().value().feature(); + if (underlyingFeature instanceof TreeFeature || underlyingFeature instanceof FallenTreeFeature || underlyingFeature instanceof CoralTreeFeature) { + String key = name.toString(); + if (TreeType.REGISTRY.get(key) == null) { + TreeType.REGISTRY.register(key, new TreeType(key)); + } + } + } + // BiomeCategories Registry biomeRegistry = server.registryAccess().lookupOrThrow(Registries.BIOME); biomeRegistry.getTags().forEach(tag -> { @@ -920,6 +938,17 @@ public void initializeRegistries() { }); } + @Override + public boolean generateTree(TreeType treeType, World world, EditSession session, BlockVector3 pt) throws MaxChangedBlocksException { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + PlacedFeature feature = originalWorld.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE).getValue(Identifier.tryParse(treeType.id())); + ServerChunkCache chunkManager = originalWorld.getChunkSource(); + try (PaperweightServerLevelDelegateProxy.LevelAndProxy proxyLevel = + PaperweightServerLevelDelegateProxy.newInstance(session, originalWorld, this)) { + return feature != null && feature.place(proxyLevel.level(), chunkManager.getGenerator(), random, new BlockPos(pt.x(), pt.y(), pt.z())); + } + } + public boolean generateFeature(ConfiguredFeatureType type, World world, EditSession session, BlockVector3 pt) { ServerLevel originalWorld = ((CraftWorld) world).getHandle(); ConfiguredFeature feature = originalWorld.registryAccess().lookupOrThrow(Registries.CONFIGURED_FEATURE).getValue(Identifier.tryParse(type.id())); diff --git a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightFaweAdapter.java index 77c772a452..c9bc10e856 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_11/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_11/PaperweightFaweAdapter.java @@ -18,6 +18,7 @@ import com.mojang.serialization.Codec; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -50,6 +51,7 @@ 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.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import io.papermc.lib.PaperLib; @@ -90,6 +92,7 @@ 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.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -659,6 +662,44 @@ public boolean generateStructure(StructureType type, World world, EditSession ed return placeFeatureIntoSession(editSession, populator, placed); } + @Override + public boolean generateTree( + final TreeType treeType, + final World world, + final EditSession session, + final BlockVector3 pt + ) throws MaxChangedBlocksException { + ServerLevel serverLevel = getServerLevel(world); + ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); + + PlacedFeature placedFeature = serverLevel + .registryAccess() + .lookupOrThrow(Registries.PLACED_FEATURE) + .getValue(Identifier.tryParse(treeType.id())); + + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + List placed = TaskManager.taskManager().sync(() -> { + preCaptureStates(serverLevel); + try { + if (!placedFeature.place( + populator, + generator, + serverLevel.random, + new BlockPos(pt.x(), pt.y(), pt.z()) + )) { + return null; + } + List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); + placedBlocks.addAll(serverLevel.capturedBlockStates.values()); + return placedBlocks; + } finally { + postCaptureBlockStates(serverLevel); + } + }); + + return placeFeatureIntoSession(session, populator, placed); + } + private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightAdapter.java index 01987f18cb..70fbe27937 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_4/PaperweightAdapter.java @@ -65,6 +65,7 @@ import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.SharedConstants; import net.minecraft.Util; @@ -112,6 +113,9 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.CoralTreeFeature; +import net.minecraft.world.level.levelgen.feature.TreeFeature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -879,6 +883,19 @@ public void initializeRegistries() { } } + // Trees + Registry placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); + for (ResourceLocation name : placedFeatureRegistry.keySet()) { + // Do some hackery to make sure this is a tree + var underlyingFeature = placedFeatureRegistry.get(name).get().value().feature().value().feature(); + if (underlyingFeature instanceof TreeFeature || underlyingFeature instanceof CoralTreeFeature) { + String key = name.toString(); + if (TreeType.REGISTRY.get(key) == null) { + TreeType.REGISTRY.register(key, new TreeType(key)); + } + } + } + // BiomeCategories Registry biomeRegistry = server.registryAccess().lookupOrThrow(Registries.BIOME); biomeRegistry.getTags().forEach(tag -> { @@ -908,6 +925,17 @@ public void sendBiomeUpdates(World world, Iterable chunks) { originalWorld.getChunkSource().chunkMap.resendBiomesForChunks(nativeChunks); } + @Override + public boolean generateTree(TreeType treeType, World world, EditSession session, BlockVector3 pt) throws MaxChangedBlocksException { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + PlacedFeature feature = originalWorld.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE).getValue(ResourceLocation.tryParse(treeType.id())); + ServerChunkCache chunkManager = originalWorld.getChunkSource(); + try (PaperweightServerLevelDelegateProxy.LevelAndProxy proxyLevel = + PaperweightServerLevelDelegateProxy.newInstance(session, originalWorld, this)) { + return feature != null && feature.place(proxyLevel.level(), chunkManager.getGenerator(), random, new BlockPos(pt.x(), pt.y(), pt.z())); + } + } + public boolean generateFeature(ConfiguredFeatureType type, World world, EditSession session, BlockVector3 pt) { ServerLevel originalWorld = ((CraftWorld) world).getHandle(); ConfiguredFeature feature = originalWorld.registryAccess().lookupOrThrow(Registries.CONFIGURED_FEATURE).getValue(ResourceLocation.tryParse(type.id())); diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java index f1282259a1..5aeba33a96 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightFaweAdapter.java @@ -18,6 +18,7 @@ import com.google.common.collect.Sets; import com.mojang.serialization.Codec; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -50,6 +51,7 @@ 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.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import io.papermc.lib.PaperLib; @@ -91,6 +93,7 @@ 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.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -671,6 +674,44 @@ public boolean generateStructure(StructureType type, World world, EditSession ed return placeFeatureIntoSession(editSession, populator, placed); } + @Override + public boolean generateTree( + final TreeType treeType, + final World world, + final EditSession session, + final BlockVector3 pt + ) throws MaxChangedBlocksException { + ServerLevel serverLevel = getServerLevel(world); + ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); + + PlacedFeature placedFeature = serverLevel + .registryAccess() + .lookupOrThrow(Registries.PLACED_FEATURE) + .getValue(ResourceLocation.tryParse(treeType.id())); + + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + List placed = TaskManager.taskManager().sync(() -> { + preCaptureStates(serverLevel); + try { + if (!placedFeature.place( + populator, + generator, + serverLevel.random, + new BlockPos(pt.x(), pt.y(), pt.z()) + )) { + return null; + } + List placedBlocks = new ArrayList<>(populator.getList()); + placedBlocks.addAll(serverLevel.capturedBlockStates.values()); + return placedBlocks; + } finally { + postCaptureBlockStates(serverLevel); + } + }); + + return placeFeatureIntoSession(session, populator, placed); + } + private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, diff --git a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_5/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_5/PaperweightAdapter.java index 7dfb944b6c..0d95e3fa40 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_5/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_5/PaperweightAdapter.java @@ -66,6 +66,7 @@ import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.SharedConstants; import net.minecraft.Util; @@ -126,6 +127,9 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.FallenTreeFeature; +import net.minecraft.world.level.levelgen.feature.TreeFeature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -891,6 +895,19 @@ public void initializeRegistries() { } } + // Trees + Registry placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); + for (ResourceLocation name : placedFeatureRegistry.keySet()) { + // Do some hackery to make sure this is a tree + var underlyingFeature = placedFeatureRegistry.get(name).get().value().feature().value().feature(); + if (underlyingFeature instanceof TreeFeature || underlyingFeature instanceof FallenTreeFeature) { + String key = name.toString(); + if (TreeType.REGISTRY.get(key) == null) { + TreeType.REGISTRY.register(key, new TreeType(key)); + } + } + } + // BiomeCategories Registry biomeRegistry = server.registryAccess().lookupOrThrow(Registries.BIOME); biomeRegistry.getTags().forEach(tag -> { @@ -909,6 +926,17 @@ public void initializeRegistries() { }); } + @Override + public boolean generateTree(TreeType treeType, World world, EditSession session, BlockVector3 pt) throws MaxChangedBlocksException { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + PlacedFeature feature = originalWorld.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE).getValue(ResourceLocation.tryParse(treeType.id())); + ServerChunkCache chunkManager = originalWorld.getChunkSource(); + try (PaperweightServerLevelDelegateProxy.LevelAndProxy proxyLevel = + PaperweightServerLevelDelegateProxy.newInstance(session, originalWorld, this)) { + return feature != null && feature.place(proxyLevel.level(), chunkManager.getGenerator(), random, new BlockPos(pt.x(), pt.y(), pt.z())); + } + } + public boolean generateFeature(ConfiguredFeatureType type, World world, EditSession session, BlockVector3 pt) { ServerLevel originalWorld = ((CraftWorld) world).getHandle(); ConfiguredFeature feature = originalWorld.registryAccess().lookupOrThrow(Registries.CONFIGURED_FEATURE).getValue(ResourceLocation.tryParse(type.id())); diff --git a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweAdapter.java index d9be2690fc..9db33d44a3 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_5/PaperweightFaweAdapter.java @@ -19,6 +19,7 @@ import com.mojang.serialization.Codec; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -51,6 +52,7 @@ 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.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import io.papermc.lib.PaperLib; @@ -91,6 +93,7 @@ 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.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -665,6 +668,44 @@ public boolean generateStructure(StructureType type, World world, EditSession ed return placeFeatureIntoSession(editSession, populator, placed); } + @Override + public boolean generateTree( + final TreeType treeType, + final World world, + final EditSession session, + final BlockVector3 pt + ) throws MaxChangedBlocksException { + ServerLevel serverLevel = getServerLevel(world); + ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); + + PlacedFeature placedFeature = serverLevel + .registryAccess() + .lookupOrThrow(Registries.PLACED_FEATURE) + .getValue(ResourceLocation.tryParse(treeType.id())); + + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + List placed = TaskManager.taskManager().sync(() -> { + preCaptureStates(serverLevel); + try { + if (!placedFeature.place( + populator, + generator, + serverLevel.random, + new BlockPos(pt.x(), pt.y(), pt.z()) + )) { + return null; + } + List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); + placedBlocks.addAll(serverLevel.capturedBlockStates.values()); + return placedBlocks; + } finally { + postCaptureBlockStates(serverLevel); + } + }); + + return placeFeatureIntoSession(session, populator, placed); + } + private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, diff --git a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_6/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_6/PaperweightAdapter.java index 20acb6a704..eb3b0b6d20 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_6/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_6/PaperweightAdapter.java @@ -69,6 +69,7 @@ import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.SharedConstants; import net.minecraft.Util; @@ -130,6 +131,10 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.CoralTreeFeature; +import net.minecraft.world.level.levelgen.feature.FallenTreeFeature; +import net.minecraft.world.level.levelgen.feature.TreeFeature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -914,6 +919,19 @@ public void initializeRegistries() { } } + // Trees + Registry placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); + for (ResourceLocation name : placedFeatureRegistry.keySet()) { + // Do some hackery to make sure this is a tree + var underlyingFeature = placedFeatureRegistry.get(name).get().value().feature().value().feature(); + if (underlyingFeature instanceof TreeFeature || underlyingFeature instanceof FallenTreeFeature || underlyingFeature instanceof CoralTreeFeature) { + String key = name.toString(); + if (TreeType.REGISTRY.get(key) == null) { + TreeType.REGISTRY.register(key, new TreeType(key)); + } + } + } + // BiomeCategories Registry biomeRegistry = server.registryAccess().lookupOrThrow(Registries.BIOME); biomeRegistry.getTags().forEach(tag -> { @@ -932,6 +950,17 @@ public void initializeRegistries() { }); } + @Override + public boolean generateTree(TreeType treeType, World world, EditSession session, BlockVector3 pt) throws MaxChangedBlocksException { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + PlacedFeature feature = originalWorld.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE).getValue(ResourceLocation.tryParse(treeType.id())); + ServerChunkCache chunkManager = originalWorld.getChunkSource(); + try (PaperweightServerLevelDelegateProxy.LevelAndProxy proxyLevel = + PaperweightServerLevelDelegateProxy.newInstance(session, originalWorld, this)) { + return feature != null && feature.place(proxyLevel.level(), chunkManager.getGenerator(), random, new BlockPos(pt.x(), pt.y(), pt.z())); + } + } + public boolean generateFeature(ConfiguredFeatureType type, World world, EditSession session, BlockVector3 pt) { ServerLevel originalWorld = ((CraftWorld) world).getHandle(); ConfiguredFeature feature = originalWorld.registryAccess().lookupOrThrow(Registries.CONFIGURED_FEATURE).getValue(ResourceLocation.tryParse(type.id())); diff --git a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweAdapter.java index 7c663390c0..a09c96b7a8 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_6/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_6/PaperweightFaweAdapter.java @@ -19,6 +19,7 @@ import com.mojang.serialization.Codec; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -51,6 +52,7 @@ 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.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import io.papermc.lib.PaperLib; @@ -91,6 +93,7 @@ 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.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -681,6 +684,44 @@ public boolean canTransformBlocks() { return placeFeatureIntoSession(editSession, populator, placed); } + @Override + public boolean generateTree( + final TreeType treeType, + final World world, + final EditSession session, + final BlockVector3 pt + ) throws MaxChangedBlocksException { + ServerLevel serverLevel = getServerLevel(world); + ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); + + PlacedFeature placedFeature = serverLevel + .registryAccess() + .lookupOrThrow(Registries.PLACED_FEATURE) + .getValue(ResourceLocation.tryParse(treeType.id())); + + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + List placed = TaskManager.taskManager().sync(() -> { + preCaptureStates(serverLevel); + try { + if (!placedFeature.place( + populator, + generator, + serverLevel.random, + new BlockPos(pt.x(), pt.y(), pt.z()) + )) { + return null; + } + List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); + placedBlocks.addAll(serverLevel.capturedBlockStates.values()); + return placedBlocks; + } finally { + postCaptureBlockStates(serverLevel); + } + }); + + return placeFeatureIntoSession(session, populator, placed); + } + private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, diff --git a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_9/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_9/PaperweightAdapter.java index 17898f34f4..78b86d485b 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_9/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_21_9/PaperweightAdapter.java @@ -69,6 +69,7 @@ import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.SharedConstants; import net.minecraft.Util; @@ -129,6 +130,10 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.CoralTreeFeature; +import net.minecraft.world.level.levelgen.feature.FallenTreeFeature; +import net.minecraft.world.level.levelgen.feature.TreeFeature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -911,6 +916,19 @@ public void initializeRegistries() { } } + // Trees + Registry placedFeatureRegistry = server.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); + for (ResourceLocation name : placedFeatureRegistry.keySet()) { + // Do some hackery to make sure this is a tree + var underlyingFeature = placedFeatureRegistry.get(name).get().value().feature().value().feature(); + if (underlyingFeature instanceof TreeFeature || underlyingFeature instanceof FallenTreeFeature || underlyingFeature instanceof CoralTreeFeature) { + String key = name.toString(); + if (TreeType.REGISTRY.get(key) == null) { + TreeType.REGISTRY.register(key, new TreeType(key)); + } + } + } + // BiomeCategories Registry biomeRegistry = server.registryAccess().lookupOrThrow(Registries.BIOME); biomeRegistry.getTags().forEach(tag -> { @@ -929,6 +947,17 @@ public void initializeRegistries() { }); } + @Override + public boolean generateTree(TreeType treeType, World world, EditSession session, BlockVector3 pt) throws MaxChangedBlocksException { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + PlacedFeature feature = originalWorld.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE).getValue(ResourceLocation.tryParse(treeType.id())); + ServerChunkCache chunkManager = originalWorld.getChunkSource(); + try (PaperweightServerLevelDelegateProxy.LevelAndProxy proxyLevel = + PaperweightServerLevelDelegateProxy.newInstance(session, originalWorld, this)) { + return feature != null && feature.place(proxyLevel.level(), chunkManager.getGenerator(), random, new BlockPos(pt.x(), pt.y(), pt.z())); + } + } + public boolean generateFeature(ConfiguredFeatureType type, World world, EditSession session, BlockVector3 pt) { ServerLevel originalWorld = ((CraftWorld) world).getHandle(); ConfiguredFeature feature = originalWorld.registryAccess().lookupOrThrow(Registries.CONFIGURED_FEATURE).getValue(ResourceLocation.tryParse(type.id())); diff --git a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweAdapter.java index 2bde85f600..e39b875476 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_21_9/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_9/PaperweightFaweAdapter.java @@ -19,6 +19,7 @@ import com.mojang.serialization.Codec; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -51,6 +52,7 @@ 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.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import io.papermc.lib.PaperLib; @@ -91,6 +93,7 @@ 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.placement.PlacedFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; @@ -675,6 +678,44 @@ public boolean generateStructure(StructureType type, World world, EditSession ed return placeFeatureIntoSession(editSession, populator, placed); } + @Override + public boolean generateTree( + final TreeType treeType, + final World world, + final EditSession session, + final BlockVector3 pt + ) throws MaxChangedBlocksException { + ServerLevel serverLevel = getServerLevel(world); + ChunkGenerator generator = serverLevel.getMinecraftWorld().getChunkSource().getGenerator(); + + PlacedFeature placedFeature = serverLevel + .registryAccess() + .lookupOrThrow(Registries.PLACED_FEATURE) + .getValue(ResourceLocation.tryParse(treeType.id())); + + FaweBlockStateListPopulator populator = new FaweBlockStateListPopulator(serverLevel); + List placed = TaskManager.taskManager().sync(() -> { + preCaptureStates(serverLevel); + try { + if (!placedFeature.place( + populator, + generator, + serverLevel.random, + new BlockPos(pt.x(), pt.y(), pt.z()) + )) { + return null; + } + List placedBlocks = new ArrayList<>(populator.getSnapshotBlocks()); + placedBlocks.addAll(serverLevel.capturedBlockStates.values()); + return placedBlocks; + } finally { + postCaptureBlockStates(serverLevel); + } + }); + + return placeFeatureIntoSession(session, populator, placed); + } + private boolean placeFeatureIntoSession( final EditSession editSession, final FaweBlockStateListPopulator populator, diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java index b902696d2f..66a229fdb0 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java @@ -13,7 +13,6 @@ import org.bukkit.World; import org.bukkit.block.BlockState; -import java.util.Collection; import java.util.List; import java.util.Map; @@ -35,6 +34,11 @@ protected FaweAdapter(final BukkitImplAdapter parent) { this.parent = parent; } + @Override + public void initializeRegistries() { + parent.initializeRegistries(); + } + @Override public boolean generateTree( final TreeGenerator.TreeType treeType, diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 99aaefdd12..40121aa912 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -31,6 +31,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; @@ -315,10 +316,16 @@ public boolean clearContainerBlockContents(BlockVector3 pt) { /** * An EnumMap that stores which WorldEdit TreeTypes apply to which Bukkit TreeTypes. */ + @Deprecated private static final EnumMap treeTypeMapping = new EnumMap<>(TreeGenerator.TreeType.class); static { + generateTreeMap(); + } + + @SuppressWarnings("deprecation") + private static void generateTreeMap() { for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) { try { TreeType bukkitType = TreeType.valueOf(type.name()); @@ -348,10 +355,13 @@ public boolean clearContainerBlockContents(BlockVector3 pt) { } } + @Deprecated public static TreeType toBukkitTreeType(TreeGenerator.TreeType type) { return treeTypeMapping.get(type); } + @SuppressWarnings("deprecation") + @Deprecated @Override public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 pt) { //FAWE start - allow tree commands to be undone and obey region restrictions @@ -360,6 +370,20 @@ public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession //FAWE end } + @Override + public boolean generateTree( + final com.sk89q.worldedit.world.generation.TreeType type, + final EditSession editSession, + final BlockVector3 position + ) throws MaxChangedBlocksException { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + return adapter.generateTree(type, getWorld(), editSession, position); + } + // No adapter, we can't generate this. + return false; + } + @Override public void dropItem(Vector3 pt, BaseItemStack item) { World world = getWorld(); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index d0708887b0..14b5d019b4 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -259,7 +259,6 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new AsyncTabCompleteListener(), this); } - initializeRegistries(); // this creates the objects matching Bukkit's enums - but doesn't fill them with data yet if (Bukkit.getWorlds().isEmpty()) { setupPreWorldData(); // register this so we can load world-dependent data right as the first world is loading @@ -291,6 +290,7 @@ public void onEnable() { private void setupPreWorldData() { loadAdapter(); + initializeRegistries(); // this creates the objects matching Bukkit's enums - but doesn't fill them with data yet WorldEdit.getInstance().loadMappings(); } @@ -352,6 +352,13 @@ private void initializeRegistries() { EntityType.REGISTRY.register("minecraft:" + lowerCaseMcId, new EntityType("minecraft:" + lowerCaseMcId)); } } + + // Registries only available via NMS + BukkitImplAdapter adapter = getBukkitImplAdapter(); + if (adapter != null) { + adapter.initializeRegistries(); + } + // ... :| GameModes.get(""); WeatherTypes.get(""); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index f3db4cb9f3..2fe28534b6 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -31,6 +31,7 @@ import com.sk89q.jnbt.LinBusConverter; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -53,6 +54,7 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import org.bukkit.Keyed; @@ -330,6 +332,19 @@ default void sendBiomeUpdates(World world, Iterable chunks) { } + /** + * Generates a Minecraft tree at the given location. + * + * @param treeType The tree + * @param world The world + * @param session The EditSession + * @param pt The location + * @return If it succeeded + */ + default boolean generateTree(TreeType treeType, World world, EditSession session, BlockVector3 pt) throws MaxChangedBlocksException { + throw new UnsupportedOperationException("This adapter does not support generating features."); + } + /** * Generates a Minecraft feature at the given location. * diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java index 1502fdbf38..e838baee4a 100644 --- a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java +++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/schematic/ClipboardWorld.java @@ -41,13 +41,13 @@ import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.generation.TreeType; import javax.annotation.Nullable; import java.io.File; @@ -141,8 +141,7 @@ public boolean regenerate(Region region, Extent extent, RegenOptions options) { } @Override - public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) - throws MaxChangedBlocksException { + public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException { return false; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java index 8421df551c..bd443ff478 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java @@ -39,6 +39,7 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.weather.WeatherType; import javax.annotation.Nullable; @@ -290,6 +291,12 @@ public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession } } + @Override + public boolean generateTree(final TreeType type, final EditSession editSession, final BlockVector3 position) throws + MaxChangedBlocksException { + return parent.generateTree(type, editSession, position); + } + @Override public boolean generateStructure(final StructureType type, final EditSession editSession, final BlockVector3 position) { return parent.generateStructure(type, editSession, position); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 29c3e26032..330d2b6678 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -150,6 +150,7 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.registry.LegacyMapper; import org.apache.logging.log4j.Logger; @@ -3033,7 +3034,9 @@ public int makePumpkinPatches(BlockVector3 position, int apothem, double density * @param treeType the tree type * @return number of trees created * @throws MaxChangedBlocksException thrown if too many blocks are changed + * @deprecated Use {@link #makeForest(Region, double, TreeType)}. */ + @Deprecated public int makeForest(BlockVector3 basePosition, int size, double density, TreeGenerator.TreeType treeType) throws MaxChangedBlocksException { return makeForest(CuboidRegion.fromCenter(basePosition, size), density, treeType); @@ -3047,7 +3050,9 @@ public int makeForest(BlockVector3 basePosition, int size, double density, TreeG * @param treeType the tree type * @return number of trees created * @throws MaxChangedBlocksException thrown if too many blocks are changed + * @deprecated Use {@link #makeForest(Region, double, TreeType)}. */ + @Deprecated public int makeForest(Region region, double density, TreeGenerator.TreeType treeType) throws MaxChangedBlocksException { ForestGenerator generator = new ForestGenerator(this, treeType); GroundFunction ground = new GroundFunction(new ExistingBlockMask(this), generator); @@ -3059,6 +3064,38 @@ public int makeForest(Region region, double density, TreeGenerator.TreeType tree return ground.getAffected(); } + /** + * Makes a forest. + * + * @param basePosition a position + * @param size a size + * @param density between 0 and 1, inclusive + * @param treeType the tree type + * @return number of trees created + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + public int makeForest(BlockVector3 basePosition, int size, double density, TreeType treeType) throws MaxChangedBlocksException { + return makeForest(CuboidRegion.fromCenter(basePosition, size), density, treeType); + } + + /** + * Makes a forest. + * + * @param region the region to generate trees in + * @param density between 0 and 1, inclusive + * @param treeType the tree type + * @return number of trees created + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + public int makeForest(Region region, double density, TreeType treeType) throws MaxChangedBlocksException { + com.sk89q.worldedit.function.generator.TreeGenerator generator = new com.sk89q.worldedit.function.generator.TreeGenerator(this, treeType); + GroundFunction ground = new GroundFunction(new ExistingBlockMask(this), generator); + LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground); + visitor.setMask(new NoiseFilter2D(new RandomNoise(), density)); + Operations.completeLegacy(visitor); + return ground.getAffected(); + } + /** * Get the block distribution inside a region. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ApplyBrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ApplyBrushCommands.java index 66e801ae2d..ba5f095a84 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ApplyBrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ApplyBrushCommands.java @@ -40,8 +40,8 @@ import com.sk89q.worldedit.internal.command.CommandRegistrationHandler; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.regions.factory.RegionFactory; -import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.world.generation.TreeType; import org.enginehub.piston.CommandManager; import org.enginehub.piston.CommandManagerService; import org.enginehub.piston.CommandParameters; @@ -115,7 +115,7 @@ public void forest( CommandParameters parameters, Player player, LocalSession localSession, @Arg(desc = "The type of tree to plant") - TreeGenerator.TreeType type + TreeType type ) throws WorldEditException { setApplyBrush(parameters, player, localSession, new TreeGeneratorFactory(type)); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index e1a2e3e401..1042932e9b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -117,7 +117,6 @@ import com.sk89q.worldedit.regions.factory.SphereRegionFactory; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.HandSide; -import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; @@ -128,6 +127,7 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import org.anarres.parallelgzip.ParallelGZIPOutputStream; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; @@ -1189,7 +1189,7 @@ public void forest( @Arg(desc = "The density of the brush", def = "20") double density, @Arg(desc = "The type of tree to use") - TreeGenerator.TreeType type + TreeType type ) throws WorldEditException { setOperationBasedBrush(player, localSession, radius, new Paint(new TreeGeneratorFactory(type), density / 100), shape, "worldedit.brush.forest" diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index 3206524baf..d949859837 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -53,12 +53,12 @@ import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/PaintBrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/PaintBrushCommands.java index c34c66055a..f2f276cde0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/PaintBrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/PaintBrushCommands.java @@ -39,9 +39,9 @@ import com.sk89q.worldedit.internal.annotation.Direction; import com.sk89q.worldedit.internal.command.CommandRegistrationHandler; import com.sk89q.worldedit.regions.factory.RegionFactory; -import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.world.generation.TreeType; import org.enginehub.piston.CommandManager; import org.enginehub.piston.CommandManagerService; import org.enginehub.piston.CommandParameters; @@ -130,7 +130,7 @@ public void forest( CommandParameters parameters, Player player, LocalSession localSession, @Arg(desc = "The type of tree to plant") - TreeGenerator.TreeType type + TreeType type ) throws WorldEditException { setPaintBrush(parameters, player, localSession, new TreeGeneratorFactory(type)); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 197968bb36..57432cae58 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -68,13 +68,13 @@ import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.regions.Regions; import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.world.RegenOptions; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.generation.TreeType; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java index 1e63b947e5..83e712e3e1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java @@ -51,7 +51,6 @@ import com.sk89q.worldedit.internal.command.CommandRegistrationHandler; import com.sk89q.worldedit.internal.command.CommandUtil; import com.sk89q.worldedit.util.HandSide; -import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; @@ -59,6 +58,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import org.enginehub.piston.CommandManager; import org.enginehub.piston.CommandManagerService; import org.enginehub.piston.CommandMetadata; @@ -242,7 +242,7 @@ public void inspectBrush(Player player, LocalSession session) throws WorldEditEx public void tree( Player player, LocalSession session, @Arg(desc = "Type of tree to generate", def = "tree") - TreeGenerator.TreeType type + TreeType type ) throws WorldEditException { setTool(player, session, new TreePlanter(type), "worldedit.tool.tree.equip"); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java index c9b69cc30c..2c4107a386 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java @@ -34,6 +34,7 @@ import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.item.ItemCategory; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.weather.WeatherType; @@ -66,7 +67,8 @@ public static void register(CommandManager commandManager) { GameMode.class, WeatherType.class, ConfiguredFeatureType.class, - StructureType.class + StructureType.class, + TreeType.class ) .stream() .map(c -> (Class) c) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/TreeGeneratorFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/TreeGeneratorFactory.java index 1e6b06d7bc..421b5c675e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/TreeGeneratorFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/TreeGeneratorFactory.java @@ -22,20 +22,20 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.function.Contextual; import com.sk89q.worldedit.function.EditContext; -import com.sk89q.worldedit.function.generator.ForestGenerator; -import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.function.generator.TreeGenerator; +import com.sk89q.worldedit.world.generation.TreeType; -public final class TreeGeneratorFactory implements Contextual { +public final class TreeGeneratorFactory implements Contextual { - private final TreeGenerator.TreeType type; + private final TreeType type; - public TreeGeneratorFactory(TreeGenerator.TreeType type) { + public TreeGeneratorFactory(TreeType type) { this.type = type; } @Override - public ForestGenerator createFromContext(EditContext input) { - return new ForestGenerator((EditSession) input.getDestination(), type); + public TreeGenerator createFromContext(EditContext input) { + return new TreeGenerator((EditSession) input.getDestination(), type); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java index db92f6af91..e5740dcb5a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java @@ -30,7 +30,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.world.generation.TreeType; import javax.annotation.Nullable; @@ -39,9 +39,9 @@ */ public class TreePlanter implements BlockTool { - private final TreeGenerator.TreeType treeType; + private final TreeType treeType; - public TreePlanter(TreeGenerator.TreeType treeType) { + public TreePlanter(TreeType treeType) { this.treeType = treeType; } @@ -66,7 +66,7 @@ public boolean actPrimary( final BlockVector3 pos = clicked.toVector().add(0, 1, 0).toBlockPoint(); for (int i = 0; i < 10; i++) { - if (treeType.generate(editSession, pos)) { + if (player.getWorld().generateTree(treeType, editSession, pos)) { successful = true; break; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/ForestGenerator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/ForestGenerator.java index a042fd16c1..72b763cd18 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/ForestGenerator.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/ForestGenerator.java @@ -31,7 +31,10 @@ /** * Generates forests by searching for the ground starting from the given upper Y * coordinate for every column given. + * + * @deprecated Use {@link com.sk89q.worldedit.function.generator.TreeGenerator} instead. */ +@Deprecated public class ForestGenerator implements RegionFunction { private final TreeGenerator.TreeType treeType; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/TreeGenerator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/TreeGenerator.java new file mode 100644 index 0000000000..35809a9187 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/TreeGenerator.java @@ -0,0 +1,48 @@ +/* + * 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.function.generator; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.generation.TreeType; + +public final class TreeGenerator implements RegionFunction { + + private final TreeType treeType; + private final EditSession editSession; + + /** + * Create a new instance. + * + * @param editSession the edit session + * @param treeType the tree type + */ + public TreeGenerator(EditSession editSession, TreeType treeType) { + this.editSession = editSession; + this.treeType = treeType; + } + + @Override + public boolean apply(BlockVector3 position) throws WorldEditException { + return editSession.getWorld().generateTree(treeType, editSession, position); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/TreeGenerator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/TreeGenerator.java index 2b6bae6b73..68ce6f4c1e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/TreeGenerator.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/TreeGenerator.java @@ -41,8 +41,10 @@ /** * Tree generator. */ +@Deprecated public final class TreeGenerator { + @Deprecated public enum TreeType { TREE("Oak tree", "oak", "tree", "regular"), BIG_TREE("Large oak tree", "largeoak", "bigoak", "big", "bigtree"), diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java index d3ad7bfd92..3832f50a29 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java @@ -38,7 +38,6 @@ import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BaseBlock; @@ -143,7 +142,7 @@ public boolean regenerate(Region region, EditSession editSession) { } @Override - public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException { + public boolean generateTree(com.sk89q.worldedit.world.generation.TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException { return false; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java index 8c9efb5bda..2430e705f8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java @@ -48,6 +48,7 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.generation.StructureType; +import com.sk89q.worldedit.world.generation.TreeType; import com.sk89q.worldedit.world.weather.WeatherType; import javax.annotation.Nullable; @@ -309,9 +310,24 @@ default boolean regenerate(Region region, Extent extent, RegenOptions options) { * @param position the position * @return true if generation was successful * @throws MaxChangedBlocksException thrown if too many blocks were changed + * @deprecated Use {@link #generateTree(TreeType, EditSession, BlockVector3)} instead */ - boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws - MaxChangedBlocksException; + @Deprecated + default boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws + MaxChangedBlocksException { + return false; + } + + /** + * Generate a tree at the given position. + * + * @param type the tree type + * @param editSession the {@link EditSession} + * @param position the position + * @return true if generation was successful + * @throws MaxChangedBlocksException thrown if too many blocks were changed + */ + boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException; /** * Generate a structure at the given position diff --git a/worldedit-bukkit/src/test/java/com/sk89q/worldedit/bukkit/BukkitWorldTest.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/TreeType.java similarity index 65% rename from worldedit-bukkit/src/test/java/com/sk89q/worldedit/bukkit/BukkitWorldTest.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/TreeType.java index df6b33b7b7..27dfd646c6 100644 --- a/worldedit-bukkit/src/test/java/com/sk89q/worldedit/bukkit/BukkitWorldTest.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/TreeType.java @@ -17,19 +17,17 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.bukkit; +package com.sk89q.worldedit.world.generation; -import com.sk89q.worldedit.util.TreeGenerator; -import org.junit.jupiter.api.Test; +import com.sk89q.worldedit.registry.Keyed; +import com.sk89q.worldedit.registry.NamespacedRegistry; -import static org.junit.jupiter.api.Assertions.assertNotNull; +public record TreeType(String id) implements Keyed { -public class BukkitWorldTest { + public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("tree_type", "minecraft"); - public void testTreeTypeMapping() { - for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) { - assertNotNull(BukkitWorld.toBukkitTreeType(type), "No mapping for: " + type); - } + @Override + public String toString() { + return this.id; } - }