minecraft-src/net/minecraft/world/level/block/entity/StructureBlockEntity.java
2025-07-04 03:15:13 +03:00

476 lines
15 KiB
Java

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.Vec3i;
import net.minecraft.core.HolderLookup.Provider;
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, 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, 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(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<BlockPos> 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<BlockPos> 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<BoundingBox> calculateEnclosingBoundingBox(BlockPos pos, Stream<BlockPos> relatedCorners) {
Iterator<BlockPos> 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;
}
}