package net.minecraft.world.level.block.entity; import java.util.Iterator; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; import net.minecraft.ResourceLocationException; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.core.Vec3i; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.util.StringUtil; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.StructureBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.StructureMode; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.templatesystem.BlockRotProcessor; import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; import org.jetbrains.annotations.Nullable; public class StructureBlockEntity extends BlockEntity { private static final int SCAN_CORNER_BLOCKS_RANGE = 5; public static final int MAX_OFFSET_PER_AXIS = 48; public static final int MAX_SIZE_PER_AXIS = 48; public static final String AUTHOR_TAG = "author"; @Nullable private ResourceLocation structureName; private String author = ""; private String metaData = ""; private BlockPos structurePos = new BlockPos(0, 1, 0); private Vec3i structureSize = Vec3i.ZERO; private Mirror mirror = Mirror.NONE; private Rotation rotation = Rotation.NONE; private StructureMode mode; private boolean ignoreEntities = true; private boolean powered; private boolean showAir; private boolean showBoundingBox = true; private float integrity = 1.0F; private long seed; public StructureBlockEntity(BlockPos pos, BlockState blockState) { super(BlockEntityType.STRUCTURE_BLOCK, pos, blockState); this.mode = blockState.getValue(StructureBlock.MODE); } @Override protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.saveAdditional(tag, registries); tag.putString("name", this.getStructureName()); tag.putString("author", this.author); tag.putString("metadata", this.metaData); tag.putInt("posX", this.structurePos.getX()); tag.putInt("posY", this.structurePos.getY()); tag.putInt("posZ", this.structurePos.getZ()); tag.putInt("sizeX", this.structureSize.getX()); tag.putInt("sizeY", this.structureSize.getY()); tag.putInt("sizeZ", this.structureSize.getZ()); tag.putString("rotation", this.rotation.toString()); tag.putString("mirror", this.mirror.toString()); tag.putString("mode", this.mode.toString()); tag.putBoolean("ignoreEntities", this.ignoreEntities); tag.putBoolean("powered", this.powered); tag.putBoolean("showair", this.showAir); tag.putBoolean("showboundingbox", this.showBoundingBox); tag.putFloat("integrity", this.integrity); tag.putLong("seed", this.seed); } @Override protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.loadAdditional(tag, registries); this.setStructureName(tag.getString("name")); this.author = tag.getString("author"); this.metaData = tag.getString("metadata"); int i = Mth.clamp(tag.getInt("posX"), -48, 48); int j = Mth.clamp(tag.getInt("posY"), -48, 48); int k = Mth.clamp(tag.getInt("posZ"), -48, 48); this.structurePos = new BlockPos(i, j, k); int l = Mth.clamp(tag.getInt("sizeX"), 0, 48); int m = Mth.clamp(tag.getInt("sizeY"), 0, 48); int n = Mth.clamp(tag.getInt("sizeZ"), 0, 48); this.structureSize = new Vec3i(l, m, n); try { this.rotation = Rotation.valueOf(tag.getString("rotation")); } catch (IllegalArgumentException var12) { this.rotation = Rotation.NONE; } try { this.mirror = Mirror.valueOf(tag.getString("mirror")); } catch (IllegalArgumentException var11) { this.mirror = Mirror.NONE; } try { this.mode = StructureMode.valueOf(tag.getString("mode")); } catch (IllegalArgumentException var10) { this.mode = StructureMode.DATA; } this.ignoreEntities = tag.getBoolean("ignoreEntities"); this.powered = tag.getBoolean("powered"); this.showAir = tag.getBoolean("showair"); this.showBoundingBox = tag.getBoolean("showboundingbox"); if (tag.contains("integrity")) { this.integrity = tag.getFloat("integrity"); } else { this.integrity = 1.0F; } this.seed = tag.getLong("seed"); this.updateBlockState(); } private void updateBlockState() { if (this.level != null) { BlockPos blockPos = this.getBlockPos(); BlockState blockState = this.level.getBlockState(blockPos); if (blockState.is(Blocks.STRUCTURE_BLOCK)) { this.level.setBlock(blockPos, blockState.setValue(StructureBlock.MODE, this.mode), 2); } } } public ClientboundBlockEntityDataPacket getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); } @Override public CompoundTag getUpdateTag(HolderLookup.Provider registries) { return this.saveCustomOnly(registries); } public boolean usedBy(Player player) { if (!player.canUseGameMasterBlocks()) { return false; } else { if (player.getCommandSenderWorld().isClientSide) { player.openStructureBlock(this); } return true; } } public String getStructureName() { return this.structureName == null ? "" : this.structureName.toString(); } public boolean hasStructureName() { return this.structureName != null; } public void setStructureName(@Nullable String structureName) { this.setStructureName(StringUtil.isNullOrEmpty(structureName) ? null : ResourceLocation.tryParse(structureName)); } public void setStructureName(@Nullable ResourceLocation structureName) { this.structureName = structureName; } public void createdBy(LivingEntity author) { this.author = author.getName().getString(); } public BlockPos getStructurePos() { return this.structurePos; } public void setStructurePos(BlockPos structurePos) { this.structurePos = structurePos; } public Vec3i getStructureSize() { return this.structureSize; } public void setStructureSize(Vec3i structureSize) { this.structureSize = structureSize; } public Mirror getMirror() { return this.mirror; } public void setMirror(Mirror mirror) { this.mirror = mirror; } public Rotation getRotation() { return this.rotation; } public void setRotation(Rotation rotation) { this.rotation = rotation; } public String getMetaData() { return this.metaData; } public void setMetaData(String metaData) { this.metaData = metaData; } public StructureMode getMode() { return this.mode; } public void setMode(StructureMode mode) { this.mode = mode; BlockState blockState = this.level.getBlockState(this.getBlockPos()); if (blockState.is(Blocks.STRUCTURE_BLOCK)) { this.level.setBlock(this.getBlockPos(), blockState.setValue(StructureBlock.MODE, mode), 2); } } public boolean isIgnoreEntities() { return this.ignoreEntities; } public void setIgnoreEntities(boolean ignoreEntities) { this.ignoreEntities = ignoreEntities; } public float getIntegrity() { return this.integrity; } public void setIntegrity(float integrity) { this.integrity = integrity; } public long getSeed() { return this.seed; } public void setSeed(long seed) { this.seed = seed; } public boolean detectSize() { if (this.mode != StructureMode.SAVE) { return false; } else { BlockPos blockPos = this.getBlockPos(); int i = 80; BlockPos blockPos2 = new BlockPos(blockPos.getX() - 80, this.level.getMinY(), blockPos.getZ() - 80); BlockPos blockPos3 = new BlockPos(blockPos.getX() + 80, this.level.getMaxY(), blockPos.getZ() + 80); Stream stream = this.getRelatedCorners(blockPos2, blockPos3); return calculateEnclosingBoundingBox(blockPos, stream) .filter( boundingBox -> { int ix = boundingBox.maxX() - boundingBox.minX(); int j = boundingBox.maxY() - boundingBox.minY(); int k = boundingBox.maxZ() - boundingBox.minZ(); if (ix > 1 && j > 1 && k > 1) { this.structurePos = new BlockPos( boundingBox.minX() - blockPos.getX() + 1, boundingBox.minY() - blockPos.getY() + 1, boundingBox.minZ() - blockPos.getZ() + 1 ); this.structureSize = new Vec3i(ix - 1, j - 1, k - 1); this.setChanged(); BlockState blockState = this.level.getBlockState(blockPos); this.level.sendBlockUpdated(blockPos, blockState, blockState, 3); return true; } else { return false; } } ) .isPresent(); } } private Stream getRelatedCorners(BlockPos minPos, BlockPos maxPos) { return BlockPos.betweenClosedStream(minPos, maxPos) .filter(blockPos -> this.level.getBlockState(blockPos).is(Blocks.STRUCTURE_BLOCK)) .map(this.level::getBlockEntity) .filter(blockEntity -> blockEntity instanceof StructureBlockEntity) .map(blockEntity -> (StructureBlockEntity)blockEntity) .filter(structureBlockEntity -> structureBlockEntity.mode == StructureMode.CORNER && Objects.equals(this.structureName, structureBlockEntity.structureName)) .map(BlockEntity::getBlockPos); } private static Optional calculateEnclosingBoundingBox(BlockPos pos, Stream relatedCorners) { Iterator iterator = relatedCorners.iterator(); if (!iterator.hasNext()) { return Optional.empty(); } else { BlockPos blockPos = (BlockPos)iterator.next(); BoundingBox boundingBox = new BoundingBox(blockPos); if (iterator.hasNext()) { iterator.forEachRemaining(boundingBox::encapsulate); } else { boundingBox.encapsulate(pos); } return Optional.of(boundingBox); } } /** * Saves the template, writing it to disk. * * @return true if the template was successfully saved. */ public boolean saveStructure() { return this.mode != StructureMode.SAVE ? false : this.saveStructure(true); } /** * Saves the template, either updating the local version or writing it to disk. * * @return true if the template was successfully saved. */ public boolean saveStructure(boolean writeToDisk) { if (this.structureName == null) { return false; } else { BlockPos blockPos = this.getBlockPos().offset(this.structurePos); ServerLevel serverLevel = (ServerLevel)this.level; StructureTemplateManager structureTemplateManager = serverLevel.getStructureManager(); StructureTemplate structureTemplate; try { structureTemplate = structureTemplateManager.getOrCreate(this.structureName); } catch (ResourceLocationException var8) { return false; } structureTemplate.fillFromWorld(this.level, blockPos, this.structureSize, !this.ignoreEntities, Blocks.STRUCTURE_VOID); structureTemplate.setAuthor(this.author); if (writeToDisk) { try { return structureTemplateManager.save(this.structureName); } catch (ResourceLocationException var7) { return false; } } else { return true; } } } public static RandomSource createRandom(long seed) { return seed == 0L ? RandomSource.create(Util.getMillis()) : RandomSource.create(seed); } public boolean placeStructureIfSameSize(ServerLevel level) { if (this.mode == StructureMode.LOAD && this.structureName != null) { StructureTemplate structureTemplate = (StructureTemplate)level.getStructureManager().get(this.structureName).orElse(null); if (structureTemplate == null) { return false; } else if (structureTemplate.getSize().equals(this.structureSize)) { this.placeStructure(level, structureTemplate); return true; } else { this.loadStructureInfo(structureTemplate); return false; } } else { return false; } } public boolean loadStructureInfo(ServerLevel level) { StructureTemplate structureTemplate = this.getStructureTemplate(level); if (structureTemplate == null) { return false; } else { this.loadStructureInfo(structureTemplate); return true; } } private void loadStructureInfo(StructureTemplate structureTemplate) { this.author = !StringUtil.isNullOrEmpty(structureTemplate.getAuthor()) ? structureTemplate.getAuthor() : ""; this.structureSize = structureTemplate.getSize(); this.setChanged(); } public void placeStructure(ServerLevel level) { StructureTemplate structureTemplate = this.getStructureTemplate(level); if (structureTemplate != null) { this.placeStructure(level, structureTemplate); } } @Nullable private StructureTemplate getStructureTemplate(ServerLevel level) { return this.structureName == null ? null : (StructureTemplate)level.getStructureManager().get(this.structureName).orElse(null); } private void placeStructure(ServerLevel level, StructureTemplate structureTemplate) { this.loadStructureInfo(structureTemplate); StructurePlaceSettings structurePlaceSettings = new StructurePlaceSettings() .setMirror(this.mirror) .setRotation(this.rotation) .setIgnoreEntities(this.ignoreEntities); if (this.integrity < 1.0F) { structurePlaceSettings.clearProcessors().addProcessor(new BlockRotProcessor(Mth.clamp(this.integrity, 0.0F, 1.0F))).setRandom(createRandom(this.seed)); } BlockPos blockPos = this.getBlockPos().offset(this.structurePos); structureTemplate.placeInWorld(level, blockPos, blockPos, structurePlaceSettings, createRandom(this.seed), 2); } public void unloadStructure() { if (this.structureName != null) { ServerLevel serverLevel = (ServerLevel)this.level; StructureTemplateManager structureTemplateManager = serverLevel.getStructureManager(); structureTemplateManager.remove(this.structureName); } } public boolean isStructureLoadable() { if (this.mode == StructureMode.LOAD && !this.level.isClientSide && this.structureName != null) { ServerLevel serverLevel = (ServerLevel)this.level; StructureTemplateManager structureTemplateManager = serverLevel.getStructureManager(); try { return structureTemplateManager.get(this.structureName).isPresent(); } catch (ResourceLocationException var4) { return false; } } else { return false; } } public boolean isPowered() { return this.powered; } public void setPowered(boolean powered) { this.powered = powered; } public boolean getShowAir() { return this.showAir; } public void setShowAir(boolean showAir) { this.showAir = showAir; } public boolean getShowBoundingBox() { return this.showBoundingBox; } public void setShowBoundingBox(boolean showBoundingBox) { this.showBoundingBox = showBoundingBox; } public static enum UpdateType { UPDATE_DATA, SAVE_AREA, LOAD_AREA, SCAN_AREA; } }