diff --git a/verification/src/changes/accepted-core-public-api-changes.json b/verification/src/changes/accepted-core-public-api-changes.json index 2d0bde7e9c..8400941833 100644 --- a/verification/src/changes/accepted-core-public-api-changes.json +++ b/verification/src/changes/accepted-core-public-api-changes.json @@ -122,6 +122,13 @@ "changes": [ "METHOD_NEW_DEFAULT" ] + }, + { + "type": "com.sk89q.worldedit.world.World", + "member": "Method com.sk89q.worldedit.world.World.generateFeature(com.sk89q.worldedit.world.generation.ConfiguredFeatureType,com.sk89q.worldedit.EditSession,com.sk89q.worldedit.math.BlockVector3)", + "changes": [ + "METHOD_NEW_DEFAULT" + ] } ], "LazyReference was never publicly extensible": [ 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 c1f769d859..491942fb00 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 @@ -39,6 +39,7 @@ import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; @@ -235,6 +236,23 @@ public int pumpkins(Actor actor, LocalSession session, EditSession editSession, return affected; } + @Command( + name = "/feature", + desc = "Generate Minecraft features" + ) + @CommandPermissions("worldedit.generation.feature") + @Logging(POSITION) + public int feature(Actor actor, LocalSession session, EditSession editSession, + @Arg(desc = "The feature") + ConfiguredFeatureType feature) throws WorldEditException { + if (editSession.getWorld().generateFeature(feature, editSession, session.getPlacementPosition(actor))) { + actor.printInfo(TranslatableComponent.of("worldedit.feature.created")); + } else { + actor.printError(TranslatableComponent.of("worldedit.feature.failed")); + } + return 0; + } + @Command( name = "/hpyramid", desc = "Generate a hollow pyramid" 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 ad07203a66..38edcec64a 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 @@ -32,6 +32,7 @@ import com.sk89q.worldedit.world.fluid.FluidCategory; import com.sk89q.worldedit.world.fluid.FluidType; import com.sk89q.worldedit.world.gamemode.GameMode; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemCategory; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.weather.WeatherType; @@ -62,7 +63,8 @@ public static void register(CommandManager commandManager) { FluidType.class, FluidCategory.class, GameMode.class, - WeatherType.class + WeatherType.class, + ConfiguredFeatureType.class ) .stream() .map(c -> (Class) c) 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 276b206ff9..bfbb3f7fdd 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 @@ -41,6 +41,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.weather.WeatherType; import java.nio.file.Path; @@ -273,6 +274,18 @@ default boolean regenerate(Region region, Extent extent, RegenOptions options) { */ boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException; + /** + * Generate a feature at the given position. + * + * @param type The feature type + * @param editSession The {@link EditSession} + * @param position The position + * @return True if the generation was successful + */ + default boolean generateFeature(ConfiguredFeatureType type, EditSession editSession, BlockVector3 position) { + return false; + } + /** * Load the chunk at the given position if it isn't loaded. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java new file mode 100644 index 0000000000..67ddea478e --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java @@ -0,0 +1,38 @@ +/* + * 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.world.generation; + +import com.sk89q.worldedit.registry.Keyed; +import com.sk89q.worldedit.registry.NamespacedRegistry; + +public class ConfiguredFeatureType implements Keyed { + public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("configured feature type"); + + private final String id; + + public ConfiguredFeatureType(String id) { + this.id = id; + } + + @Override + public String getId() { + return this.id; + } +} diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 84921ef860..2d328da7fc 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -245,6 +245,8 @@ "worldedit.sphere.created": "{0} blocks have been created.", "worldedit.forestgen.created": "{0} trees created.", "worldedit.pumpkins.created": "{0} pumpkin patches created.", + "worldedit.feature.created": "Feature created.", + "worldedit.feature.failed": "Failed to generate feature. Is it a valid spot for it?", "worldedit.pyramid.created": "{0} blocks have been created.", "worldedit.generate.created": "{0} blocks have been created.", "worldedit.generatebiome.changed": "{0} biomes affected.", diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java index a4bfa0f365..f63fd6b72a 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java @@ -31,6 +31,7 @@ import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; @@ -47,6 +48,7 @@ import net.minecraft.world.item.ItemStack; 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.DirectionProperty; import net.minecraft.world.phys.Vec3; @@ -196,6 +198,15 @@ public static BlockState adapt(net.minecraft.world.level.block.state.BlockState return worldEdit; } + public static BaseBlock adapt(BlockEntity blockEntity) { + int blockStateId = Block.getId(blockEntity.getBlockState()); + BlockState worldEdit = BlockStateIdAccess.getBlockStateById(blockStateId); + if (worldEdit == null) { + worldEdit = FabricTransmogrifier.transmogToWorldEdit(blockEntity.getBlockState()); + } + return worldEdit.toBaseBlock(LazyReference.from(() -> NBTConverter.fromNative(blockEntity.saveWithId()))); + } + public static Block adapt(BlockType blockType) { return FabricWorldEdit.getRegistry(Registries.BLOCK).get(new ResourceLocation(blockType.getId())); } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java index fa811ef8e9..ee17530411 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java @@ -34,6 +34,7 @@ import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.fabric.internal.ExtendedMinecraftServer; +import com.sk89q.worldedit.fabric.internal.FabricEditSessionDelegate; import com.sk89q.worldedit.fabric.internal.FabricWorldNativeAccess; import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.fabric.mixin.AccessorDerivedLevelData; @@ -60,6 +61,7 @@ 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.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.weather.WeatherTypes; @@ -463,11 +465,18 @@ public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position = position.add(0, 1, 0); } return generator != null && generator.place( - world, chunkManager.getGenerator(), random, + new FabricEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, FabricAdapter.toBlockPos(position) ); } + public boolean generateFeature(ConfiguredFeatureType type, EditSession editSession, BlockVector3 position) { + ServerLevel world = (ServerLevel) getWorld(); + ConfiguredFeature k = world.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).get(ResourceLocation.tryParse(type.getId())); + ServerChunkCache chunkManager = world.getChunkSource(); + return k != null && k.place(new FabricEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, FabricAdapter.toBlockPos(position)); + } + @Override public void checkLoadedChunk(BlockVector3 pt) { getWorld().getChunk(FabricAdapter.toBlockPos(pt)); diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java index a4045c0cfb..5747cb4865 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java @@ -40,6 +40,7 @@ import com.sk89q.worldedit.world.block.BlockCategory; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemCategory; import com.sk89q.worldedit.world.item.ItemType; import net.fabricmc.api.ModInitializer; @@ -228,6 +229,12 @@ private void setupRegistries(MinecraftServer server) { ItemCategory.REGISTRY.register(name.toString(), new ItemCategory(name.toString())); } }); + // Features + for (ResourceLocation name: server.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).keySet()) { + if (ConfiguredFeatureType.REGISTRY.get(name.toString()) == null) { + ConfiguredFeatureType.REGISTRY.register(name.toString(), new ConfiguredFeatureType(name.toString())); + } + } } private void onStartingServer(MinecraftServer minecraftServer) { diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java new file mode 100644 index 0000000000..ae689c69fa --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java @@ -0,0 +1,286 @@ +/* + * 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.fabric.internal; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.fabric.internal.NBTConverter; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.fluid.FluidType; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.RandomSource; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +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.ChunkStatus; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.ticks.LevelTickAccess; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +public class FabricEditSessionDelegate implements WorldGenLevel { + + private final EditSession editSession; + private final ServerLevel level; + + public FabricEditSessionDelegate(EditSession editSession, ServerLevel level) { + this.editSession = editSession; + this.level = level; + } + + @Override + public long getSeed() { + return this.level.getSeed(); + } + + @Override + public ServerLevel getLevel() { + return this.level; + } + + @Override + public long nextSubTickCount() { + return this.level.nextSubTickCount(); + } + + @Override + public LevelTickAccess getBlockTicks() { + return this.level.getBlockTicks(); + } + + @Override + public LevelTickAccess getFluidTicks() { + return this.level.getFluidTicks(); + } + + @Override + public LevelData getLevelData() { + return this.level.getLevelData(); + } + + @Override + public DifficultyInstance getCurrentDifficultyAt(BlockPos blockPos) { + return this.level.getCurrentDifficultyAt(blockPos); + } + + @Nullable + @Override + public MinecraftServer getServer() { + return this.level.getServer(); + } + + @Override + public ChunkSource getChunkSource() { + return this.level.getChunkSource(); + } + + @Override + public RandomSource getRandom() { + // TODO investigate overriding this for supplying seeds + return this.level.getRandom(); + } + + @Override + public void playSound(@Nullable Player player, BlockPos blockPos, SoundEvent soundEvent, SoundSource soundSource, float f, float g) { + this.level.playSound(player, blockPos, soundEvent, soundSource, f, g); + } + + @Override + public void addParticle(ParticleOptions particleOptions, double d, double e, double f, double g, double h, double i) { + this.level.addParticle(particleOptions, d, e, f, g, h, i); + } + + @Override + public void levelEvent(@Nullable Player player, int i, BlockPos blockPos, int j) { + this.level.levelEvent(player, i, blockPos, j); + } + + @Override + public void gameEvent(GameEvent gameEvent, Vec3 vec3, GameEvent.Context context) { + this.level.gameEvent(gameEvent, vec3, context); + } + + @Override + public float getShade(Direction direction, boolean bl) { + return this.level.getShade(direction, bl); + } + + @Override + public LevelLightEngine getLightEngine() { + return this.level.getLightEngine(); + } + + @Override + public WorldBorder getWorldBorder() { + return this.level.getWorldBorder(); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos blockPos) { + BlockEntity tileEntity = this.level.getChunkAt(blockPos).getBlockEntity(blockPos); + if (tileEntity == null) { + return null; + } + BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos)); + newEntity.load(NBTConverter.toNative(this.editSession.getFullBlock(FabricAdapter.adapt(blockPos)).getNbtReference().getValue())); + + return newEntity; + } + + @Override + public BlockState getBlockState(BlockPos blockPos) { + return FabricAdapter.adapt(this.editSession.getBlock(FabricAdapter.adapt(blockPos))); + } + + @Override + public FluidState getFluidState(BlockPos blockPos) { + return this.level.getFluidState(blockPos); + } + + @Override + public List getEntities(@Nullable Entity entity, AABB aabb, Predicate predicate) { + return this.level.getEntities(entity, aabb, predicate); + } + + @Override + public List getEntities(EntityTypeTest entityTypeTest, AABB aabb, Predicate predicate) { + return this.level.getEntities(entityTypeTest, aabb, predicate); + } + + @Override + public List players() { + return this.level.players(); + } + + @Nullable + @Override + public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl) { + return this.level.getChunk(i, j, chunkStatus, bl); + } + + @Override + public int getHeight(Heightmap.Types types, int i, int j) { + return this.level.getHeight(types, i, j); + } + + @Override + public int getSkyDarken() { + return this.level.getSkyDarken(); + } + + @Override + public BiomeManager getBiomeManager() { + return this.level.getBiomeManager(); + } + + @Override + public Holder getUncachedNoiseBiome(int i, int j, int k) { + return this.level.getUncachedNoiseBiome(i, j, k); + } + + @Override + public boolean isClientSide() { + return false; + } + + @Override + public int getSeaLevel() { + return this.level.getSeaLevel(); + } + + @Override + public DimensionType dimensionType() { + return this.level.dimensionType(); + } + + @Override + public RegistryAccess registryAccess() { + return this.level.registryAccess(); + } + + @Override + public FeatureFlagSet enabledFeatures() { + return this.level.enabledFeatures(); + } + + @Override + public boolean isStateAtPosition(BlockPos blockPos, Predicate predicate) { + return predicate.test(this.getBlockState(blockPos)); + } + + @Override + public boolean isFluidAtPosition(BlockPos blockPos, Predicate predicate) { + return predicate.test(this.getFluidState(blockPos)); + } + + @Override + public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j) { + try { + return editSession.setBlock(FabricAdapter.adapt(blockPos), FabricAdapter.adapt(blockState)); + } catch (MaxChangedBlocksException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean removeBlock(BlockPos blockPos, boolean bl) { + try { + return editSession.setBlock(FabricAdapter.adapt(blockPos), BlockTypes.AIR.getDefaultState()); + } catch (MaxChangedBlocksException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { + return removeBlock(blockPos, bl); + } +} diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java index 4d6049ba3a..0912a0af2f 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java @@ -33,6 +33,7 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.forge.internal.ForgeEditSessionDelegate; import com.sk89q.worldedit.forge.internal.ForgeWorldNativeAccess; import com.sk89q.worldedit.forge.internal.NBTConverter; import com.sk89q.worldedit.forge.internal.TileEntityUtils; @@ -57,6 +58,7 @@ 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.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.weather.WeatherTypes; @@ -448,10 +450,17 @@ public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position = position.add(0, 1, 0); } return generator != null && generator.place( - world, chunkManager.getGenerator(), random, ForgeAdapter.toBlockPos(position) + new ForgeEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, ForgeAdapter.toBlockPos(position) ); } + public boolean generateFeature(ConfiguredFeatureType type, EditSession editSession, BlockVector3 position) { + ServerLevel world = getWorld(); + ConfiguredFeature k = world.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).get(ResourceLocation.tryParse(type.getId())); + ServerChunkCache chunkManager = world.getChunkSource(); + return k != null && k.place(new ForgeEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, ForgeAdapter.toBlockPos(position)); + } + @Override public void checkLoadedChunk(BlockVector3 pt) { getWorld().getChunk(ForgeAdapter.toBlockPos(pt)); diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 9d5eb57a81..ac27beb5da 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -42,6 +42,7 @@ import com.sk89q.worldedit.world.block.BlockCategory; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemCategory; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.commands.CommandSourceStack; @@ -208,6 +209,12 @@ private void setupRegistries(MinecraftServer server) { ItemCategory.REGISTRY.register(name.toString(), new ItemCategory(name.toString())); } }); + // Features + for (ResourceLocation name: server.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).keySet()) { + if (ConfiguredFeatureType.REGISTRY.get(name.toString()) == null) { + ConfiguredFeatureType.REGISTRY.register(name.toString(), new ConfiguredFeatureType(name.toString())); + } + } } @SubscribeEvent diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java new file mode 100644 index 0000000000..5a3a9405ac --- /dev/null +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java @@ -0,0 +1,284 @@ +/* + * 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.forge.internal; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.forge.ForgeAdapter; +import com.sk89q.worldedit.world.block.BlockTypes; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.RandomSource; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +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.ChunkStatus; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.ticks.LevelTickAccess; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +public class ForgeEditSessionDelegate implements WorldGenLevel { + + private final EditSession editSession; + private final ServerLevel level; + + public ForgeEditSessionDelegate(EditSession editSession, ServerLevel level) { + this.editSession = editSession; + this.level = level; + } + + @Override + public long getSeed() { + return this.level.getSeed(); + } + + @Override + public ServerLevel getLevel() { + return this.level; + } + + @Override + public long nextSubTickCount() { + return this.level.nextSubTickCount(); + } + + @Override + public LevelTickAccess getBlockTicks() { + return this.level.getBlockTicks(); + } + + @Override + public LevelTickAccess getFluidTicks() { + return this.level.getFluidTicks(); + } + + @Override + public LevelData getLevelData() { + return this.level.getLevelData(); + } + + @Override + public DifficultyInstance getCurrentDifficultyAt(BlockPos blockPos) { + return this.level.getCurrentDifficultyAt(blockPos); + } + + @Nullable + @Override + public MinecraftServer getServer() { + return this.level.getServer(); + } + + @Override + public ChunkSource getChunkSource() { + return this.level.getChunkSource(); + } + + @Override + public RandomSource getRandom() { + // TODO investigate overriding this for supplying seeds + return this.level.getRandom(); + } + + @Override + public void playSound(@Nullable Player player, BlockPos blockPos, SoundEvent soundEvent, SoundSource soundSource, float f, float g) { + this.level.playSound(player, blockPos, soundEvent, soundSource, f, g); + } + + @Override + public void addParticle(ParticleOptions particleOptions, double d, double e, double f, double g, double h, double i) { + this.level.addParticle(particleOptions, d, e, f, g, h, i); + } + + @Override + public void levelEvent(@Nullable Player player, int i, BlockPos blockPos, int j) { + this.level.levelEvent(player, i, blockPos, j); + } + + @Override + public void gameEvent(GameEvent gameEvent, Vec3 vec3, GameEvent.Context context) { + this.level.gameEvent(gameEvent, vec3, context); + } + + @Override + public float getShade(Direction direction, boolean bl) { + return this.level.getShade(direction, bl); + } + + @Override + public LevelLightEngine getLightEngine() { + return this.level.getLightEngine(); + } + + @Override + public WorldBorder getWorldBorder() { + return this.level.getWorldBorder(); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos blockPos) { + BlockEntity tileEntity = this.level.getChunkAt(blockPos).getBlockEntity(blockPos); + if (tileEntity == null) { + return null; + } + BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos)); + newEntity.load(NBTConverter.toNative(this.editSession.getFullBlock(ForgeAdapter.adapt(blockPos)).getNbtReference().getValue())); + + return newEntity; + } + + @Override + public BlockState getBlockState(BlockPos blockPos) { + return ForgeAdapter.adapt(this.editSession.getBlock(ForgeAdapter.adapt(blockPos))); + } + + @Override + public FluidState getFluidState(BlockPos blockPos) { + return this.level.getFluidState(blockPos); + } + + @Override + public List getEntities(@Nullable Entity entity, AABB aabb, Predicate predicate) { + return this.level.getEntities(entity, aabb, predicate); + } + + @Override + public List getEntities(EntityTypeTest entityTypeTest, AABB aabb, Predicate predicate) { + return this.level.getEntities(entityTypeTest, aabb, predicate); + } + + @Override + public List players() { + return this.level.players(); + } + + @Nullable + @Override + public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl) { + return this.level.getChunk(i, j, chunkStatus, bl); + } + + @Override + public int getHeight(Heightmap.Types types, int i, int j) { + return this.level.getHeight(types, i, j); + } + + @Override + public int getSkyDarken() { + return this.level.getSkyDarken(); + } + + @Override + public BiomeManager getBiomeManager() { + return this.level.getBiomeManager(); + } + + @Override + public Holder getUncachedNoiseBiome(int i, int j, int k) { + return this.level.getUncachedNoiseBiome(i, j, k); + } + + @Override + public boolean isClientSide() { + return false; + } + + @Override + public int getSeaLevel() { + return this.level.getSeaLevel(); + } + + @Override + public DimensionType dimensionType() { + return this.level.dimensionType(); + } + + @Override + public RegistryAccess registryAccess() { + return this.level.registryAccess(); + } + + @Override + public FeatureFlagSet enabledFeatures() { + return this.level.enabledFeatures(); + } + + @Override + public boolean isStateAtPosition(BlockPos blockPos, Predicate predicate) { + return predicate.test(this.getBlockState(blockPos)); + } + + @Override + public boolean isFluidAtPosition(BlockPos blockPos, Predicate predicate) { + return predicate.test(this.getFluidState(blockPos)); + } + + @Override + public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j) { + try { + return editSession.setBlock(ForgeAdapter.adapt(blockPos), ForgeAdapter.adapt(blockState)); + } catch (MaxChangedBlocksException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean removeBlock(BlockPos blockPos, boolean bl) { + try { + return editSession.setBlock(ForgeAdapter.adapt(blockPos), BlockTypes.AIR.getDefaultState()); + } catch (MaxChangedBlocksException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { + return removeBlock(blockPos, bl); + } +}