322 lines
12 KiB
Java
322 lines
12 KiB
Java
package net.minecraft.world.level.block.entity;
|
|
|
|
import com.mojang.logging.LogUtils;
|
|
import com.mojang.serialization.Codec;
|
|
import com.mojang.serialization.MapCodec;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.CrashReportCategory;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.HolderLookup;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.core.component.DataComponentGetter;
|
|
import net.minecraft.core.component.DataComponentMap;
|
|
import net.minecraft.core.component.DataComponentPatch;
|
|
import net.minecraft.core.component.DataComponentType;
|
|
import net.minecraft.core.component.DataComponents;
|
|
import net.minecraft.core.component.PatchedDataComponentMap;
|
|
import net.minecraft.core.registries.BuiltInRegistries;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.NbtOps;
|
|
import net.minecraft.nbt.Tag;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.chat.ComponentSerialization;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
|
import net.minecraft.world.Container;
|
|
import net.minecraft.world.Containers;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import org.slf4j.Logger;
|
|
|
|
public abstract class BlockEntity {
|
|
private static final Codec<BlockEntityType<?>> TYPE_CODEC = BuiltInRegistries.BLOCK_ENTITY_TYPE.byNameCodec();
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private final BlockEntityType<?> type;
|
|
@Nullable
|
|
protected Level level;
|
|
protected final BlockPos worldPosition;
|
|
protected boolean remove;
|
|
private BlockState blockState;
|
|
private DataComponentMap components = DataComponentMap.EMPTY;
|
|
|
|
public BlockEntity(BlockEntityType<?> p_155228_, BlockPos p_155229_, BlockState p_155230_) {
|
|
this.type = p_155228_;
|
|
this.worldPosition = p_155229_.immutable();
|
|
this.validateBlockState(p_155230_);
|
|
this.blockState = p_155230_;
|
|
}
|
|
|
|
private void validateBlockState(BlockState p_345558_) {
|
|
if (!this.isValidBlockState(p_345558_)) {
|
|
throw new IllegalStateException("Invalid block entity " + this.getNameForReporting() + " state at " + this.worldPosition + ", got " + p_345558_);
|
|
}
|
|
}
|
|
|
|
public boolean isValidBlockState(BlockState p_345570_) {
|
|
return this.type.isValid(p_345570_);
|
|
}
|
|
|
|
public static BlockPos getPosFromTag(ChunkPos p_396083_, CompoundTag p_187473_) {
|
|
int i = p_187473_.getIntOr("x", 0);
|
|
int j = p_187473_.getIntOr("y", 0);
|
|
int k = p_187473_.getIntOr("z", 0);
|
|
int l = SectionPos.blockToSectionCoord(i);
|
|
int i1 = SectionPos.blockToSectionCoord(k);
|
|
if (l != p_396083_.x || i1 != p_396083_.z) {
|
|
LOGGER.warn("Block entity {} found in a wrong chunk, expected position from chunk {}", p_187473_, p_396083_);
|
|
i = p_396083_.getBlockX(SectionPos.sectionRelative(i));
|
|
k = p_396083_.getBlockZ(SectionPos.sectionRelative(k));
|
|
}
|
|
|
|
return new BlockPos(i, j, k);
|
|
}
|
|
|
|
@Nullable
|
|
public Level getLevel() {
|
|
return this.level;
|
|
}
|
|
|
|
public void setLevel(Level p_155231_) {
|
|
this.level = p_155231_;
|
|
}
|
|
|
|
public boolean hasLevel() {
|
|
return this.level != null;
|
|
}
|
|
|
|
protected void loadAdditional(CompoundTag p_331149_, HolderLookup.Provider p_333170_) {
|
|
}
|
|
|
|
public final void loadWithComponents(CompoundTag p_331756_, HolderLookup.Provider p_335164_) {
|
|
this.loadAdditional(p_331756_, p_335164_);
|
|
this.components = p_331756_.read(BlockEntity.ComponentHelper.COMPONENTS_CODEC, p_335164_.createSerializationContext(NbtOps.INSTANCE)).orElse(DataComponentMap.EMPTY);
|
|
}
|
|
|
|
public final void loadCustomOnly(CompoundTag p_333694_, HolderLookup.Provider p_332017_) {
|
|
this.loadAdditional(p_333694_, p_332017_);
|
|
}
|
|
|
|
protected void saveAdditional(CompoundTag p_187471_, HolderLookup.Provider p_327783_) {
|
|
}
|
|
|
|
public final CompoundTag saveWithFullMetadata(HolderLookup.Provider p_331193_) {
|
|
CompoundTag compoundtag = this.saveWithoutMetadata(p_331193_);
|
|
this.saveMetadata(compoundtag);
|
|
return compoundtag;
|
|
}
|
|
|
|
public final CompoundTag saveWithId(HolderLookup.Provider p_332686_) {
|
|
CompoundTag compoundtag = this.saveWithoutMetadata(p_332686_);
|
|
this.saveId(compoundtag);
|
|
return compoundtag;
|
|
}
|
|
|
|
public final CompoundTag saveWithoutMetadata(HolderLookup.Provider p_332372_) {
|
|
CompoundTag compoundtag = new CompoundTag();
|
|
this.saveAdditional(compoundtag, p_332372_);
|
|
compoundtag.store(BlockEntity.ComponentHelper.COMPONENTS_CODEC, p_332372_.createSerializationContext(NbtOps.INSTANCE), this.components);
|
|
return compoundtag;
|
|
}
|
|
|
|
public final CompoundTag saveCustomOnly(HolderLookup.Provider p_333091_) {
|
|
CompoundTag compoundtag = new CompoundTag();
|
|
this.saveAdditional(compoundtag, p_333091_);
|
|
return compoundtag;
|
|
}
|
|
|
|
public final CompoundTag saveCustomAndMetadata(HolderLookup.Provider p_334487_) {
|
|
CompoundTag compoundtag = this.saveCustomOnly(p_334487_);
|
|
this.saveMetadata(compoundtag);
|
|
return compoundtag;
|
|
}
|
|
|
|
private void saveId(CompoundTag p_187475_) {
|
|
addEntityType(p_187475_, this.getType());
|
|
}
|
|
|
|
public static void addEntityType(CompoundTag p_187469_, BlockEntityType<?> p_187470_) {
|
|
p_187469_.store("id", TYPE_CODEC, p_187470_);
|
|
}
|
|
|
|
private void saveMetadata(CompoundTag p_187479_) {
|
|
this.saveId(p_187479_);
|
|
p_187479_.putInt("x", this.worldPosition.getX());
|
|
p_187479_.putInt("y", this.worldPosition.getY());
|
|
p_187479_.putInt("z", this.worldPosition.getZ());
|
|
}
|
|
|
|
@Nullable
|
|
public static BlockEntity loadStatic(BlockPos p_155242_, BlockState p_155243_, CompoundTag p_155244_, HolderLookup.Provider p_336084_) {
|
|
BlockEntityType<?> blockentitytype = p_155244_.read("id", TYPE_CODEC).orElse(null);
|
|
if (blockentitytype == null) {
|
|
LOGGER.error("Skipping block entity with invalid type: {}", p_155244_.get("id"));
|
|
return null;
|
|
} else {
|
|
BlockEntity blockentity;
|
|
try {
|
|
blockentity = blockentitytype.create(p_155242_, p_155243_);
|
|
} catch (Throwable throwable1) {
|
|
LOGGER.error("Failed to create block entity {} for block {} at position {} ", blockentitytype, p_155242_, p_155243_, throwable1);
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
blockentity.loadWithComponents(p_155244_, p_336084_);
|
|
return blockentity;
|
|
} catch (Throwable throwable) {
|
|
LOGGER.error("Failed to load data for block entity {} for block {} at position {}", blockentitytype, p_155242_, p_155243_, throwable);
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setChanged() {
|
|
if (this.level != null) {
|
|
setChanged(this.level, this.worldPosition, this.blockState);
|
|
}
|
|
}
|
|
|
|
protected static void setChanged(Level p_155233_, BlockPos p_155234_, BlockState p_155235_) {
|
|
p_155233_.blockEntityChanged(p_155234_);
|
|
if (!p_155235_.isAir()) {
|
|
p_155233_.updateNeighbourForOutputSignal(p_155234_, p_155235_.getBlock());
|
|
}
|
|
}
|
|
|
|
public BlockPos getBlockPos() {
|
|
return this.worldPosition;
|
|
}
|
|
|
|
public BlockState getBlockState() {
|
|
return this.blockState;
|
|
}
|
|
|
|
@Nullable
|
|
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
|
return null;
|
|
}
|
|
|
|
public CompoundTag getUpdateTag(HolderLookup.Provider p_329179_) {
|
|
return new CompoundTag();
|
|
}
|
|
|
|
public boolean isRemoved() {
|
|
return this.remove;
|
|
}
|
|
|
|
public void setRemoved() {
|
|
this.remove = true;
|
|
}
|
|
|
|
public void clearRemoved() {
|
|
this.remove = false;
|
|
}
|
|
|
|
public void preRemoveSideEffects(BlockPos p_397404_, BlockState p_395805_) {
|
|
if (this instanceof Container container && this.level != null) {
|
|
Containers.dropContents(this.level, p_397404_, container);
|
|
}
|
|
}
|
|
|
|
public boolean triggerEvent(int p_58889_, int p_58890_) {
|
|
return false;
|
|
}
|
|
|
|
public void fillCrashReportCategory(CrashReportCategory p_58887_) {
|
|
p_58887_.setDetail("Name", this::getNameForReporting);
|
|
p_58887_.setDetail("Cached block", this.getBlockState()::toString);
|
|
if (this.level == null) {
|
|
p_58887_.setDetail("Block location", () -> this.worldPosition + " (world missing)");
|
|
} else {
|
|
p_58887_.setDetail("Actual block", this.level.getBlockState(this.worldPosition)::toString);
|
|
CrashReportCategory.populateBlockLocationDetails(p_58887_, this.level, this.worldPosition);
|
|
}
|
|
}
|
|
|
|
private String getNameForReporting() {
|
|
return BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(this.getType()) + " // " + this.getClass().getCanonicalName();
|
|
}
|
|
|
|
public BlockEntityType<?> getType() {
|
|
return this.type;
|
|
}
|
|
|
|
@Deprecated
|
|
public void setBlockState(BlockState p_155251_) {
|
|
this.validateBlockState(p_155251_);
|
|
this.blockState = p_155251_;
|
|
}
|
|
|
|
protected void applyImplicitComponents(DataComponentGetter p_391290_) {
|
|
}
|
|
|
|
public final void applyComponentsFromItemStack(ItemStack p_328941_) {
|
|
this.applyComponents(p_328941_.getPrototype(), p_328941_.getComponentsPatch());
|
|
}
|
|
|
|
public final void applyComponents(DataComponentMap p_335232_, DataComponentPatch p_331646_) {
|
|
final Set<DataComponentType<?>> set = new HashSet<>();
|
|
set.add(DataComponents.BLOCK_ENTITY_DATA);
|
|
set.add(DataComponents.BLOCK_STATE);
|
|
final DataComponentMap datacomponentmap = PatchedDataComponentMap.fromPatch(p_335232_, p_331646_);
|
|
this.applyImplicitComponents(new DataComponentGetter() {
|
|
@Nullable
|
|
@Override
|
|
public <T> T get(DataComponentType<? extends T> p_335233_) {
|
|
set.add(p_335233_);
|
|
return datacomponentmap.get(p_335233_);
|
|
}
|
|
|
|
@Override
|
|
public <T> T getOrDefault(DataComponentType<? extends T> p_334887_, T p_333244_) {
|
|
set.add(p_334887_);
|
|
return datacomponentmap.getOrDefault(p_334887_, p_333244_);
|
|
}
|
|
});
|
|
DataComponentPatch datacomponentpatch = p_331646_.forget(set::contains);
|
|
this.components = datacomponentpatch.split().added();
|
|
}
|
|
|
|
protected void collectImplicitComponents(DataComponentMap.Builder p_328216_) {
|
|
}
|
|
|
|
@Deprecated
|
|
public void removeComponentsFromTag(CompoundTag p_334718_) {
|
|
}
|
|
|
|
public final DataComponentMap collectComponents() {
|
|
DataComponentMap.Builder datacomponentmap$builder = DataComponentMap.builder();
|
|
datacomponentmap$builder.addAll(this.components);
|
|
this.collectImplicitComponents(datacomponentmap$builder);
|
|
return datacomponentmap$builder.build();
|
|
}
|
|
|
|
public DataComponentMap components() {
|
|
return this.components;
|
|
}
|
|
|
|
public void setComponents(DataComponentMap p_335672_) {
|
|
this.components = p_335672_;
|
|
}
|
|
|
|
@Nullable
|
|
public static Component parseCustomNameSafe(@Nullable Tag p_393442_, HolderLookup.Provider p_336417_) {
|
|
return p_393442_ == null
|
|
? null
|
|
: ComponentSerialization.CODEC
|
|
.parse(p_336417_.createSerializationContext(NbtOps.INSTANCE), p_393442_)
|
|
.resultOrPartial(p_327293_ -> LOGGER.warn("Failed to parse custom name, discarding: {}", p_327293_))
|
|
.orElse(null);
|
|
}
|
|
|
|
static class ComponentHelper {
|
|
public static final MapCodec<DataComponentMap> COMPONENTS_CODEC = DataComponentMap.CODEC.optionalFieldOf("components", DataComponentMap.EMPTY);
|
|
|
|
private ComponentHelper() {
|
|
}
|
|
}
|
|
} |