package net.minecraft.world.level.chunk; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.util.Collections; import java.util.Map; import java.util.Map.Entry; import java.util.function.Consumer; import java.util.function.Supplier; import javax.annotation.Nullable; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.server.level.FullChunkStatus; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseRailBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.TickingBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.gameevent.EuclideanGameEventListenerRegistry; import net.minecraft.world.level.gameevent.GameEventListener; import net.minecraft.world.level.gameevent.GameEventListenerRegistry; import net.minecraft.world.level.levelgen.DebugLevelSource; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.blending.BlendingData; import net.minecraft.world.level.lighting.LightEngine; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.ticks.LevelChunkTicks; import net.minecraft.world.ticks.TickContainerAccess; import org.slf4j.Logger; public class LevelChunk extends ChunkAccess { static final Logger LOGGER = LogUtils.getLogger(); private static final TickingBlockEntity NULL_TICKER = new TickingBlockEntity() { @Override public void tick() { } @Override public boolean isRemoved() { return true; } @Override public BlockPos getPos() { return BlockPos.ZERO; } @Override public String getType() { return ""; } }; private final Map tickersInLevel = Maps.newHashMap(); private boolean loaded; final Level level; @Nullable private Supplier fullStatus; @Nullable private LevelChunk.PostLoadProcessor postLoad; private final Int2ObjectMap gameEventListenerRegistrySections; private final LevelChunkTicks blockTicks; private final LevelChunkTicks fluidTicks; private LevelChunk.UnsavedListener unsavedListener = p_360556_ -> {}; public LevelChunk(Level p_187945_, ChunkPos p_187946_) { this(p_187945_, p_187946_, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null); } public LevelChunk( Level p_196854_, ChunkPos p_196855_, UpgradeData p_196856_, LevelChunkTicks p_196857_, LevelChunkTicks p_196858_, long p_196859_, @Nullable LevelChunkSection[] p_196860_, @Nullable LevelChunk.PostLoadProcessor p_196861_, @Nullable BlendingData p_196862_ ) { super(p_196855_, p_196856_, p_196854_, p_196854_.registryAccess().lookupOrThrow(Registries.BIOME), p_196859_, p_196860_, p_196862_); this.level = p_196854_; this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>(); for (Heightmap.Types heightmap$types : Heightmap.Types.values()) { if (ChunkStatus.FULL.heightmapsAfter().contains(heightmap$types)) { this.heightmaps.put(heightmap$types, new Heightmap(this, heightmap$types)); } } this.postLoad = p_196861_; this.blockTicks = p_196857_; this.fluidTicks = p_196858_; } public LevelChunk(ServerLevel p_196850_, ProtoChunk p_196851_, @Nullable LevelChunk.PostLoadProcessor p_196852_) { this( p_196850_, p_196851_.getPos(), p_196851_.getUpgradeData(), p_196851_.unpackBlockTicks(), p_196851_.unpackFluidTicks(), p_196851_.getInhabitedTime(), p_196851_.getSections(), p_196852_, p_196851_.getBlendingData() ); if (!Collections.disjoint(p_196851_.pendingBlockEntities.keySet(), p_196851_.blockEntities.keySet())) { LOGGER.error("Chunk at {} contains duplicated block entities", p_196851_.getPos()); } for (BlockEntity blockentity : p_196851_.getBlockEntities().values()) { this.setBlockEntity(blockentity); } this.pendingBlockEntities.putAll(p_196851_.getBlockEntityNbts()); for (int i = 0; i < p_196851_.getPostProcessing().length; i++) { this.postProcessing[i] = p_196851_.getPostProcessing()[i]; } this.setAllStarts(p_196851_.getAllStarts()); this.setAllReferences(p_196851_.getAllReferences()); for (Entry entry : p_196851_.getHeightmaps()) { if (ChunkStatus.FULL.heightmapsAfter().contains(entry.getKey())) { this.setHeightmap(entry.getKey(), entry.getValue().getRawData()); } } this.skyLightSources = p_196851_.skyLightSources; this.setLightCorrect(p_196851_.isLightCorrect()); this.markUnsaved(); } public void setUnsavedListener(LevelChunk.UnsavedListener p_364949_) { this.unsavedListener = p_364949_; if (this.isUnsaved()) { p_364949_.setUnsaved(this.chunkPos); } } @Override public void markUnsaved() { boolean flag = this.isUnsaved(); super.markUnsaved(); if (!flag) { this.unsavedListener.setUnsaved(this.chunkPos); } } @Override public TickContainerAccess getBlockTicks() { return this.blockTicks; } @Override public TickContainerAccess getFluidTicks() { return this.fluidTicks; } @Override public ChunkAccess.PackedTicks getTicksForSerialization(long p_361110_) { return new ChunkAccess.PackedTicks(this.blockTicks.pack(p_361110_), this.fluidTicks.pack(p_361110_)); } @Override public GameEventListenerRegistry getListenerRegistry(int p_251193_) { return this.level instanceof ServerLevel serverlevel ? this.gameEventListenerRegistrySections.computeIfAbsent(p_251193_, p_281221_ -> new EuclideanGameEventListenerRegistry(serverlevel, p_251193_, this::removeGameEventListenerRegistry)) : super.getListenerRegistry(p_251193_); } @Override public BlockState getBlockState(BlockPos p_62923_) { int i = p_62923_.getX(); int j = p_62923_.getY(); int k = p_62923_.getZ(); if (this.level.isDebug()) { BlockState blockstate = null; if (j == 60) { blockstate = Blocks.BARRIER.defaultBlockState(); } if (j == 70) { blockstate = DebugLevelSource.getBlockStateFor(i, k); } return blockstate == null ? Blocks.AIR.defaultBlockState() : blockstate; } else { try { int l = this.getSectionIndex(j); if (l >= 0 && l < this.sections.length) { LevelChunkSection levelchunksection = this.sections[l]; if (!levelchunksection.hasOnlyAir()) { return levelchunksection.getBlockState(i & 15, j & 15, k & 15); } } return Blocks.AIR.defaultBlockState(); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Getting block state"); CrashReportCategory crashreportcategory = crashreport.addCategory("Block being got"); crashreportcategory.setDetail("Location", () -> CrashReportCategory.formatLocation(this, i, j, k)); throw new ReportedException(crashreport); } } } @Override public FluidState getFluidState(BlockPos p_62895_) { return this.getFluidState(p_62895_.getX(), p_62895_.getY(), p_62895_.getZ()); } public FluidState getFluidState(int p_62815_, int p_62816_, int p_62817_) { try { int i = this.getSectionIndex(p_62816_); if (i >= 0 && i < this.sections.length) { LevelChunkSection levelchunksection = this.sections[i]; if (!levelchunksection.hasOnlyAir()) { return levelchunksection.getFluidState(p_62815_ & 15, p_62816_ & 15, p_62817_ & 15); } } return Fluids.EMPTY.defaultFluidState(); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Getting fluid state"); CrashReportCategory crashreportcategory = crashreport.addCategory("Block being got"); crashreportcategory.setDetail("Location", () -> CrashReportCategory.formatLocation(this, p_62815_, p_62816_, p_62817_)); throw new ReportedException(crashreport); } } @Nullable @Override public BlockState setBlockState(BlockPos p_62865_, BlockState p_62866_, int p_393998_) { int i = p_62865_.getY(); LevelChunkSection levelchunksection = this.getSection(this.getSectionIndex(i)); boolean flag = levelchunksection.hasOnlyAir(); if (flag && p_62866_.isAir()) { return null; } else { int j = p_62865_.getX() & 15; int k = i & 15; int l = p_62865_.getZ() & 15; BlockState blockstate = levelchunksection.setBlockState(j, k, l, p_62866_); if (blockstate == p_62866_) { return null; } else { Block block = p_62866_.getBlock(); this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING).update(j, i, l, p_62866_); this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(j, i, l, p_62866_); this.heightmaps.get(Heightmap.Types.OCEAN_FLOOR).update(j, i, l, p_62866_); this.heightmaps.get(Heightmap.Types.WORLD_SURFACE).update(j, i, l, p_62866_); boolean flag1 = levelchunksection.hasOnlyAir(); if (flag != flag1) { this.level.getChunkSource().getLightEngine().updateSectionStatus(p_62865_, flag1); this.level.getChunkSource().onSectionEmptinessChanged(this.chunkPos.x, SectionPos.blockToSectionCoord(i), this.chunkPos.z, flag1); } if (LightEngine.hasDifferentLightProperties(blockstate, p_62866_)) { ProfilerFiller profilerfiller = Profiler.get(); profilerfiller.push("updateSkyLightSources"); this.skyLightSources.update(this, j, i, l); profilerfiller.popPush("queueCheckLight"); this.level.getChunkSource().getLightEngine().checkBlock(p_62865_); profilerfiller.pop(); } boolean flag4 = !blockstate.is(block); boolean flag2 = (p_393998_ & 64) != 0; boolean flag3 = (p_393998_ & 256) == 0; if (flag4 && blockstate.hasBlockEntity()) { if (!this.level.isClientSide && flag3) { BlockEntity blockentity = this.level.getBlockEntity(p_62865_); if (blockentity != null) { blockentity.preRemoveSideEffects(p_62865_, blockstate); } } this.removeBlockEntity(p_62865_); } if ((flag4 || block instanceof BaseRailBlock) && this.level instanceof ServerLevel serverlevel && ((p_393998_ & 1) != 0 || flag2)) { blockstate.affectNeighborsAfterRemoval(serverlevel, p_62865_, flag2); } if (!levelchunksection.getBlockState(j, k, l).is(block)) { return null; } else { if (!this.level.isClientSide && (p_393998_ & 512) == 0) { p_62866_.onPlace(this.level, p_62865_, blockstate, flag2); } if (p_62866_.hasBlockEntity()) { BlockEntity blockentity1 = this.getBlockEntity(p_62865_, LevelChunk.EntityCreationType.CHECK); if (blockentity1 != null && !blockentity1.isValidBlockState(p_62866_)) { LOGGER.warn( "Found mismatched block entity @ {}: type = {}, state = {}", p_62865_, blockentity1.getType().builtInRegistryHolder().key().location(), p_62866_ ); this.removeBlockEntity(p_62865_); blockentity1 = null; } if (blockentity1 == null) { blockentity1 = ((EntityBlock)block).newBlockEntity(p_62865_, p_62866_); if (blockentity1 != null) { this.addAndRegisterBlockEntity(blockentity1); } } else { blockentity1.setBlockState(p_62866_); this.updateBlockEntityTicker(blockentity1); } } this.markUnsaved(); return blockstate; } } } } @Deprecated @Override public void addEntity(Entity p_62826_) { } @Nullable private BlockEntity createBlockEntity(BlockPos p_62935_) { BlockState blockstate = this.getBlockState(p_62935_); return !blockstate.hasBlockEntity() ? null : ((EntityBlock)blockstate.getBlock()).newBlockEntity(p_62935_, blockstate); } @Nullable @Override public BlockEntity getBlockEntity(BlockPos p_62912_) { return this.getBlockEntity(p_62912_, LevelChunk.EntityCreationType.CHECK); } @Nullable public BlockEntity getBlockEntity(BlockPos p_62868_, LevelChunk.EntityCreationType p_62869_) { BlockEntity blockentity = this.blockEntities.get(p_62868_); if (blockentity == null) { CompoundTag compoundtag = this.pendingBlockEntities.remove(p_62868_); if (compoundtag != null) { BlockEntity blockentity1 = this.promotePendingBlockEntity(p_62868_, compoundtag); if (blockentity1 != null) { return blockentity1; } } } if (blockentity == null) { if (p_62869_ == LevelChunk.EntityCreationType.IMMEDIATE) { blockentity = this.createBlockEntity(p_62868_); if (blockentity != null) { this.addAndRegisterBlockEntity(blockentity); } } } else if (blockentity.isRemoved()) { this.blockEntities.remove(p_62868_); return null; } return blockentity; } public void addAndRegisterBlockEntity(BlockEntity p_156391_) { this.setBlockEntity(p_156391_); if (this.isInLevel()) { if (this.level instanceof ServerLevel serverlevel) { this.addGameEventListener(p_156391_, serverlevel); } this.updateBlockEntityTicker(p_156391_); } } private boolean isInLevel() { return this.loaded || this.level.isClientSide(); } boolean isTicking(BlockPos p_156411_) { if (!this.level.getWorldBorder().isWithinBounds(p_156411_)) { return false; } else { return !(this.level instanceof ServerLevel serverlevel) ? true : this.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING) && serverlevel.areEntitiesLoaded(ChunkPos.asLong(p_156411_)); } } @Override public void setBlockEntity(BlockEntity p_156374_) { BlockPos blockpos = p_156374_.getBlockPos(); BlockState blockstate = this.getBlockState(blockpos); if (!blockstate.hasBlockEntity()) { LOGGER.warn("Trying to set block entity {} at position {}, but state {} does not allow it", p_156374_, blockpos, blockstate); } else { BlockState blockstate1 = p_156374_.getBlockState(); if (blockstate != blockstate1) { if (!p_156374_.getType().isValid(blockstate)) { LOGGER.warn("Trying to set block entity {} at position {}, but state {} does not allow it", p_156374_, blockpos, blockstate); return; } if (blockstate.getBlock() != blockstate1.getBlock()) { LOGGER.warn("Block state mismatch on block entity {} in position {}, {} != {}, updating", p_156374_, blockpos, blockstate, blockstate1); } p_156374_.setBlockState(blockstate); } p_156374_.setLevel(this.level); p_156374_.clearRemoved(); BlockEntity blockentity = this.blockEntities.put(blockpos.immutable(), p_156374_); if (blockentity != null && blockentity != p_156374_) { blockentity.setRemoved(); } } } @Nullable @Override public CompoundTag getBlockEntityNbtForSaving(BlockPos p_62932_, HolderLookup.Provider p_329605_) { BlockEntity blockentity = this.getBlockEntity(p_62932_); if (blockentity != null && !blockentity.isRemoved()) { CompoundTag compoundtag1 = blockentity.saveWithFullMetadata(this.level.registryAccess()); compoundtag1.putBoolean("keepPacked", false); return compoundtag1; } else { CompoundTag compoundtag = this.pendingBlockEntities.get(p_62932_); if (compoundtag != null) { compoundtag = compoundtag.copy(); compoundtag.putBoolean("keepPacked", true); } return compoundtag; } } @Override public void removeBlockEntity(BlockPos p_62919_) { if (this.isInLevel()) { BlockEntity blockentity = this.blockEntities.remove(p_62919_); if (blockentity != null) { if (this.level instanceof ServerLevel serverlevel) { this.removeGameEventListener(blockentity, serverlevel); } blockentity.setRemoved(); } } this.removeBlockEntityTicker(p_62919_); } private void removeGameEventListener(T p_223413_, ServerLevel p_223414_) { Block block = p_223413_.getBlockState().getBlock(); if (block instanceof EntityBlock) { GameEventListener gameeventlistener = ((EntityBlock)block).getListener(p_223414_, p_223413_); if (gameeventlistener != null) { int i = SectionPos.blockToSectionCoord(p_223413_.getBlockPos().getY()); GameEventListenerRegistry gameeventlistenerregistry = this.getListenerRegistry(i); gameeventlistenerregistry.unregister(gameeventlistener); } } } private void removeGameEventListenerRegistry(int p_283355_) { this.gameEventListenerRegistrySections.remove(p_283355_); } private void removeBlockEntityTicker(BlockPos p_156413_) { LevelChunk.RebindableTickingBlockEntityWrapper levelchunk$rebindabletickingblockentitywrapper = this.tickersInLevel.remove(p_156413_); if (levelchunk$rebindabletickingblockentitywrapper != null) { levelchunk$rebindabletickingblockentitywrapper.rebind(NULL_TICKER); } } public void runPostLoad() { if (this.postLoad != null) { this.postLoad.run(this); this.postLoad = null; } } public boolean isEmpty() { return false; } public void replaceWithPacketData( FriendlyByteBuf p_187972_, Map p_394716_, Consumer p_187974_ ) { this.clearAllBlockEntities(); for (LevelChunkSection levelchunksection : this.sections) { levelchunksection.read(p_187972_); } p_394716_.forEach(this::setHeightmap); this.initializeLightSources(); p_187974_.accept((p_327409_, p_327410_, p_327411_) -> { BlockEntity blockentity = this.getBlockEntity(p_327409_, LevelChunk.EntityCreationType.IMMEDIATE); if (blockentity != null && p_327411_ != null && blockentity.getType() == p_327410_) { blockentity.loadWithComponents(p_327411_, this.level.registryAccess()); } }); } public void replaceBiomes(FriendlyByteBuf p_275574_) { for (LevelChunkSection levelchunksection : this.sections) { levelchunksection.readBiomes(p_275574_); } } public void setLoaded(boolean p_62914_) { this.loaded = p_62914_; } public Level getLevel() { return this.level; } public Map getBlockEntities() { return this.blockEntities; } public void postProcessGeneration(ServerLevel p_364672_) { ChunkPos chunkpos = this.getPos(); for (int i = 0; i < this.postProcessing.length; i++) { if (this.postProcessing[i] != null) { for (Short oshort : this.postProcessing[i]) { BlockPos blockpos = ProtoChunk.unpackOffsetCoordinates(oshort, this.getSectionYFromSectionIndex(i), chunkpos); BlockState blockstate = this.getBlockState(blockpos); FluidState fluidstate = blockstate.getFluidState(); if (!fluidstate.isEmpty()) { fluidstate.tick(p_364672_, blockpos, blockstate); } if (!(blockstate.getBlock() instanceof LiquidBlock)) { BlockState blockstate1 = Block.updateFromNeighbourShapes(blockstate, p_364672_, blockpos); if (blockstate1 != blockstate) { p_364672_.setBlock(blockpos, blockstate1, 276); } } } this.postProcessing[i].clear(); } } for (BlockPos blockpos1 : ImmutableList.copyOf(this.pendingBlockEntities.keySet())) { this.getBlockEntity(blockpos1); } this.pendingBlockEntities.clear(); this.upgradeData.upgrade(this); } @Nullable private BlockEntity promotePendingBlockEntity(BlockPos p_62871_, CompoundTag p_62872_) { BlockState blockstate = this.getBlockState(p_62871_); BlockEntity blockentity; if ("DUMMY".equals(p_62872_.getStringOr("id", ""))) { if (blockstate.hasBlockEntity()) { blockentity = ((EntityBlock)blockstate.getBlock()).newBlockEntity(p_62871_, blockstate); } else { blockentity = null; LOGGER.warn("Tried to load a DUMMY block entity @ {} but found not block entity block {} at location", p_62871_, blockstate); } } else { blockentity = BlockEntity.loadStatic(p_62871_, blockstate, p_62872_, this.level.registryAccess()); } if (blockentity != null) { blockentity.setLevel(this.level); this.addAndRegisterBlockEntity(blockentity); } else { LOGGER.warn("Tried to load a block entity for block {} but failed at location {}", blockstate, p_62871_); } return blockentity; } public void unpackTicks(long p_187986_) { this.blockTicks.unpack(p_187986_); this.fluidTicks.unpack(p_187986_); } public void registerTickContainerInLevel(ServerLevel p_187959_) { p_187959_.getBlockTicks().addContainer(this.chunkPos, this.blockTicks); p_187959_.getFluidTicks().addContainer(this.chunkPos, this.fluidTicks); } public void unregisterTickContainerFromLevel(ServerLevel p_187980_) { p_187980_.getBlockTicks().removeContainer(this.chunkPos); p_187980_.getFluidTicks().removeContainer(this.chunkPos); } @Override public ChunkStatus getPersistedStatus() { return ChunkStatus.FULL; } public FullChunkStatus getFullStatus() { return this.fullStatus == null ? FullChunkStatus.FULL : this.fullStatus.get(); } public void setFullStatus(Supplier p_62880_) { this.fullStatus = p_62880_; } public void clearAllBlockEntities() { this.blockEntities.values().forEach(BlockEntity::setRemoved); this.blockEntities.clear(); this.tickersInLevel.values().forEach(p_187966_ -> p_187966_.rebind(NULL_TICKER)); this.tickersInLevel.clear(); } public void registerAllBlockEntitiesAfterLevelLoad() { this.blockEntities.values().forEach(p_187988_ -> { if (this.level instanceof ServerLevel serverlevel) { this.addGameEventListener(p_187988_, serverlevel); } this.updateBlockEntityTicker(p_187988_); }); } private void addGameEventListener(T p_223416_, ServerLevel p_223417_) { Block block = p_223416_.getBlockState().getBlock(); if (block instanceof EntityBlock) { GameEventListener gameeventlistener = ((EntityBlock)block).getListener(p_223417_, p_223416_); if (gameeventlistener != null) { this.getListenerRegistry(SectionPos.blockToSectionCoord(p_223416_.getBlockPos().getY())).register(gameeventlistener); } } } private void updateBlockEntityTicker(T p_156407_) { BlockState blockstate = p_156407_.getBlockState(); BlockEntityTicker blockentityticker = blockstate.getTicker(this.level, (BlockEntityType)p_156407_.getType()); if (blockentityticker == null) { this.removeBlockEntityTicker(p_156407_.getBlockPos()); } else { this.tickersInLevel .compute( p_156407_.getBlockPos(), (p_375350_, p_375351_) -> { TickingBlockEntity tickingblockentity = this.createTicker(p_156407_, blockentityticker); if (p_375351_ != null) { p_375351_.rebind(tickingblockentity); return (LevelChunk.RebindableTickingBlockEntityWrapper)p_375351_; } else if (this.isInLevel()) { LevelChunk.RebindableTickingBlockEntityWrapper levelchunk$rebindabletickingblockentitywrapper = new LevelChunk.RebindableTickingBlockEntityWrapper( tickingblockentity ); this.level.addBlockEntityTicker(levelchunk$rebindabletickingblockentitywrapper); return levelchunk$rebindabletickingblockentitywrapper; } else { return null; } } ); } } private TickingBlockEntity createTicker(T p_156376_, BlockEntityTicker p_156377_) { return new LevelChunk.BoundTickingBlockEntity<>(p_156376_, p_156377_); } class BoundTickingBlockEntity implements TickingBlockEntity { private final T blockEntity; private final BlockEntityTicker ticker; private boolean loggedInvalidBlockState; BoundTickingBlockEntity(final T p_156433_, final BlockEntityTicker p_156434_) { this.blockEntity = p_156433_; this.ticker = p_156434_; } @Override public void tick() { if (!this.blockEntity.isRemoved() && this.blockEntity.hasLevel()) { BlockPos blockpos = this.blockEntity.getBlockPos(); if (LevelChunk.this.isTicking(blockpos)) { try { ProfilerFiller profilerfiller = Profiler.get(); profilerfiller.push(this::getType); BlockState blockstate = LevelChunk.this.getBlockState(blockpos); if (this.blockEntity.getType().isValid(blockstate)) { this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), blockstate, this.blockEntity); this.loggedInvalidBlockState = false; } else if (!this.loggedInvalidBlockState) { this.loggedInvalidBlockState = true; LevelChunk.LOGGER .warn( "Block entity {} @ {} state {} invalid for ticking:", LogUtils.defer(this::getType), LogUtils.defer(this::getPos), blockstate ); } profilerfiller.pop(); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Ticking block entity"); CrashReportCategory crashreportcategory = crashreport.addCategory("Block entity being ticked"); this.blockEntity.fillCrashReportCategory(crashreportcategory); throw new ReportedException(crashreport); } } } } @Override public boolean isRemoved() { return this.blockEntity.isRemoved(); } @Override public BlockPos getPos() { return this.blockEntity.getBlockPos(); } @Override public String getType() { return BlockEntityType.getKey(this.blockEntity.getType()).toString(); } @Override public String toString() { return "Level ticker for " + this.getType() + "@" + this.getPos(); } } public static enum EntityCreationType { IMMEDIATE, QUEUED, CHECK; } @FunctionalInterface public interface PostLoadProcessor { void run(LevelChunk p_196867_); } static class RebindableTickingBlockEntityWrapper implements TickingBlockEntity { private TickingBlockEntity ticker; RebindableTickingBlockEntityWrapper(TickingBlockEntity p_156447_) { this.ticker = p_156447_; } void rebind(TickingBlockEntity p_156450_) { this.ticker = p_156450_; } @Override public void tick() { this.ticker.tick(); } @Override public boolean isRemoved() { return this.ticker.isRemoved(); } @Override public BlockPos getPos() { return this.ticker.getPos(); } @Override public String getType() { return this.ticker.getType(); } @Override public String toString() { return this.ticker + " "; } } @FunctionalInterface public interface UnsavedListener { void setUnsaved(ChunkPos p_366175_); } }