From e5e73c4723169bfe41747ff678c59dafabb80c72 Mon Sep 17 00:00:00 2001 From: Madeline Miller Date: Sun, 9 Apr 2023 18:18:23 +1000 Subject: [PATCH] Add structure generator command (cherry picked from commit 5ca80395a35100c2d7686ad8839baaa57f8db07c) --- .../ext.fawe/v1_20_R3/PaperweightAdapter.java | 35 +++++++++++++++ .../sk89q/worldedit/bukkit/BukkitWorld.java | 11 +++++ .../bukkit/adapter/BukkitImplAdapter.java | 14 ++++++ .../worldedit/command/GenerationCommands.java | 18 ++++++++ .../command/argument/RegistryConverter.java | 4 +- .../java/com/sk89q/worldedit/world/World.java | 5 +++ .../world/generation/StructureType.java | 43 +++++++++++++++++++ .../src/main/resources/lang/strings.json | 2 + 8 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/StructureType.java 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 802144198..f5b0b2307 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 @@ -67,12 +67,14 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; 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.item.ItemType; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; import net.minecraft.core.registries.Registries; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; @@ -111,6 +113,9 @@ import net.minecraft.world.level.chunk.LevelChunk; 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.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.StructureStart; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.phys.BlockHitResult; @@ -894,12 +899,19 @@ public final class PaperweightAdapter implements BukkitImplAdapter biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME); @@ -939,6 +951,29 @@ public final class PaperweightAdapter implements BukkitImplAdapter true); + + if (!structureStart.isValid()) { + return false; + } else { + BoundingBox boundingBox = structureStart.getBoundingBox(); + ChunkPos min = new ChunkPos(SectionPos.blockToSectionCoord(boundingBox.minX()), SectionPos.blockToSectionCoord(boundingBox.minZ())); + ChunkPos max = new ChunkPos(SectionPos.blockToSectionCoord(boundingBox.maxX()), SectionPos.blockToSectionCoord(boundingBox.maxZ())); + ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk(proxyLevel, originalWorld.structureManager(), chunkManager.getGenerator(), originalWorld.getRandom(), new BoundingBox(chunkPosx.getMinBlockX(), originalWorld.getMinBuildHeight(), chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), originalWorld.getMaxBuildHeight(), chunkPosx.getMaxBlockZ()), chunkPosx)); + return true; + } + } + // ------------------------------------------------------------------------ // Code that is less likely to break // ------------------------------------------------------------------------ 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 ea2dac1c9..ce02dd368 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 @@ -55,6 +55,7 @@ import com.sk89q.worldedit.world.block.BaseBlock; 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.generation.StructureType; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.weather.WeatherTypes; import io.papermc.lib.PaperLib; @@ -530,6 +531,16 @@ public class BukkitWorld extends AbstractWorld { return false; } + @Override + public boolean generateStructure(StructureType type, EditSession editSession, BlockVector3 position) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + if (adapter != null) { + return adapter.generateStructure(type, getWorld(), editSession, position); + } + // No adapter, we can't generate this. + return false; + } + private static volatile boolean hasWarnedImplError = false; @Override 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 60cdbc60f..7726563bc 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 @@ -50,6 +50,7 @@ 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.generation.ConfiguredFeatureType; +import com.sk89q.worldedit.world.generation.StructureType; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.BlockMaterial; import org.bukkit.Keyed; @@ -337,6 +338,19 @@ public interface BukkitImplAdapter extends IBukkitAdapter { throw new UnsupportedOperationException("This adapter does not support generating features."); } + /** + * Generates a Minecraft structure at the given location. + * + * @param feature The feature + * @param world The world + * @param session The EditSession + * @param pt The location + * @return If it succeeded + */ + default boolean generateStructure(StructureType feature, World world, EditSession session, BlockVector3 pt) { + throw new UnsupportedOperationException("This adapter does not support generating features."); + } + //FAWE start default BlockMaterial getMaterial(BlockType blockType) { return getMaterial(blockType.getDefaultState()); 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 c08314218..599e9da83 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 @@ -58,6 +58,7 @@ 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 org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; @@ -377,6 +378,23 @@ public class GenerationCommands { return 0; } + @Command( + name = "/structure", + desc = "Generate Minecraft structures" + ) + @CommandPermissions("worldedit.generation.structure") + @Logging(POSITION) + public int structure(Actor actor, LocalSession session, EditSession editSession, + @Arg(desc = "The structure") + StructureType feature) throws WorldEditException { + if (editSession.getWorld().generateStructure(feature, editSession, session.getPlacementPosition(actor))) { + actor.printInfo(Caption.of("worldedit.structure.created")); + } else { + actor.printError(Caption.of("worldedit.structure.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 9ed38c642..c9b69cc30 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 @@ -33,6 +33,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.generation.StructureType; import com.sk89q.worldedit.world.item.ItemCategory; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.weather.WeatherType; @@ -64,7 +65,8 @@ public final class RegistryConverter implements ArgumentConvert FluidCategory.class, GameMode.class, WeatherType.class, - ConfiguredFeatureType.class + ConfiguredFeatureType.class, + StructureType.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 6a19abc54..9476ecaae 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 @@ -47,6 +47,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.generation.StructureType; import com.sk89q.worldedit.world.weather.WeatherType; import javax.annotation.Nullable; @@ -312,6 +313,10 @@ public interface World extends Extent, Keyed, IChunkCache { boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException; + default boolean generateStructure(StructureType type, EditSession editSession, BlockVector3 position) { + return false; + } + /** * Generate a feature at the given position. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/StructureType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/StructureType.java new file mode 100644 index 000000000..e4e8aaebb --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/StructureType.java @@ -0,0 +1,43 @@ +/* + * 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 StructureType implements Keyed { + public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("structure type"); + + private final String id; + + public StructureType(String id) { + this.id = id; + } + + @Override + public String getId() { + return this.id; + } + + @Override + public String toString() { + return this.id; + } +} diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 2e2d92435..a42c9863a 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -519,6 +519,8 @@ "worldedit.pyramid.created": "{0} blocks have been created.", "worldedit.generate.created": "{0} blocks have been created.", "worldedit.generatebiome.changed": "{0} biomes affected.", + "worldedit.structure.created": "Structure created.", + "worldedit.structure.failed": "Failed to generate structure. Is it a valid spot for it?", "worldedit.reload.config": "Configuration reloaded!", "worldedit.report.written": "FAWE report written to {0}", "worldedit.report.error": "Failed to write report: {0}",