Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow placement of minecraft gen features and structures #2364

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import com.google.common.util.concurrent.Futures;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Lifecycle;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack;
Expand Down Expand Up @@ -65,12 +65,15 @@
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;
Expand All @@ -95,6 +98,7 @@
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
Expand All @@ -107,6 +111,10 @@
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;
Expand Down Expand Up @@ -519,22 +527,6 @@ public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state
}
}

// This removes all unwanted tags from the main entity and all its passengers
private void removeUnwantedEntityTagsRecursively(net.minecraft.nbt.CompoundTag tag) {
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name);
}

// Adapted from net.minecraft.world.entity.EntityType#loadEntityRecursive
if (tag.contains("Passengers", NBTConstants.TYPE_LIST)) {
net.minecraft.nbt.ListTag nbttaglist = tag.getList("Passengers", NBTConstants.TYPE_COMPOUND);

for (int i = 0; i < nbttaglist.size(); ++i) {
removeUnwantedEntityTagsRecursively(nbttaglist.getCompound(i));
}
}
}

@Override
public Component getRichBlockName(BlockType blockType) {
return TranslatableComponent.of(getBlockFromType(blockType).getDescriptionId());
Expand Down Expand Up @@ -642,7 +634,7 @@ public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
= CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new));

@Override
public boolean simulateItemUse(org.bukkit.World world, BlockVector3 position, BaseItem item, Direction face) {
public boolean simulateItemUse(World world, BlockVector3 position, BaseItem item, Direction face) {
CraftWorld craftWorld = (CraftWorld) world;
ServerLevel worldServer = craftWorld.getHandle();
ItemStack stack = CraftItemStack.asNMSCopy(BukkitAdapter.adapt(item instanceof BaseItemStack
Expand Down Expand Up @@ -892,6 +884,20 @@ public void initializeRegistries() {
}
}

// 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()));
}
}

// Structures
for (ResourceLocation name : server.registryAccess().registryOrThrow(Registries.STRUCTURE).keySet()) {
if (StructureType.REGISTRY.get(name.toString()) == null) {
StructureType.REGISTRY.register(name.toString(), new StructureType(name.toString()));
}
}

// BiomeCategories
Registry<Biome> biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME);
biomeRegistry.getTagNames().forEach(tagKey -> {
Expand Down Expand Up @@ -921,6 +927,39 @@ public void sendBiomeUpdates(World world, Iterable<BlockVector2> chunks) {
originalWorld.getChunkSource().chunkMap.resendBiomesForChunks(nativeChunks);
}

@Override
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()));
ServerChunkCache chunkManager = originalWorld.getChunkSource();
WorldGenLevel proxyLevel = PaperweightServerLevelDelegateProxy.newInstance(session, originalWorld, this);
return k != null && k.place(proxyLevel, chunkManager.getGenerator(), random, new BlockPos(pt.x(), pt.y(), pt.z()));
}

@Override
public boolean generateStructure(StructureType type, World world, EditSession session, BlockVector3 pt) {
ServerLevel originalWorld = ((CraftWorld) world).getHandle();
Structure k = originalWorld.registryAccess().registryOrThrow(Registries.STRUCTURE).get(ResourceLocation.tryParse(type.id()));
if (k == null) {
return false;
}

ServerChunkCache chunkManager = originalWorld.getChunkSource();
WorldGenLevel proxyLevel = PaperweightServerLevelDelegateProxy.newInstance(session, originalWorld, this);
ChunkPos chunkPos = new ChunkPos(new BlockPos(pt.x(), pt.y(), pt.z()));
StructureStart structureStart = k.generate(originalWorld.registryAccess(), chunkManager.getGenerator(), chunkManager.getGenerator().getBiomeSource(), chunkManager.randomState(), originalWorld.getStructureManager(), originalWorld.getSeed(), chunkPos, 0, proxyLevel, biome -> true);

if (!structureStart.isValid()) {
return false;
} else {
BoundingBox boundingBox = structureStart.getBoundingBox();
ChunkPos min = new ChunkPos(SectionPos.blockToSectionCoord(boundingBox.minX()), SectionPos.blockToSectionCoord(boundingBox.minZ()));
ChunkPos max = new ChunkPos(SectionPos.blockToSectionCoord(boundingBox.maxX()), SectionPos.blockToSectionCoord(boundingBox.maxZ()));
ChunkPos.rangeClosed(min, max).forEach((chunkPosx) -> structureStart.placeInChunk(proxyLevel, originalWorld.structureManager(), chunkManager.getGenerator(), originalWorld.getRandom(), new BoundingBox(chunkPosx.getMinBlockX(), originalWorld.getMinBuildHeight(), chunkPosx.getMinBlockZ(), chunkPosx.getMaxBlockX(), originalWorld.getMaxBuildHeight(), chunkPosx.getMaxBlockZ()), chunkPosx));
return true;
}
}

// ------------------------------------------------------------------------
// Code that is less likely to break
// ------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import org.bukkit.craftbukkit.v1_20_R2.util.BlockStateListPopulator;
import org.jetbrains.annotations.Nullable;

public class FaweBlockStateListPopulator extends BlockStateListPopulator {

private final ServerLevel world;

public FaweBlockStateListPopulator(ServerLevel world) {
super(world);
this.world = world;
}

@Override
public long getSeed() {
return world.getSeed();
}

@Override
public ServerLevel getLevel() {
return world.getLevel();
}

@Override
public MinecraftServer getServer() {
return world.getServer();
}

@Override
public ChunkSource getChunkSource() {
return world.getChunkSource();
}

@Override
public ChunkAccess getChunk(final int chunkX, final int chunkZ, final ChunkStatus leastStatus, final boolean create) {
return world.getChunk(chunkX, chunkZ, leastStatus, create);
}

@Override
public BiomeManager getBiomeManager() {
return world.getBiomeManager();
}

@Override
public Holder<Biome> getUncachedNoiseBiome(final int biomeX, final int biomeY, final int biomeZ) {
return world.getUncachedNoiseBiome(biomeX, biomeY, biomeZ);
}

@Override
public int getSeaLevel() {
return world.getSeaLevel();
}

@Override
public FeatureFlagSet enabledFeatures() {
return world.enabledFeatures();
}

@Override
public float getShade(final Direction direction, final boolean shaded) {
return world.getShade(direction, shaded);
}

@Override
public LevelLightEngine getLightEngine() {
return world.getLightEngine();
}

@Nullable
@Override
public ChunkAccess getChunkIfLoadedImmediately(final int x, final int z) {
return world.getChunkIfLoadedImmediately(x, z);
}

@Override
public BlockState getBlockStateIfLoaded(final BlockPos blockposition) {
return world.getBlockStateIfLoaded(blockposition);
}

@Override
public FluidState getFluidIfLoaded(final BlockPos blockposition) {
return world.getFluidIfLoaded(blockposition);
}

@Override
public WorldBorder getWorldBorder() {
return world.getWorldBorder();
}

@Override
public boolean setBlock(final BlockPos pos, final BlockState state, final int flags, final int maxUpdateDepth) {
return world.setBlock(pos, state, flags, maxUpdateDepth);
}

@Override
public boolean removeBlock(final BlockPos pos, final boolean move) {
return world.removeBlock(pos, move);
}

@Override
public boolean destroyBlock(final BlockPos pos, final boolean drop, final Entity breakingEntity, final int maxUpdateDepth) {
return world.destroyBlock(pos, drop, breakingEntity, maxUpdateDepth);
}

@Override
public BlockState getBlockState(final BlockPos pos) {
return world.getBlockState(pos);
}

@Override
public boolean setBlock(final BlockPos pos, final BlockState state, final int flags) {
return world.setBlock(pos, state, flags);
}

}
Loading
Loading