Code/net/minecraft/world/level/block/entity/StructureBlockEntity.java

545 lines
20 KiB
Java
Raw Permalink Normal View History

2025-07-01 06:20:03 +00:00
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 javax.annotation.Nullable;
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;
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 p_155779_, BlockState p_155780_) {
super(BlockEntityType.STRUCTURE_BLOCK, p_155779_, p_155780_);
this.mode = p_155780_.getValue(StructureBlock.MODE);
}
@Override
protected void saveAdditional(CompoundTag p_187524_, HolderLookup.Provider p_331199_) {
super.saveAdditional(p_187524_, p_331199_);
p_187524_.putString("name", this.getStructureName());
p_187524_.putString("author", this.author);
p_187524_.putString("metadata", this.metaData);
p_187524_.putInt("posX", this.structurePos.getX());
p_187524_.putInt("posY", this.structurePos.getY());
p_187524_.putInt("posZ", this.structurePos.getZ());
p_187524_.putInt("sizeX", this.structureSize.getX());
p_187524_.putInt("sizeY", this.structureSize.getY());
p_187524_.putInt("sizeZ", this.structureSize.getZ());
p_187524_.store("rotation", Rotation.LEGACY_CODEC, this.rotation);
p_187524_.store("mirror", Mirror.LEGACY_CODEC, this.mirror);
p_187524_.store("mode", StructureMode.LEGACY_CODEC, this.mode);
p_187524_.putBoolean("ignoreEntities", this.ignoreEntities);
p_187524_.putBoolean("strict", this.strict);
p_187524_.putBoolean("powered", this.powered);
p_187524_.putBoolean("showair", this.showAir);
p_187524_.putBoolean("showboundingbox", this.showBoundingBox);
p_187524_.putFloat("integrity", this.integrity);
p_187524_.putLong("seed", this.seed);
}
@Override
protected void loadAdditional(CompoundTag p_332447_, HolderLookup.Provider p_334283_) {
super.loadAdditional(p_332447_, p_334283_);
this.setStructureName(p_332447_.getStringOr("name", ""));
this.author = p_332447_.getStringOr("author", "");
this.metaData = p_332447_.getStringOr("metadata", "");
int i = Mth.clamp(p_332447_.getIntOr("posX", DEFAULT_POS.getX()), -48, 48);
int j = Mth.clamp(p_332447_.getIntOr("posY", DEFAULT_POS.getY()), -48, 48);
int k = Mth.clamp(p_332447_.getIntOr("posZ", DEFAULT_POS.getZ()), -48, 48);
this.structurePos = new BlockPos(i, j, k);
int l = Mth.clamp(p_332447_.getIntOr("sizeX", DEFAULT_SIZE.getX()), 0, 48);
int i1 = Mth.clamp(p_332447_.getIntOr("sizeY", DEFAULT_SIZE.getY()), 0, 48);
int j1 = Mth.clamp(p_332447_.getIntOr("sizeZ", DEFAULT_SIZE.getZ()), 0, 48);
this.structureSize = new Vec3i(l, i1, j1);
this.rotation = p_332447_.read("rotation", Rotation.LEGACY_CODEC).orElse(DEFAULT_ROTATION);
this.mirror = p_332447_.read("mirror", Mirror.LEGACY_CODEC).orElse(DEFAULT_MIRROR);
this.mode = p_332447_.read("mode", StructureMode.LEGACY_CODEC).orElse(StructureMode.DATA);
this.ignoreEntities = p_332447_.getBooleanOr("ignoreEntities", true);
this.strict = p_332447_.getBooleanOr("strict", false);
this.powered = p_332447_.getBooleanOr("powered", false);
this.showAir = p_332447_.getBooleanOr("showair", false);
this.showBoundingBox = p_332447_.getBooleanOr("showboundingbox", true);
this.integrity = p_332447_.getFloatOr("integrity", 1.0F);
this.seed = p_332447_.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 p_327713_) {
return this.saveCustomOnly(p_327713_);
}
public boolean usedBy(Player p_59854_) {
if (!p_59854_.canUseGameMasterBlocks()) {
return false;
} else {
if (p_59854_.getCommandSenderWorld().isClientSide) {
p_59854_.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 p_59869_) {
this.setStructureName(StringUtil.isNullOrEmpty(p_59869_) ? null : ResourceLocation.tryParse(p_59869_));
}
public void setStructureName(@Nullable ResourceLocation p_59875_) {
this.structureName = p_59875_;
}
public void createdBy(LivingEntity p_59852_) {
this.author = p_59852_.getName().getString();
}
public BlockPos getStructurePos() {
return this.structurePos;
}
public void setStructurePos(BlockPos p_59886_) {
this.structurePos = p_59886_;
}
public Vec3i getStructureSize() {
return this.structureSize;
}
public void setStructureSize(Vec3i p_155798_) {
this.structureSize = p_155798_;
}
public Mirror getMirror() {
return this.mirror;
}
public void setMirror(Mirror p_59882_) {
this.mirror = p_59882_;
}
public Rotation getRotation() {
return this.rotation;
}
public void setRotation(Rotation p_59884_) {
this.rotation = p_59884_;
}
public String getMetaData() {
return this.metaData;
}
public void setMetaData(String p_59888_) {
this.metaData = p_59888_;
}
public StructureMode getMode() {
return this.mode;
}
public void setMode(StructureMode p_59861_) {
this.mode = p_59861_;
BlockState blockstate = this.level.getBlockState(this.getBlockPos());
if (blockstate.is(Blocks.STRUCTURE_BLOCK)) {
this.level.setBlock(this.getBlockPos(), blockstate.setValue(StructureBlock.MODE, p_59861_), 2);
}
}
public boolean isIgnoreEntities() {
return this.ignoreEntities;
}
public boolean isStrict() {
return this.strict;
}
public void setIgnoreEntities(boolean p_59877_) {
this.ignoreEntities = p_59877_;
}
public void setStrict(boolean p_394300_) {
this.strict = p_394300_;
}
public float getIntegrity() {
return this.integrity;
}
public void setIntegrity(float p_59839_) {
this.integrity = p_59839_;
}
public long getSeed() {
return this.seed;
}
public void setSeed(long p_59841_) {
this.seed = p_59841_;
}
public boolean detectSize() {
if (this.mode != StructureMode.SAVE) {
return false;
} else {
BlockPos blockpos = this.getBlockPos();
int i = 80;
BlockPos blockpos1 = new BlockPos(blockpos.getX() - 80, this.level.getMinY(), blockpos.getZ() - 80);
BlockPos blockpos2 = new BlockPos(blockpos.getX() + 80, this.level.getMaxY(), blockpos.getZ() + 80);
Stream<BlockPos> stream = this.getRelatedCorners(blockpos1, blockpos2);
return calculateEnclosingBoundingBox(blockpos, stream)
.filter(
p_155790_ -> {
int j = p_155790_.maxX() - p_155790_.minX();
int k = p_155790_.maxY() - p_155790_.minY();
int l = p_155790_.maxZ() - p_155790_.minZ();
if (j > 1 && k > 1 && l > 1) {
this.structurePos = new BlockPos(
p_155790_.minX() - blockpos.getX() + 1,
p_155790_.minY() - blockpos.getY() + 1,
p_155790_.minZ() - blockpos.getZ() + 1
);
this.structureSize = new Vec3i(j - 1, k - 1, l - 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 p_155792_, BlockPos p_155793_) {
return BlockPos.betweenClosedStream(p_155792_, p_155793_)
.filter(p_272561_ -> this.level.getBlockState(p_272561_).is(Blocks.STRUCTURE_BLOCK))
.map(this.level::getBlockEntity)
.filter(p_155802_ -> p_155802_ instanceof StructureBlockEntity)
.map(p_155785_ -> (StructureBlockEntity)p_155785_)
.filter(p_155787_ -> p_155787_.mode == StructureMode.CORNER && Objects.equals(this.structureName, p_155787_.structureName))
.map(BlockEntity::getBlockPos);
}
private static Optional<BoundingBox> calculateEnclosingBoundingBox(BlockPos p_155795_, Stream<BlockPos> p_155796_) {
Iterator<BlockPos> iterator = p_155796_.iterator();
if (!iterator.hasNext()) {
return Optional.empty();
} else {
BlockPos blockpos = iterator.next();
BoundingBox boundingbox = new BoundingBox(blockpos);
if (iterator.hasNext()) {
iterator.forEachRemaining(boundingbox::encapsulate);
} else {
boundingbox.encapsulate(p_155795_);
}
return Optional.of(boundingbox);
}
}
public boolean saveStructure() {
return this.mode != StructureMode.SAVE ? false : this.saveStructure(true);
}
public boolean saveStructure(boolean p_59890_) {
if (this.structureName != null && this.level instanceof ServerLevel serverlevel) {
BlockPos blockpos = this.getBlockPos().offset(this.structurePos);
return saveStructure(serverlevel, this.structureName, blockpos, this.structureSize, this.ignoreEntities, this.author, p_59890_);
} else {
return false;
}
}
public static boolean saveStructure(
ServerLevel p_394863_, ResourceLocation p_392409_, BlockPos p_397553_, Vec3i p_395544_, boolean p_397953_, String p_394234_, boolean p_396341_
) {
StructureTemplateManager structuretemplatemanager = p_394863_.getStructureManager();
StructureTemplate structuretemplate;
try {
structuretemplate = structuretemplatemanager.getOrCreate(p_392409_);
} catch (ResourceLocationException resourcelocationexception1) {
return false;
}
structuretemplate.fillFromWorld(p_394863_, p_397553_, p_395544_, !p_397953_, Blocks.STRUCTURE_VOID);
structuretemplate.setAuthor(p_394234_);
if (p_396341_) {
try {
return structuretemplatemanager.save(p_392409_);
} catch (ResourceLocationException resourcelocationexception) {
return false;
}
} else {
return true;
}
}
public static RandomSource createRandom(long p_222889_) {
return p_222889_ == 0L ? RandomSource.create(Util.getMillis()) : RandomSource.create(p_222889_);
}
public boolean placeStructureIfSameSize(ServerLevel p_310062_) {
if (this.mode == StructureMode.LOAD && this.structureName != null) {
StructureTemplate structuretemplate = p_310062_.getStructureManager().get(this.structureName).orElse(null);
if (structuretemplate == null) {
return false;
} else if (structuretemplate.getSize().equals(this.structureSize)) {
this.placeStructure(p_310062_, structuretemplate);
return true;
} else {
this.loadStructureInfo(structuretemplate);
return false;
}
} else {
return false;
}
}
public boolean loadStructureInfo(ServerLevel p_312602_) {
StructureTemplate structuretemplate = this.getStructureTemplate(p_312602_);
if (structuretemplate == null) {
return false;
} else {
this.loadStructureInfo(structuretemplate);
return true;
}
}
private void loadStructureInfo(StructureTemplate p_311753_) {
this.author = !StringUtil.isNullOrEmpty(p_311753_.getAuthor()) ? p_311753_.getAuthor() : "";
this.structureSize = p_311753_.getSize();
this.setChanged();
}
public void placeStructure(ServerLevel p_312292_) {
StructureTemplate structuretemplate = this.getStructureTemplate(p_312292_);
if (structuretemplate != null) {
this.placeStructure(p_312292_, structuretemplate);
}
}
@Nullable
private StructureTemplate getStructureTemplate(ServerLevel p_310290_) {
return this.structureName == null ? null : p_310290_.getStructureManager().get(this.structureName).orElse(null);
}
private void placeStructure(ServerLevel p_311121_, StructureTemplate p_312324_) {
this.loadStructureInfo(p_312324_);
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);
p_312324_.placeInWorld(p_311121_, 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 resourcelocationexception) {
return false;
}
} else {
return false;
}
}
public boolean isPowered() {
return this.powered;
}
public void setPowered(boolean p_59894_) {
this.powered = p_59894_;
}
public boolean getShowAir() {
return this.showAir;
}
public void setShowAir(boolean p_59897_) {
this.showAir = p_59897_;
}
public boolean getShowBoundingBox() {
return this.showBoundingBox;
}
public void setShowBoundingBox(boolean p_59899_) {
this.showBoundingBox = p_59899_;
}
@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 j1 = blockpos.getY();
int i2 = j1 + vec3i.getY();
int k;
int l;
switch (this.mirror) {
case LEFT_RIGHT:
k = vec3i.getX();
l = -vec3i.getZ();
break;
case FRONT_BACK:
k = -vec3i.getX();
l = vec3i.getZ();
break;
default:
k = vec3i.getX();
l = vec3i.getZ();
}
int i1;
int k1;
int l1;
int j2;
switch (this.rotation) {
case CLOCKWISE_90:
i1 = l < 0 ? i : i + 1;
k1 = k < 0 ? j + 1 : j;
l1 = i1 - l;
j2 = k1 + k;
break;
case CLOCKWISE_180:
i1 = k < 0 ? i : i + 1;
k1 = l < 0 ? j : j + 1;
l1 = i1 - k;
j2 = k1 - l;
break;
case COUNTERCLOCKWISE_90:
i1 = l < 0 ? i + 1 : i;
k1 = k < 0 ? j : j + 1;
l1 = i1 + l;
j2 = k1 - k;
break;
default:
i1 = k < 0 ? i + 1 : i;
k1 = l < 0 ? j + 1 : j;
l1 = i1 + k;
j2 = k1 + l;
}
return BoundingBoxRenderable.RenderableBox.fromCorners(i1, j1, k1, l1, i2, j2);
}
public static enum UpdateType {
UPDATE_DATA,
SAVE_AREA,
LOAD_AREA,
SCAN_AREA;
}
}