853 lines
34 KiB
Java
853 lines
34 KiB
Java
package net.minecraft.world.level.levelgen.structure.templatesystem;
|
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Maps;
|
|
import com.mojang.datafixers.util.Pair;
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
import java.util.ArrayList;
|
|
import java.util.Comparator;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.stream.Collectors;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.HolderGetter;
|
|
import net.minecraft.core.IdMapper;
|
|
import net.minecraft.core.Vec3i;
|
|
import net.minecraft.data.worldgen.Pools;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.DoubleTag;
|
|
import net.minecraft.nbt.IntTag;
|
|
import net.minecraft.nbt.ListTag;
|
|
import net.minecraft.nbt.NbtUtils;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.world.RandomizableContainer;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntitySpawnReason;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.decoration.Painting;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.level.EmptyBlockGetter;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.LevelAccessor;
|
|
import net.minecraft.world.level.ServerLevelAccessor;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.JigsawBlock;
|
|
import net.minecraft.world.level.block.LiquidBlockContainer;
|
|
import net.minecraft.world.level.block.Mirror;
|
|
import net.minecraft.world.level.block.Rotation;
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
import net.minecraft.world.level.block.entity.JigsawBlockEntity;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
|
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape;
|
|
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class StructureTemplate {
|
|
public static final String PALETTE_TAG = "palette";
|
|
public static final String PALETTE_LIST_TAG = "palettes";
|
|
public static final String ENTITIES_TAG = "entities";
|
|
public static final String BLOCKS_TAG = "blocks";
|
|
public static final String BLOCK_TAG_POS = "pos";
|
|
public static final String BLOCK_TAG_STATE = "state";
|
|
public static final String BLOCK_TAG_NBT = "nbt";
|
|
public static final String ENTITY_TAG_POS = "pos";
|
|
public static final String ENTITY_TAG_BLOCKPOS = "blockPos";
|
|
public static final String ENTITY_TAG_NBT = "nbt";
|
|
public static final String SIZE_TAG = "size";
|
|
private final List<StructureTemplate.Palette> palettes = Lists.<StructureTemplate.Palette>newArrayList();
|
|
private final List<StructureTemplate.StructureEntityInfo> entityInfoList = Lists.<StructureTemplate.StructureEntityInfo>newArrayList();
|
|
private Vec3i size = Vec3i.ZERO;
|
|
private String author = "?";
|
|
|
|
public Vec3i getSize() {
|
|
return this.size;
|
|
}
|
|
|
|
public void setAuthor(String author) {
|
|
this.author = author;
|
|
}
|
|
|
|
public String getAuthor() {
|
|
return this.author;
|
|
}
|
|
|
|
public void fillFromWorld(Level level, BlockPos pos, Vec3i size, boolean withEntities, @Nullable Block toIgnore) {
|
|
if (size.getX() >= 1 && size.getY() >= 1 && size.getZ() >= 1) {
|
|
BlockPos blockPos = pos.offset(size).offset(-1, -1, -1);
|
|
List<StructureTemplate.StructureBlockInfo> list = Lists.<StructureTemplate.StructureBlockInfo>newArrayList();
|
|
List<StructureTemplate.StructureBlockInfo> list2 = Lists.<StructureTemplate.StructureBlockInfo>newArrayList();
|
|
List<StructureTemplate.StructureBlockInfo> list3 = Lists.<StructureTemplate.StructureBlockInfo>newArrayList();
|
|
BlockPos blockPos2 = new BlockPos(Math.min(pos.getX(), blockPos.getX()), Math.min(pos.getY(), blockPos.getY()), Math.min(pos.getZ(), blockPos.getZ()));
|
|
BlockPos blockPos3 = new BlockPos(Math.max(pos.getX(), blockPos.getX()), Math.max(pos.getY(), blockPos.getY()), Math.max(pos.getZ(), blockPos.getZ()));
|
|
this.size = size;
|
|
|
|
for (BlockPos blockPos4 : BlockPos.betweenClosed(blockPos2, blockPos3)) {
|
|
BlockPos blockPos5 = blockPos4.subtract(blockPos2);
|
|
BlockState blockState = level.getBlockState(blockPos4);
|
|
if (toIgnore == null || !blockState.is(toIgnore)) {
|
|
BlockEntity blockEntity = level.getBlockEntity(blockPos4);
|
|
StructureTemplate.StructureBlockInfo structureBlockInfo;
|
|
if (blockEntity != null) {
|
|
structureBlockInfo = new StructureTemplate.StructureBlockInfo(blockPos5, blockState, blockEntity.saveWithId(level.registryAccess()));
|
|
} else {
|
|
structureBlockInfo = new StructureTemplate.StructureBlockInfo(blockPos5, blockState, null);
|
|
}
|
|
|
|
addToLists(structureBlockInfo, list, list2, list3);
|
|
}
|
|
}
|
|
|
|
List<StructureTemplate.StructureBlockInfo> list4 = buildInfoList(list, list2, list3);
|
|
this.palettes.clear();
|
|
this.palettes.add(new StructureTemplate.Palette(list4));
|
|
if (withEntities) {
|
|
this.fillEntityList(level, blockPos2, blockPos3);
|
|
} else {
|
|
this.entityInfoList.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void addToLists(
|
|
StructureTemplate.StructureBlockInfo blockInfo,
|
|
List<StructureTemplate.StructureBlockInfo> normalBlocks,
|
|
List<StructureTemplate.StructureBlockInfo> blocksWithNbt,
|
|
List<StructureTemplate.StructureBlockInfo> blocksWithSpecialShape
|
|
) {
|
|
if (blockInfo.nbt != null) {
|
|
blocksWithNbt.add(blockInfo);
|
|
} else if (!blockInfo.state.getBlock().hasDynamicShape() && blockInfo.state.isCollisionShapeFullBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) {
|
|
normalBlocks.add(blockInfo);
|
|
} else {
|
|
blocksWithSpecialShape.add(blockInfo);
|
|
}
|
|
}
|
|
|
|
private static List<StructureTemplate.StructureBlockInfo> buildInfoList(
|
|
List<StructureTemplate.StructureBlockInfo> normalBlocks,
|
|
List<StructureTemplate.StructureBlockInfo> blocksWithNbt,
|
|
List<StructureTemplate.StructureBlockInfo> blocksWithSpecialShape
|
|
) {
|
|
Comparator<StructureTemplate.StructureBlockInfo> comparator = Comparator.comparingInt(structureBlockInfo -> structureBlockInfo.pos.getY())
|
|
.thenComparingInt(structureBlockInfo -> structureBlockInfo.pos.getX())
|
|
.thenComparingInt(structureBlockInfo -> structureBlockInfo.pos.getZ());
|
|
normalBlocks.sort(comparator);
|
|
blocksWithSpecialShape.sort(comparator);
|
|
blocksWithNbt.sort(comparator);
|
|
List<StructureTemplate.StructureBlockInfo> list = Lists.<StructureTemplate.StructureBlockInfo>newArrayList();
|
|
list.addAll(normalBlocks);
|
|
list.addAll(blocksWithSpecialShape);
|
|
list.addAll(blocksWithNbt);
|
|
return list;
|
|
}
|
|
|
|
private void fillEntityList(Level level, BlockPos startPos, BlockPos endPos) {
|
|
List<Entity> list = level.getEntitiesOfClass(Entity.class, AABB.encapsulatingFullBlocks(startPos, endPos), entityx -> !(entityx instanceof Player));
|
|
this.entityInfoList.clear();
|
|
|
|
for (Entity entity : list) {
|
|
Vec3 vec3 = new Vec3(entity.getX() - startPos.getX(), entity.getY() - startPos.getY(), entity.getZ() - startPos.getZ());
|
|
CompoundTag compoundTag = new CompoundTag();
|
|
entity.save(compoundTag);
|
|
BlockPos blockPos;
|
|
if (entity instanceof Painting) {
|
|
blockPos = ((Painting)entity).getPos().subtract(startPos);
|
|
} else {
|
|
blockPos = BlockPos.containing(vec3);
|
|
}
|
|
|
|
this.entityInfoList.add(new StructureTemplate.StructureEntityInfo(vec3, blockPos, compoundTag.copy()));
|
|
}
|
|
}
|
|
|
|
public List<StructureTemplate.StructureBlockInfo> filterBlocks(BlockPos pos, StructurePlaceSettings settings, Block block) {
|
|
return this.filterBlocks(pos, settings, block, true);
|
|
}
|
|
|
|
public List<StructureTemplate.JigsawBlockInfo> getJigsaws(BlockPos pos, Rotation rotation) {
|
|
if (this.palettes.isEmpty()) {
|
|
return new ArrayList();
|
|
} else {
|
|
StructurePlaceSettings structurePlaceSettings = new StructurePlaceSettings().setRotation(rotation);
|
|
List<StructureTemplate.JigsawBlockInfo> list = structurePlaceSettings.getRandomPalette(this.palettes, pos).jigsaws();
|
|
List<StructureTemplate.JigsawBlockInfo> list2 = new ArrayList(list.size());
|
|
|
|
for (StructureTemplate.JigsawBlockInfo jigsawBlockInfo : list) {
|
|
StructureTemplate.StructureBlockInfo structureBlockInfo = jigsawBlockInfo.info;
|
|
list2.add(
|
|
jigsawBlockInfo.withInfo(
|
|
new StructureTemplate.StructureBlockInfo(
|
|
calculateRelativePosition(structurePlaceSettings, structureBlockInfo.pos()).offset(pos),
|
|
structureBlockInfo.state.rotate(structurePlaceSettings.getRotation()),
|
|
structureBlockInfo.nbt
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
return list2;
|
|
}
|
|
}
|
|
|
|
public ObjectArrayList<StructureTemplate.StructureBlockInfo> filterBlocks(BlockPos pos, StructurePlaceSettings settings, Block block, boolean relativePosition) {
|
|
ObjectArrayList<StructureTemplate.StructureBlockInfo> objectArrayList = new ObjectArrayList<>();
|
|
BoundingBox boundingBox = settings.getBoundingBox();
|
|
if (this.palettes.isEmpty()) {
|
|
return objectArrayList;
|
|
} else {
|
|
for (StructureTemplate.StructureBlockInfo structureBlockInfo : settings.getRandomPalette(this.palettes, pos).blocks(block)) {
|
|
BlockPos blockPos = relativePosition ? calculateRelativePosition(settings, structureBlockInfo.pos).offset(pos) : structureBlockInfo.pos;
|
|
if (boundingBox == null || boundingBox.isInside(blockPos)) {
|
|
objectArrayList.add(new StructureTemplate.StructureBlockInfo(blockPos, structureBlockInfo.state.rotate(settings.getRotation()), structureBlockInfo.nbt));
|
|
}
|
|
}
|
|
|
|
return objectArrayList;
|
|
}
|
|
}
|
|
|
|
public BlockPos calculateConnectedPosition(StructurePlaceSettings decorator, BlockPos start, StructurePlaceSettings settings, BlockPos end) {
|
|
BlockPos blockPos = calculateRelativePosition(decorator, start);
|
|
BlockPos blockPos2 = calculateRelativePosition(settings, end);
|
|
return blockPos.subtract(blockPos2);
|
|
}
|
|
|
|
public static BlockPos calculateRelativePosition(StructurePlaceSettings decorator, BlockPos pos) {
|
|
return transform(pos, decorator.getMirror(), decorator.getRotation(), decorator.getRotationPivot());
|
|
}
|
|
|
|
public boolean placeInWorld(ServerLevelAccessor serverLevel, BlockPos offset, BlockPos pos, StructurePlaceSettings settings, RandomSource random, int flags) {
|
|
if (this.palettes.isEmpty()) {
|
|
return false;
|
|
} else {
|
|
List<StructureTemplate.StructureBlockInfo> list = settings.getRandomPalette(this.palettes, offset).blocks();
|
|
if ((!list.isEmpty() || !settings.isIgnoreEntities() && !this.entityInfoList.isEmpty())
|
|
&& this.size.getX() >= 1
|
|
&& this.size.getY() >= 1
|
|
&& this.size.getZ() >= 1) {
|
|
BoundingBox boundingBox = settings.getBoundingBox();
|
|
List<BlockPos> list2 = Lists.<BlockPos>newArrayListWithCapacity(settings.shouldApplyWaterlogging() ? list.size() : 0);
|
|
List<BlockPos> list3 = Lists.<BlockPos>newArrayListWithCapacity(settings.shouldApplyWaterlogging() ? list.size() : 0);
|
|
List<Pair<BlockPos, CompoundTag>> list4 = Lists.<Pair<BlockPos, CompoundTag>>newArrayListWithCapacity(list.size());
|
|
int i = Integer.MAX_VALUE;
|
|
int j = Integer.MAX_VALUE;
|
|
int k = Integer.MAX_VALUE;
|
|
int l = Integer.MIN_VALUE;
|
|
int m = Integer.MIN_VALUE;
|
|
int n = Integer.MIN_VALUE;
|
|
|
|
for (StructureTemplate.StructureBlockInfo structureBlockInfo : processBlockInfos(serverLevel, offset, pos, settings, list)) {
|
|
BlockPos blockPos = structureBlockInfo.pos;
|
|
if (boundingBox == null || boundingBox.isInside(blockPos)) {
|
|
FluidState fluidState = settings.shouldApplyWaterlogging() ? serverLevel.getFluidState(blockPos) : null;
|
|
BlockState blockState = structureBlockInfo.state.mirror(settings.getMirror()).rotate(settings.getRotation());
|
|
if (structureBlockInfo.nbt != null) {
|
|
serverLevel.setBlock(blockPos, Blocks.BARRIER.defaultBlockState(), 820);
|
|
}
|
|
|
|
if (serverLevel.setBlock(blockPos, blockState, flags)) {
|
|
i = Math.min(i, blockPos.getX());
|
|
j = Math.min(j, blockPos.getY());
|
|
k = Math.min(k, blockPos.getZ());
|
|
l = Math.max(l, blockPos.getX());
|
|
m = Math.max(m, blockPos.getY());
|
|
n = Math.max(n, blockPos.getZ());
|
|
list4.add(Pair.of(blockPos, structureBlockInfo.nbt));
|
|
if (structureBlockInfo.nbt != null) {
|
|
BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos);
|
|
if (blockEntity != null) {
|
|
if (blockEntity instanceof RandomizableContainer) {
|
|
structureBlockInfo.nbt.putLong("LootTableSeed", random.nextLong());
|
|
}
|
|
|
|
blockEntity.loadWithComponents(structureBlockInfo.nbt, serverLevel.registryAccess());
|
|
}
|
|
}
|
|
|
|
if (fluidState != null) {
|
|
if (blockState.getFluidState().isSource()) {
|
|
list3.add(blockPos);
|
|
} else if (blockState.getBlock() instanceof LiquidBlockContainer) {
|
|
((LiquidBlockContainer)blockState.getBlock()).placeLiquid(serverLevel, blockPos, blockState, fluidState);
|
|
if (!fluidState.isSource()) {
|
|
list2.add(blockPos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean bl = true;
|
|
Direction[] directions = new Direction[]{Direction.UP, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
|
|
|
|
while (bl && !list2.isEmpty()) {
|
|
bl = false;
|
|
Iterator<BlockPos> iterator = list2.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
BlockPos blockPos2 = (BlockPos)iterator.next();
|
|
FluidState fluidState2 = serverLevel.getFluidState(blockPos2);
|
|
|
|
for (int o = 0; o < directions.length && !fluidState2.isSource(); o++) {
|
|
BlockPos blockPos3 = blockPos2.relative(directions[o]);
|
|
FluidState fluidState3 = serverLevel.getFluidState(blockPos3);
|
|
if (fluidState3.isSource() && !list3.contains(blockPos3)) {
|
|
fluidState2 = fluidState3;
|
|
}
|
|
}
|
|
|
|
if (fluidState2.isSource()) {
|
|
BlockState blockState2 = serverLevel.getBlockState(blockPos2);
|
|
Block block = blockState2.getBlock();
|
|
if (block instanceof LiquidBlockContainer) {
|
|
((LiquidBlockContainer)block).placeLiquid(serverLevel, blockPos2, blockState2, fluidState2);
|
|
bl = true;
|
|
iterator.remove();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i <= l) {
|
|
if (!settings.getKnownShape()) {
|
|
DiscreteVoxelShape discreteVoxelShape = new BitSetDiscreteVoxelShape(l - i + 1, m - j + 1, n - k + 1);
|
|
int p = i;
|
|
int q = j;
|
|
int ox = k;
|
|
|
|
for (Pair<BlockPos, CompoundTag> pair : list4) {
|
|
BlockPos blockPos4 = pair.getFirst();
|
|
discreteVoxelShape.fill(blockPos4.getX() - p, blockPos4.getY() - q, blockPos4.getZ() - ox);
|
|
}
|
|
|
|
updateShapeAtEdge(serverLevel, flags, discreteVoxelShape, p, q, ox);
|
|
}
|
|
|
|
for (Pair<BlockPos, CompoundTag> pair2 : list4) {
|
|
BlockPos blockPos5 = pair2.getFirst();
|
|
if (!settings.getKnownShape()) {
|
|
BlockState blockState2 = serverLevel.getBlockState(blockPos5);
|
|
BlockState blockState3 = Block.updateFromNeighbourShapes(blockState2, serverLevel, blockPos5);
|
|
if (blockState2 != blockState3) {
|
|
serverLevel.setBlock(blockPos5, blockState3, flags & -2 | 16);
|
|
}
|
|
|
|
serverLevel.updateNeighborsAt(blockPos5, blockState3.getBlock());
|
|
}
|
|
|
|
if (pair2.getSecond() != null) {
|
|
BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos5);
|
|
if (blockEntity != null) {
|
|
blockEntity.setChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!settings.isIgnoreEntities()) {
|
|
this.placeEntities(
|
|
serverLevel, offset, settings.getMirror(), settings.getRotation(), settings.getRotationPivot(), boundingBox, settings.shouldFinalizeEntities()
|
|
);
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void updateShapeAtEdge(LevelAccessor level, int flags, DiscreteVoxelShape shape, BlockPos pos) {
|
|
updateShapeAtEdge(level, flags, shape, pos.getX(), pos.getY(), pos.getZ());
|
|
}
|
|
|
|
public static void updateShapeAtEdge(LevelAccessor level, int flags, DiscreteVoxelShape shape, int x, int y, int z) {
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
BlockPos.MutableBlockPos mutableBlockPos2 = new BlockPos.MutableBlockPos();
|
|
shape.forAllFaces((direction, m, n, o) -> {
|
|
mutableBlockPos.set(x + m, y + n, z + o);
|
|
mutableBlockPos2.setWithOffset(mutableBlockPos, direction);
|
|
BlockState blockState = level.getBlockState(mutableBlockPos);
|
|
BlockState blockState2 = level.getBlockState(mutableBlockPos2);
|
|
BlockState blockState3 = blockState.updateShape(level, level, mutableBlockPos, direction, mutableBlockPos2, blockState2, level.getRandom());
|
|
if (blockState != blockState3) {
|
|
level.setBlock(mutableBlockPos, blockState3, flags & -2);
|
|
}
|
|
|
|
BlockState blockState4 = blockState2.updateShape(level, level, mutableBlockPos2, direction.getOpposite(), mutableBlockPos, blockState3, level.getRandom());
|
|
if (blockState2 != blockState4) {
|
|
level.setBlock(mutableBlockPos2, blockState4, flags & -2);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static List<StructureTemplate.StructureBlockInfo> processBlockInfos(
|
|
ServerLevelAccessor serverLevel, BlockPos offset, BlockPos pos, StructurePlaceSettings settings, List<StructureTemplate.StructureBlockInfo> blockInfos
|
|
) {
|
|
List<StructureTemplate.StructureBlockInfo> list = new ArrayList();
|
|
List<StructureTemplate.StructureBlockInfo> list2 = new ArrayList();
|
|
|
|
for (StructureTemplate.StructureBlockInfo structureBlockInfo : blockInfos) {
|
|
BlockPos blockPos = calculateRelativePosition(settings, structureBlockInfo.pos).offset(offset);
|
|
StructureTemplate.StructureBlockInfo structureBlockInfo2 = new StructureTemplate.StructureBlockInfo(
|
|
blockPos, structureBlockInfo.state, structureBlockInfo.nbt != null ? structureBlockInfo.nbt.copy() : null
|
|
);
|
|
Iterator<StructureProcessor> iterator = settings.getProcessors().iterator();
|
|
|
|
while (structureBlockInfo2 != null && iterator.hasNext()) {
|
|
structureBlockInfo2 = ((StructureProcessor)iterator.next()).processBlock(serverLevel, offset, pos, structureBlockInfo, structureBlockInfo2, settings);
|
|
}
|
|
|
|
if (structureBlockInfo2 != null) {
|
|
list2.add(structureBlockInfo2);
|
|
list.add(structureBlockInfo);
|
|
}
|
|
}
|
|
|
|
for (StructureProcessor structureProcessor : settings.getProcessors()) {
|
|
list2 = structureProcessor.finalizeProcessing(serverLevel, offset, pos, list, list2, settings);
|
|
}
|
|
|
|
return list2;
|
|
}
|
|
|
|
private void placeEntities(
|
|
ServerLevelAccessor serverLevel, BlockPos pos, Mirror mirror, Rotation rotation, BlockPos offset, @Nullable BoundingBox boundingBox, boolean withEntities
|
|
) {
|
|
for (StructureTemplate.StructureEntityInfo structureEntityInfo : this.entityInfoList) {
|
|
BlockPos blockPos = transform(structureEntityInfo.blockPos, mirror, rotation, offset).offset(pos);
|
|
if (boundingBox == null || boundingBox.isInside(blockPos)) {
|
|
CompoundTag compoundTag = structureEntityInfo.nbt.copy();
|
|
Vec3 vec3 = transform(structureEntityInfo.pos, mirror, rotation, offset);
|
|
Vec3 vec32 = vec3.add(pos.getX(), pos.getY(), pos.getZ());
|
|
ListTag listTag = new ListTag();
|
|
listTag.add(DoubleTag.valueOf(vec32.x));
|
|
listTag.add(DoubleTag.valueOf(vec32.y));
|
|
listTag.add(DoubleTag.valueOf(vec32.z));
|
|
compoundTag.put("Pos", listTag);
|
|
compoundTag.remove("UUID");
|
|
createEntityIgnoreException(serverLevel, compoundTag).ifPresent(entity -> {
|
|
float f = entity.rotate(rotation);
|
|
f += entity.mirror(mirror) - entity.getYRot();
|
|
entity.snapTo(vec32.x, vec32.y, vec32.z, f, entity.getXRot());
|
|
if (withEntities && entity instanceof Mob) {
|
|
((Mob)entity).finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(BlockPos.containing(vec32)), EntitySpawnReason.STRUCTURE, null);
|
|
}
|
|
|
|
serverLevel.addFreshEntityWithPassengers(entity);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Optional<Entity> createEntityIgnoreException(ServerLevelAccessor level, CompoundTag tag) {
|
|
try {
|
|
return EntityType.create(tag, level.getLevel(), EntitySpawnReason.STRUCTURE);
|
|
} catch (Exception var3) {
|
|
return Optional.empty();
|
|
}
|
|
}
|
|
|
|
public Vec3i getSize(Rotation rotation) {
|
|
switch (rotation) {
|
|
case COUNTERCLOCKWISE_90:
|
|
case CLOCKWISE_90:
|
|
return new Vec3i(this.size.getZ(), this.size.getY(), this.size.getX());
|
|
default:
|
|
return this.size;
|
|
}
|
|
}
|
|
|
|
public static BlockPos transform(BlockPos targetPos, Mirror mirror, Rotation rotation, BlockPos offset) {
|
|
int i = targetPos.getX();
|
|
int j = targetPos.getY();
|
|
int k = targetPos.getZ();
|
|
boolean bl = true;
|
|
switch (mirror) {
|
|
case LEFT_RIGHT:
|
|
k = -k;
|
|
break;
|
|
case FRONT_BACK:
|
|
i = -i;
|
|
break;
|
|
default:
|
|
bl = false;
|
|
}
|
|
|
|
int l = offset.getX();
|
|
int m = offset.getZ();
|
|
switch (rotation) {
|
|
case COUNTERCLOCKWISE_90:
|
|
return new BlockPos(l - m + k, j, l + m - i);
|
|
case CLOCKWISE_90:
|
|
return new BlockPos(l + m - k, j, m - l + i);
|
|
case CLOCKWISE_180:
|
|
return new BlockPos(l + l - i, j, m + m - k);
|
|
default:
|
|
return bl ? new BlockPos(i, j, k) : targetPos;
|
|
}
|
|
}
|
|
|
|
public static Vec3 transform(Vec3 target, Mirror mirror, Rotation rotation, BlockPos centerOffset) {
|
|
double d = target.x;
|
|
double e = target.y;
|
|
double f = target.z;
|
|
boolean bl = true;
|
|
switch (mirror) {
|
|
case LEFT_RIGHT:
|
|
f = 1.0 - f;
|
|
break;
|
|
case FRONT_BACK:
|
|
d = 1.0 - d;
|
|
break;
|
|
default:
|
|
bl = false;
|
|
}
|
|
|
|
int i = centerOffset.getX();
|
|
int j = centerOffset.getZ();
|
|
switch (rotation) {
|
|
case COUNTERCLOCKWISE_90:
|
|
return new Vec3(i - j + f, e, i + j + 1 - d);
|
|
case CLOCKWISE_90:
|
|
return new Vec3(i + j + 1 - f, e, j - i + d);
|
|
case CLOCKWISE_180:
|
|
return new Vec3(i + i + 1 - d, e, j + j + 1 - f);
|
|
default:
|
|
return bl ? new Vec3(d, e, f) : target;
|
|
}
|
|
}
|
|
|
|
public BlockPos getZeroPositionWithTransform(BlockPos targetPos, Mirror mirror, Rotation rotation) {
|
|
return getZeroPositionWithTransform(targetPos, mirror, rotation, this.getSize().getX(), this.getSize().getZ());
|
|
}
|
|
|
|
public static BlockPos getZeroPositionWithTransform(BlockPos pos, Mirror mirror, Rotation rotation, int sizeX, int sizeZ) {
|
|
sizeX--;
|
|
sizeZ--;
|
|
int i = mirror == Mirror.FRONT_BACK ? sizeX : 0;
|
|
int j = mirror == Mirror.LEFT_RIGHT ? sizeZ : 0;
|
|
BlockPos blockPos = pos;
|
|
switch (rotation) {
|
|
case COUNTERCLOCKWISE_90:
|
|
blockPos = pos.offset(j, 0, sizeX - i);
|
|
break;
|
|
case CLOCKWISE_90:
|
|
blockPos = pos.offset(sizeZ - j, 0, i);
|
|
break;
|
|
case CLOCKWISE_180:
|
|
blockPos = pos.offset(sizeX - i, 0, sizeZ - j);
|
|
break;
|
|
case NONE:
|
|
blockPos = pos.offset(i, 0, j);
|
|
}
|
|
|
|
return blockPos;
|
|
}
|
|
|
|
public BoundingBox getBoundingBox(StructurePlaceSettings settings, BlockPos startPos) {
|
|
return this.getBoundingBox(startPos, settings.getRotation(), settings.getRotationPivot(), settings.getMirror());
|
|
}
|
|
|
|
public BoundingBox getBoundingBox(BlockPos startPos, Rotation rotation, BlockPos pivotPos, Mirror mirror) {
|
|
return getBoundingBox(startPos, rotation, pivotPos, mirror, this.size);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
protected static BoundingBox getBoundingBox(BlockPos startPos, Rotation rotation, BlockPos pivotPos, Mirror mirror, Vec3i size) {
|
|
Vec3i vec3i = size.offset(-1, -1, -1);
|
|
BlockPos blockPos = transform(BlockPos.ZERO, mirror, rotation, pivotPos);
|
|
BlockPos blockPos2 = transform(BlockPos.ZERO.offset(vec3i), mirror, rotation, pivotPos);
|
|
return BoundingBox.fromCorners(blockPos, blockPos2).move(startPos);
|
|
}
|
|
|
|
public CompoundTag save(CompoundTag tag) {
|
|
if (this.palettes.isEmpty()) {
|
|
tag.put("blocks", new ListTag());
|
|
tag.put("palette", new ListTag());
|
|
} else {
|
|
List<StructureTemplate.SimplePalette> list = Lists.<StructureTemplate.SimplePalette>newArrayList();
|
|
StructureTemplate.SimplePalette simplePalette = new StructureTemplate.SimplePalette();
|
|
list.add(simplePalette);
|
|
|
|
for (int i = 1; i < this.palettes.size(); i++) {
|
|
list.add(new StructureTemplate.SimplePalette());
|
|
}
|
|
|
|
ListTag listTag = new ListTag();
|
|
List<StructureTemplate.StructureBlockInfo> list2 = ((StructureTemplate.Palette)this.palettes.get(0)).blocks();
|
|
|
|
for (int j = 0; j < list2.size(); j++) {
|
|
StructureTemplate.StructureBlockInfo structureBlockInfo = (StructureTemplate.StructureBlockInfo)list2.get(j);
|
|
CompoundTag compoundTag = new CompoundTag();
|
|
compoundTag.put("pos", this.newIntegerList(structureBlockInfo.pos.getX(), structureBlockInfo.pos.getY(), structureBlockInfo.pos.getZ()));
|
|
int k = simplePalette.idFor(structureBlockInfo.state);
|
|
compoundTag.putInt("state", k);
|
|
if (structureBlockInfo.nbt != null) {
|
|
compoundTag.put("nbt", structureBlockInfo.nbt);
|
|
}
|
|
|
|
listTag.add(compoundTag);
|
|
|
|
for (int l = 1; l < this.palettes.size(); l++) {
|
|
StructureTemplate.SimplePalette simplePalette2 = (StructureTemplate.SimplePalette)list.get(l);
|
|
simplePalette2.addMapping(((StructureTemplate.StructureBlockInfo)((StructureTemplate.Palette)this.palettes.get(l)).blocks().get(j)).state, k);
|
|
}
|
|
}
|
|
|
|
tag.put("blocks", listTag);
|
|
if (list.size() == 1) {
|
|
ListTag listTag2 = new ListTag();
|
|
|
|
for (BlockState blockState : simplePalette) {
|
|
listTag2.add(NbtUtils.writeBlockState(blockState));
|
|
}
|
|
|
|
tag.put("palette", listTag2);
|
|
} else {
|
|
ListTag listTag2 = new ListTag();
|
|
|
|
for (StructureTemplate.SimplePalette simplePalette3 : list) {
|
|
ListTag listTag3 = new ListTag();
|
|
|
|
for (BlockState blockState2 : simplePalette3) {
|
|
listTag3.add(NbtUtils.writeBlockState(blockState2));
|
|
}
|
|
|
|
listTag2.add(listTag3);
|
|
}
|
|
|
|
tag.put("palettes", listTag2);
|
|
}
|
|
}
|
|
|
|
ListTag listTag4 = new ListTag();
|
|
|
|
for (StructureTemplate.StructureEntityInfo structureEntityInfo : this.entityInfoList) {
|
|
CompoundTag compoundTag2 = new CompoundTag();
|
|
compoundTag2.put("pos", this.newDoubleList(structureEntityInfo.pos.x, structureEntityInfo.pos.y, structureEntityInfo.pos.z));
|
|
compoundTag2.put(
|
|
"blockPos", this.newIntegerList(structureEntityInfo.blockPos.getX(), structureEntityInfo.blockPos.getY(), structureEntityInfo.blockPos.getZ())
|
|
);
|
|
if (structureEntityInfo.nbt != null) {
|
|
compoundTag2.put("nbt", structureEntityInfo.nbt);
|
|
}
|
|
|
|
listTag4.add(compoundTag2);
|
|
}
|
|
|
|
tag.put("entities", listTag4);
|
|
tag.put("size", this.newIntegerList(this.size.getX(), this.size.getY(), this.size.getZ()));
|
|
return NbtUtils.addCurrentDataVersion(tag);
|
|
}
|
|
|
|
public void load(HolderGetter<Block> blockGetter, CompoundTag tag) {
|
|
this.palettes.clear();
|
|
this.entityInfoList.clear();
|
|
ListTag listTag = tag.getListOrEmpty("size");
|
|
this.size = new Vec3i(listTag.getIntOr(0, 0), listTag.getIntOr(1, 0), listTag.getIntOr(2, 0));
|
|
ListTag listTag2 = tag.getListOrEmpty("blocks");
|
|
Optional<ListTag> optional = tag.getList("palettes");
|
|
if (optional.isPresent()) {
|
|
for (int i = 0; i < ((ListTag)optional.get()).size(); i++) {
|
|
this.loadPalette(blockGetter, ((ListTag)optional.get()).getListOrEmpty(i), listTag2);
|
|
}
|
|
} else {
|
|
this.loadPalette(blockGetter, tag.getListOrEmpty("palette"), listTag2);
|
|
}
|
|
|
|
tag.getListOrEmpty("entities").compoundStream().forEach(compoundTag -> {
|
|
ListTag listTagx = compoundTag.getListOrEmpty("pos");
|
|
Vec3 vec3 = new Vec3(listTagx.getDoubleOr(0, 0.0), listTagx.getDoubleOr(1, 0.0), listTagx.getDoubleOr(2, 0.0));
|
|
ListTag listTag2x = compoundTag.getListOrEmpty("blockPos");
|
|
BlockPos blockPos = new BlockPos(listTag2x.getIntOr(0, 0), listTag2x.getIntOr(1, 0), listTag2x.getIntOr(2, 0));
|
|
compoundTag.getCompound("nbt").ifPresent(compoundTagx -> this.entityInfoList.add(new StructureTemplate.StructureEntityInfo(vec3, blockPos, compoundTagx)));
|
|
});
|
|
}
|
|
|
|
private void loadPalette(HolderGetter<Block> blockGetter, ListTag paletteTag, ListTag blocksTag) {
|
|
StructureTemplate.SimplePalette simplePalette = new StructureTemplate.SimplePalette();
|
|
|
|
for (int i = 0; i < paletteTag.size(); i++) {
|
|
simplePalette.addMapping(NbtUtils.readBlockState(blockGetter, paletteTag.getCompoundOrEmpty(i)), i);
|
|
}
|
|
|
|
List<StructureTemplate.StructureBlockInfo> list = Lists.<StructureTemplate.StructureBlockInfo>newArrayList();
|
|
List<StructureTemplate.StructureBlockInfo> list2 = Lists.<StructureTemplate.StructureBlockInfo>newArrayList();
|
|
List<StructureTemplate.StructureBlockInfo> list3 = Lists.<StructureTemplate.StructureBlockInfo>newArrayList();
|
|
blocksTag.compoundStream().forEach(compoundTag -> {
|
|
ListTag listTag = compoundTag.getListOrEmpty("pos");
|
|
BlockPos blockPos = new BlockPos(listTag.getIntOr(0, 0), listTag.getIntOr(1, 0), listTag.getIntOr(2, 0));
|
|
BlockState blockState = simplePalette.stateFor(compoundTag.getIntOr("state", 0));
|
|
CompoundTag compoundTag2 = (CompoundTag)compoundTag.getCompound("nbt").orElse(null);
|
|
StructureTemplate.StructureBlockInfo structureBlockInfo = new StructureTemplate.StructureBlockInfo(blockPos, blockState, compoundTag2);
|
|
addToLists(structureBlockInfo, list, list2, list3);
|
|
});
|
|
List<StructureTemplate.StructureBlockInfo> list4 = buildInfoList(list, list2, list3);
|
|
this.palettes.add(new StructureTemplate.Palette(list4));
|
|
}
|
|
|
|
private ListTag newIntegerList(int... values) {
|
|
ListTag listTag = new ListTag();
|
|
|
|
for (int i : values) {
|
|
listTag.add(IntTag.valueOf(i));
|
|
}
|
|
|
|
return listTag;
|
|
}
|
|
|
|
private ListTag newDoubleList(double... values) {
|
|
ListTag listTag = new ListTag();
|
|
|
|
for (double d : values) {
|
|
listTag.add(DoubleTag.valueOf(d));
|
|
}
|
|
|
|
return listTag;
|
|
}
|
|
|
|
public static JigsawBlockEntity.JointType getJointType(CompoundTag tag, BlockState state) {
|
|
return (JigsawBlockEntity.JointType)tag.read("joint", JigsawBlockEntity.JointType.CODEC).orElseGet(() -> getDefaultJointType(state));
|
|
}
|
|
|
|
public static JigsawBlockEntity.JointType getDefaultJointType(BlockState state) {
|
|
return JigsawBlock.getFrontFacing(state).getAxis().isHorizontal() ? JigsawBlockEntity.JointType.ALIGNED : JigsawBlockEntity.JointType.ROLLABLE;
|
|
}
|
|
|
|
public record JigsawBlockInfo(
|
|
StructureTemplate.StructureBlockInfo info,
|
|
JigsawBlockEntity.JointType jointType,
|
|
ResourceLocation name,
|
|
ResourceKey<StructureTemplatePool> pool,
|
|
ResourceLocation target,
|
|
int placementPriority,
|
|
int selectionPriority
|
|
) {
|
|
|
|
public static StructureTemplate.JigsawBlockInfo of(StructureTemplate.StructureBlockInfo structureBlockInfo) {
|
|
CompoundTag compoundTag = (CompoundTag)Objects.requireNonNull(structureBlockInfo.nbt(), () -> structureBlockInfo + " nbt was null");
|
|
return new StructureTemplate.JigsawBlockInfo(
|
|
structureBlockInfo,
|
|
StructureTemplate.getJointType(compoundTag, structureBlockInfo.state()),
|
|
(ResourceLocation)compoundTag.read("name", ResourceLocation.CODEC).orElse(JigsawBlockEntity.EMPTY_ID),
|
|
(ResourceKey<StructureTemplatePool>)compoundTag.read("pool", JigsawBlockEntity.POOL_CODEC).orElse(Pools.EMPTY),
|
|
(ResourceLocation)compoundTag.read("target", ResourceLocation.CODEC).orElse(JigsawBlockEntity.EMPTY_ID),
|
|
compoundTag.getIntOr("placement_priority", 0),
|
|
compoundTag.getIntOr("selection_priority", 0)
|
|
);
|
|
}
|
|
|
|
public String toString() {
|
|
return String.format(
|
|
Locale.ROOT,
|
|
"<JigsawBlockInfo | %s | %s | name: %s | pool: %s | target: %s | placement: %d | selection: %d | %s>",
|
|
this.info.pos,
|
|
this.info.state,
|
|
this.name,
|
|
this.pool.location(),
|
|
this.target,
|
|
this.placementPriority,
|
|
this.selectionPriority,
|
|
this.info.nbt
|
|
);
|
|
}
|
|
|
|
public StructureTemplate.JigsawBlockInfo withInfo(StructureTemplate.StructureBlockInfo info) {
|
|
return new StructureTemplate.JigsawBlockInfo(info, this.jointType, this.name, this.pool, this.target, this.placementPriority, this.selectionPriority);
|
|
}
|
|
}
|
|
|
|
public static final class Palette {
|
|
private final List<StructureTemplate.StructureBlockInfo> blocks;
|
|
private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.<Block, List<StructureTemplate.StructureBlockInfo>>newHashMap();
|
|
@Nullable
|
|
private List<StructureTemplate.JigsawBlockInfo> cachedJigsaws;
|
|
|
|
Palette(List<StructureTemplate.StructureBlockInfo> blocks) {
|
|
this.blocks = blocks;
|
|
}
|
|
|
|
public List<StructureTemplate.JigsawBlockInfo> jigsaws() {
|
|
if (this.cachedJigsaws == null) {
|
|
this.cachedJigsaws = this.blocks(Blocks.JIGSAW).stream().map(StructureTemplate.JigsawBlockInfo::of).toList();
|
|
}
|
|
|
|
return this.cachedJigsaws;
|
|
}
|
|
|
|
public List<StructureTemplate.StructureBlockInfo> blocks() {
|
|
return this.blocks;
|
|
}
|
|
|
|
public List<StructureTemplate.StructureBlockInfo> blocks(Block block) {
|
|
return (List<StructureTemplate.StructureBlockInfo>)this.cache
|
|
.computeIfAbsent(block, blockx -> (List)this.blocks.stream().filter(structureBlockInfo -> structureBlockInfo.state.is(blockx)).collect(Collectors.toList()));
|
|
}
|
|
}
|
|
|
|
static class SimplePalette implements Iterable<BlockState> {
|
|
public static final BlockState DEFAULT_BLOCK_STATE = Blocks.AIR.defaultBlockState();
|
|
private final IdMapper<BlockState> ids = new IdMapper<>(16);
|
|
private int lastId;
|
|
|
|
public int idFor(BlockState state) {
|
|
int i = this.ids.getId(state);
|
|
if (i == -1) {
|
|
i = this.lastId++;
|
|
this.ids.addMapping(state, i);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
@Nullable
|
|
public BlockState stateFor(int id) {
|
|
BlockState blockState = this.ids.byId(id);
|
|
return blockState == null ? DEFAULT_BLOCK_STATE : blockState;
|
|
}
|
|
|
|
public Iterator<BlockState> iterator() {
|
|
return this.ids.iterator();
|
|
}
|
|
|
|
public void addMapping(BlockState state, int id) {
|
|
this.ids.addMapping(state, id);
|
|
}
|
|
}
|
|
|
|
public record StructureBlockInfo(BlockPos pos, BlockState state, @Nullable CompoundTag nbt) {
|
|
|
|
public String toString() {
|
|
return String.format(Locale.ROOT, "<StructureBlockInfo | %s | %s | %s>", this.pos, this.state, this.nbt);
|
|
}
|
|
}
|
|
|
|
public static class StructureEntityInfo {
|
|
public final Vec3 pos;
|
|
public final BlockPos blockPos;
|
|
public final CompoundTag nbt;
|
|
|
|
public StructureEntityInfo(Vec3 pos, BlockPos blockPos, CompoundTag nbt) {
|
|
this.pos = pos;
|
|
this.blockPos = blockPos;
|
|
this.nbt = nbt;
|
|
}
|
|
}
|
|
}
|