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

553 lines
18 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.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 implements BoundingBoxRenderable {
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";
private static final String DEFAULT_AUTHOR = "";
private static final String DEFAULT_METADATA = "";
private static final BlockPos DEFAULT_POS = new BlockPos(0, 1, 0);
private static final Vec3i DEFAULT_SIZE = Vec3i.ZERO;
private static final Rotation DEFAULT_ROTATION = Rotation.NONE;
private static final Mirror DEFAULT_MIRROR = Mirror.NONE;
private static final boolean DEFAULT_IGNORE_ENTITIES = true;
private static final boolean DEFAULT_STRICT = false;
private static final boolean DEFAULT_POWERED = false;
private static final boolean DEFAULT_SHOW_AIR = false;
private static final boolean DEFAULT_SHOW_BOUNDING_BOX = true;
private static final float DEFAULT_INTEGRITY = 1.0F;
private static final long DEFAULT_SEED = 0L;
@Nullable
private ResourceLocation structureName;
private String author = "";
private String metaData = "";
private BlockPos structurePos = DEFAULT_POS;
private Vec3i structureSize = DEFAULT_SIZE;
private Mirror mirror = Mirror.NONE;
private Rotation rotation = Rotation.NONE;
private StructureMode mode;
private boolean ignoreEntities = true;
private boolean strict = false;
private boolean powered = false;
private boolean showAir = false;
private boolean showBoundingBox = true;
private float integrity = 1.0F;
private long seed = 0L;
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.store("rotation", Rotation.LEGACY_CODEC, this.rotation);
tag.store("mirror", Mirror.LEGACY_CODEC, this.mirror);
tag.store("mode", StructureMode.LEGACY_CODEC, this.mode);
tag.putBoolean("ignoreEntities", this.ignoreEntities);
tag.putBoolean("strict", this.strict);
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.getStringOr("name", ""));
this.author = tag.getStringOr("author", "");
this.metaData = tag.getStringOr("metadata", "");
int i = Mth.clamp(tag.getIntOr("posX", DEFAULT_POS.getX()), -48, 48);
int j = Mth.clamp(tag.getIntOr("posY", DEFAULT_POS.getY()), -48, 48);
int k = Mth.clamp(tag.getIntOr("posZ", DEFAULT_POS.getZ()), -48, 48);
this.structurePos = new BlockPos(i, j, k);
int l = Mth.clamp(tag.getIntOr("sizeX", DEFAULT_SIZE.getX()), 0, 48);
int m = Mth.clamp(tag.getIntOr("sizeY", DEFAULT_SIZE.getY()), 0, 48);
int n = Mth.clamp(tag.getIntOr("sizeZ", DEFAULT_SIZE.getZ()), 0, 48);
this.structureSize = new Vec3i(l, m, n);
this.rotation = (Rotation)tag.read("rotation", Rotation.LEGACY_CODEC).orElse(DEFAULT_ROTATION);
this.mirror = (Mirror)tag.read("mirror", Mirror.LEGACY_CODEC).orElse(DEFAULT_MIRROR);
this.mode = (StructureMode)tag.read("mode", StructureMode.LEGACY_CODEC).orElse(StructureMode.DATA);
this.ignoreEntities = tag.getBooleanOr("ignoreEntities", true);
this.strict = tag.getBooleanOr("strict", false);
this.powered = tag.getBooleanOr("powered", false);
this.showAir = tag.getBooleanOr("showair", false);
this.showBoundingBox = tag.getBooleanOr("showboundingbox", true);
this.integrity = tag.getFloatOr("integrity", 1.0F);
this.seed = tag.getLongOr("seed", 0L);
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 boolean isStrict() {
return this.strict;
}
public void setIgnoreEntities(boolean ignoreEntities) {
this.ignoreEntities = ignoreEntities;
}
public void setStrict(boolean strict) {
this.strict = strict;
}
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 && this.level instanceof ServerLevel serverLevel) {
BlockPos var4 = this.getBlockPos().offset(this.structurePos);
return saveStructure(serverLevel, this.structureName, var4, this.structureSize, this.ignoreEntities, this.author, writeToDisk);
} else {
return false;
}
}
public static boolean saveStructure(
ServerLevel level, ResourceLocation name, BlockPos pos, Vec3i size, boolean ignoreEntities, String author, boolean writeToDisk
) {
StructureTemplateManager structureTemplateManager = level.getStructureManager();
StructureTemplate structureTemplate;
try {
structureTemplate = structureTemplateManager.getOrCreate(name);
} catch (ResourceLocationException var11) {
return false;
}
structureTemplate.fillFromWorld(level, pos, size, !ignoreEntities, Blocks.STRUCTURE_VOID);
structureTemplate.setAuthor(author);
if (writeToDisk) {
try {
return structureTemplateManager.save(name);
} catch (ResourceLocationException var10) {
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)
.setKnownShape(this.strict);
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 | (this.strict ? 816 : 0));
}
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;
}
@Override
public BoundingBoxRenderable.Mode renderMode() {
if (this.mode != StructureMode.SAVE && this.mode != StructureMode.LOAD) {
return BoundingBoxRenderable.Mode.NONE;
} else if (this.mode == StructureMode.SAVE && this.showAir) {
return BoundingBoxRenderable.Mode.BOX_AND_INVISIBLE_BLOCKS;
} else {
return this.mode != StructureMode.SAVE && !this.showBoundingBox ? BoundingBoxRenderable.Mode.NONE : BoundingBoxRenderable.Mode.BOX;
}
}
@Override
public BoundingBoxRenderable.RenderableBox getRenderableBox() {
BlockPos blockPos = this.getStructurePos();
Vec3i vec3i = this.getStructureSize();
int i = blockPos.getX();
int j = blockPos.getZ();
int k = blockPos.getY();
int l = k + vec3i.getY();
int m;
int n;
switch (this.mirror) {
case LEFT_RIGHT:
m = vec3i.getX();
n = -vec3i.getZ();
break;
case FRONT_BACK:
m = -vec3i.getX();
n = vec3i.getZ();
break;
default:
m = vec3i.getX();
n = vec3i.getZ();
}
int o;
int p;
int q;
int r;
switch (this.rotation) {
case CLOCKWISE_90:
o = n < 0 ? i : i + 1;
p = m < 0 ? j + 1 : j;
q = o - n;
r = p + m;
break;
case CLOCKWISE_180:
o = m < 0 ? i : i + 1;
p = n < 0 ? j : j + 1;
q = o - m;
r = p - n;
break;
case COUNTERCLOCKWISE_90:
o = n < 0 ? i + 1 : i;
p = m < 0 ? j : j + 1;
q = o + n;
r = p - m;
break;
default:
o = m < 0 ? i + 1 : i;
p = n < 0 ? j + 1 : j;
q = o + m;
r = p + n;
}
return BoundingBoxRenderable.RenderableBox.fromCorners(o, k, p, q, l, r);
}
public static enum UpdateType {
UPDATE_DATA,
SAVE_AREA,
LOAD_AREA,
SCAN_AREA;
}
}