/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.common.annotations.VisibleForTesting * org.jspecify.annotations.Nullable */ package net.minecraft.world.level.lighting; import com.google.common.annotations.VisibleForTesting; import java.util.Objects; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.SectionPos; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.DataLayer; import net.minecraft.world.level.chunk.LightChunk; import net.minecraft.world.level.chunk.LightChunkGetter; import net.minecraft.world.level.lighting.ChunkSkyLightSources; import net.minecraft.world.level.lighting.LightEngine; import net.minecraft.world.level.lighting.SkyLightSectionStorage; import org.jspecify.annotations.Nullable; public final class SkyLightEngine extends LightEngine { private static final long REMOVE_TOP_SKY_SOURCE_ENTRY = LightEngine.QueueEntry.decreaseAllDirections(15); private static final long REMOVE_SKY_SOURCE_ENTRY = LightEngine.QueueEntry.decreaseSkipOneDirection(15, Direction.UP); private static final long ADD_SKY_SOURCE_ENTRY = LightEngine.QueueEntry.increaseSkipOneDirection(15, false, Direction.UP); private final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); private final ChunkSkyLightSources emptyChunkSources; public SkyLightEngine(LightChunkGetter chunkSource) { this(chunkSource, new SkyLightSectionStorage(chunkSource)); } @VisibleForTesting protected SkyLightEngine(LightChunkGetter chunkSource, SkyLightSectionStorage storage) { super(chunkSource, storage); this.emptyChunkSources = new ChunkSkyLightSources(chunkSource.getLevel()); } private static boolean isSourceLevel(int value) { return value == 15; } private int getLowestSourceY(int x, int z, int defaultValue) { ChunkSkyLightSources sources = this.getChunkSources(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z)); if (sources == null) { return defaultValue; } return sources.getLowestSourceY(SectionPos.sectionRelative(x), SectionPos.sectionRelative(z)); } private @Nullable ChunkSkyLightSources getChunkSources(int chunkX, int chunkZ) { LightChunk chunk = this.chunkSource.getChunkForLighting(chunkX, chunkZ); return chunk != null ? chunk.getSkyLightSources() : null; } @Override protected void checkNode(long blockNode) { boolean isSource; int lowestSourceY; int x = BlockPos.getX(blockNode); int y = BlockPos.getY(blockNode); int z = BlockPos.getZ(blockNode); long sectionNode = SectionPos.blockToSection(blockNode); int n = lowestSourceY = ((SkyLightSectionStorage)this.storage).lightOnInSection(sectionNode) ? this.getLowestSourceY(x, z, Integer.MAX_VALUE) : Integer.MAX_VALUE; if (lowestSourceY != Integer.MAX_VALUE) { this.updateSourcesInColumn(x, z, lowestSourceY); } if (!((SkyLightSectionStorage)this.storage).storingLightForSection(sectionNode)) { return; } boolean bl = isSource = y >= lowestSourceY; if (isSource) { this.enqueueDecrease(blockNode, REMOVE_SKY_SOURCE_ENTRY); this.enqueueIncrease(blockNode, ADD_SKY_SOURCE_ENTRY); } else { int oldLevel = ((SkyLightSectionStorage)this.storage).getStoredLevel(blockNode); if (oldLevel > 0) { ((SkyLightSectionStorage)this.storage).setStoredLevel(blockNode, 0); this.enqueueDecrease(blockNode, LightEngine.QueueEntry.decreaseAllDirections(oldLevel)); } else { this.enqueueDecrease(blockNode, PULL_LIGHT_IN_ENTRY); } } } private void updateSourcesInColumn(int x, int z, int lowestSourceY) { int worldBottomY = SectionPos.sectionToBlockCoord(((SkyLightSectionStorage)this.storage).getBottomSectionY()); this.removeSourcesBelow(x, z, lowestSourceY, worldBottomY); this.addSourcesAbove(x, z, lowestSourceY, worldBottomY); } private void removeSourcesBelow(int x, int z, int lowestSourceY, int worldBottomY) { if (lowestSourceY <= worldBottomY) { return; } int sectionX = SectionPos.blockToSectionCoord(x); int sectionZ = SectionPos.blockToSectionCoord(z); int startY = lowestSourceY - 1; int sectionY = SectionPos.blockToSectionCoord(startY); while (((SkyLightSectionStorage)this.storage).hasLightDataAtOrBelow(sectionY)) { if (((SkyLightSectionStorage)this.storage).storingLightForSection(SectionPos.asLong(sectionX, sectionY, sectionZ))) { int sectionBottomY = SectionPos.sectionToBlockCoord(sectionY); int sectionTopY = sectionBottomY + 15; for (int y = Math.min(sectionTopY, startY); y >= sectionBottomY; --y) { long blockNode = BlockPos.asLong(x, y, z); if (!SkyLightEngine.isSourceLevel(((SkyLightSectionStorage)this.storage).getStoredLevel(blockNode))) { return; } ((SkyLightSectionStorage)this.storage).setStoredLevel(blockNode, 0); this.enqueueDecrease(blockNode, y == lowestSourceY - 1 ? REMOVE_TOP_SKY_SOURCE_ENTRY : REMOVE_SKY_SOURCE_ENTRY); } } --sectionY; } } private void addSourcesAbove(int x, int z, int lowestSourceY, int worldBottomY) { int sectionX = SectionPos.blockToSectionCoord(x); int sectionZ = SectionPos.blockToSectionCoord(z); int neighborLowestSourceY = Math.max(Math.max(this.getLowestSourceY(x - 1, z, Integer.MIN_VALUE), this.getLowestSourceY(x + 1, z, Integer.MIN_VALUE)), Math.max(this.getLowestSourceY(x, z - 1, Integer.MIN_VALUE), this.getLowestSourceY(x, z + 1, Integer.MIN_VALUE))); int startY = Math.max(lowestSourceY, worldBottomY); long sectionNode = SectionPos.asLong(sectionX, SectionPos.blockToSectionCoord(startY), sectionZ); while (!((SkyLightSectionStorage)this.storage).isAboveData(sectionNode)) { if (((SkyLightSectionStorage)this.storage).storingLightForSection(sectionNode)) { int sectionBottomY = SectionPos.sectionToBlockCoord(SectionPos.y(sectionNode)); int sectionTopY = sectionBottomY + 15; for (int y = Math.max(sectionBottomY, startY); y <= sectionTopY; ++y) { long blockNode = BlockPos.asLong(x, y, z); if (SkyLightEngine.isSourceLevel(((SkyLightSectionStorage)this.storage).getStoredLevel(blockNode))) { return; } ((SkyLightSectionStorage)this.storage).setStoredLevel(blockNode, 15); if (y >= neighborLowestSourceY && y != lowestSourceY) continue; this.enqueueIncrease(blockNode, ADD_SKY_SOURCE_ENTRY); } } sectionNode = SectionPos.offset(sectionNode, Direction.UP); } } @Override protected void propagateIncrease(long fromNode, long increaseData, int fromLevel) { BlockState fromState = null; int emptySectionsBelow = this.countEmptySectionsBelowIfAtBorder(fromNode); for (Direction propagationDirection : PROPAGATION_DIRECTIONS) { int toLevel; int maxPossibleNewToLevel; long toNode; if (!LightEngine.QueueEntry.shouldPropagateInDirection(increaseData, propagationDirection) || !((SkyLightSectionStorage)this.storage).storingLightForSection(SectionPos.blockToSection(toNode = BlockPos.offset(fromNode, propagationDirection))) || (maxPossibleNewToLevel = fromLevel - 1) <= (toLevel = ((SkyLightSectionStorage)this.storage).getStoredLevel(toNode))) continue; this.mutablePos.set(toNode); BlockState toState = this.getState(this.mutablePos); int newToLevel = fromLevel - this.getOpacity(toState); if (newToLevel <= toLevel) continue; if (fromState == null) { BlockState blockState = fromState = LightEngine.QueueEntry.isFromEmptyShape(increaseData) ? Blocks.AIR.defaultBlockState() : this.getState(this.mutablePos.set(fromNode)); } if (this.shapeOccludes(fromState, toState, propagationDirection)) continue; ((SkyLightSectionStorage)this.storage).setStoredLevel(toNode, newToLevel); if (newToLevel > 1) { this.enqueueIncrease(toNode, LightEngine.QueueEntry.increaseSkipOneDirection(newToLevel, SkyLightEngine.isEmptyShape(toState), propagationDirection.getOpposite())); } this.propagateFromEmptySections(toNode, propagationDirection, newToLevel, true, emptySectionsBelow); } } @Override protected void propagateDecrease(long fromNode, long decreaseData) { int emptySectionsBelow = this.countEmptySectionsBelowIfAtBorder(fromNode); int oldFromLevel = LightEngine.QueueEntry.getFromLevel(decreaseData); for (Direction propagationDirection : PROPAGATION_DIRECTIONS) { int toLevel; long toNode; if (!LightEngine.QueueEntry.shouldPropagateInDirection(decreaseData, propagationDirection) || !((SkyLightSectionStorage)this.storage).storingLightForSection(SectionPos.blockToSection(toNode = BlockPos.offset(fromNode, propagationDirection))) || (toLevel = ((SkyLightSectionStorage)this.storage).getStoredLevel(toNode)) == 0) continue; if (toLevel <= oldFromLevel - 1) { ((SkyLightSectionStorage)this.storage).setStoredLevel(toNode, 0); this.enqueueDecrease(toNode, LightEngine.QueueEntry.decreaseSkipOneDirection(toLevel, propagationDirection.getOpposite())); this.propagateFromEmptySections(toNode, propagationDirection, toLevel, false, emptySectionsBelow); continue; } this.enqueueIncrease(toNode, LightEngine.QueueEntry.increaseOnlyOneDirection(toLevel, false, propagationDirection.getOpposite())); } } private int countEmptySectionsBelowIfAtBorder(long blockNode) { int y = BlockPos.getY(blockNode); int localY = SectionPos.sectionRelative(y); if (localY != 0) { return 0; } int x = BlockPos.getX(blockNode); int z = BlockPos.getZ(blockNode); int localX = SectionPos.sectionRelative(x); int localZ = SectionPos.sectionRelative(z); if (localX == 0 || localX == 15 || localZ == 0 || localZ == 15) { int sectionX = SectionPos.blockToSectionCoord(x); int sectionY = SectionPos.blockToSectionCoord(y); int sectionZ = SectionPos.blockToSectionCoord(z); int emptySectionsBelow = 0; while (!((SkyLightSectionStorage)this.storage).storingLightForSection(SectionPos.asLong(sectionX, sectionY - emptySectionsBelow - 1, sectionZ)) && ((SkyLightSectionStorage)this.storage).hasLightDataAtOrBelow(sectionY - emptySectionsBelow - 1)) { ++emptySectionsBelow; } return emptySectionsBelow; } return 0; } private void propagateFromEmptySections(long toNode, Direction propagationDirection, int toLevel, boolean increase, int emptySectionsBelow) { if (emptySectionsBelow == 0) { return; } int x = BlockPos.getX(toNode); int z = BlockPos.getZ(toNode); if (!SkyLightEngine.crossedSectionEdge(propagationDirection, SectionPos.sectionRelative(x), SectionPos.sectionRelative(z))) { return; } int y = BlockPos.getY(toNode); int sectionX = SectionPos.blockToSectionCoord(x); int sectionZ = SectionPos.blockToSectionCoord(z); int sectionY = SectionPos.blockToSectionCoord(y) - 1; int bottomSectionY = sectionY - emptySectionsBelow + 1; while (sectionY >= bottomSectionY) { if (!((SkyLightSectionStorage)this.storage).storingLightForSection(SectionPos.asLong(sectionX, sectionY, sectionZ))) { --sectionY; continue; } int sectionMinY = SectionPos.sectionToBlockCoord(sectionY); for (int localY = 15; localY >= 0; --localY) { long blockNode = BlockPos.asLong(x, sectionMinY + localY, z); if (increase) { ((SkyLightSectionStorage)this.storage).setStoredLevel(blockNode, toLevel); if (toLevel <= 1) continue; this.enqueueIncrease(blockNode, LightEngine.QueueEntry.increaseSkipOneDirection(toLevel, true, propagationDirection.getOpposite())); continue; } ((SkyLightSectionStorage)this.storage).setStoredLevel(blockNode, 0); this.enqueueDecrease(blockNode, LightEngine.QueueEntry.decreaseSkipOneDirection(toLevel, propagationDirection.getOpposite())); } --sectionY; } } private static boolean crossedSectionEdge(Direction propagationDirection, int x, int z) { return switch (propagationDirection) { case Direction.NORTH -> { if (z == 15) { yield true; } yield false; } case Direction.SOUTH -> { if (z == 0) { yield true; } yield false; } case Direction.WEST -> { if (x == 15) { yield true; } yield false; } case Direction.EAST -> { if (x == 0) { yield true; } yield false; } default -> false; }; } @Override public void setLightEnabled(ChunkPos pos, boolean enable) { super.setLightEnabled(pos, enable); if (enable) { ChunkSkyLightSources sources = Objects.requireNonNullElse(this.getChunkSources(pos.x, pos.z), this.emptyChunkSources); int highestNonSourceY = sources.getHighestLowestSourceY() - 1; int lowestFullySourceSectionY = SectionPos.blockToSectionCoord(highestNonSourceY) + 1; long zeroNode = SectionPos.getZeroNode(pos.x, pos.z); int topSectionY = ((SkyLightSectionStorage)this.storage).getTopSectionY(zeroNode); int bottomSectionY = Math.max(((SkyLightSectionStorage)this.storage).getBottomSectionY(), lowestFullySourceSectionY); for (int sectionY = topSectionY - 1; sectionY >= bottomSectionY; --sectionY) { DataLayer dataLayer = ((SkyLightSectionStorage)this.storage).getDataLayerToWrite(SectionPos.asLong(pos.x, sectionY, pos.z)); if (dataLayer == null || !dataLayer.isEmpty()) continue; dataLayer.fill(15); } } } @Override public void propagateLightSources(ChunkPos pos) { long zeroNode = SectionPos.getZeroNode(pos.x, pos.z); ((SkyLightSectionStorage)this.storage).setLightEnabled(zeroNode, true); ChunkSkyLightSources sources = Objects.requireNonNullElse(this.getChunkSources(pos.x, pos.z), this.emptyChunkSources); ChunkSkyLightSources northSources = Objects.requireNonNullElse(this.getChunkSources(pos.x, pos.z - 1), this.emptyChunkSources); ChunkSkyLightSources southSources = Objects.requireNonNullElse(this.getChunkSources(pos.x, pos.z + 1), this.emptyChunkSources); ChunkSkyLightSources westSources = Objects.requireNonNullElse(this.getChunkSources(pos.x - 1, pos.z), this.emptyChunkSources); ChunkSkyLightSources eastSources = Objects.requireNonNullElse(this.getChunkSources(pos.x + 1, pos.z), this.emptyChunkSources); int topSectionY = ((SkyLightSectionStorage)this.storage).getTopSectionY(zeroNode); int bottomSectionY = ((SkyLightSectionStorage)this.storage).getBottomSectionY(); int sectionMinX = SectionPos.sectionToBlockCoord(pos.x); int sectionMinZ = SectionPos.sectionToBlockCoord(pos.z); for (int sectionY = topSectionY - 1; sectionY >= bottomSectionY; --sectionY) { long sectionNode = SectionPos.asLong(pos.x, sectionY, pos.z); DataLayer dataLayer = ((SkyLightSectionStorage)this.storage).getDataLayerToWrite(sectionNode); if (dataLayer == null) continue; int sectionMinY = SectionPos.sectionToBlockCoord(sectionY); int sectionMaxY = sectionMinY + 15; boolean sourcesBelow = false; for (int z = 0; z < 16; ++z) { for (int x = 0; x < 16; ++x) { int lowestSourceY = sources.getLowestSourceY(x, z); if (lowestSourceY > sectionMaxY) continue; int northLowestSourceY = z == 0 ? northSources.getLowestSourceY(x, 15) : sources.getLowestSourceY(x, z - 1); int southLowestSourceY = z == 15 ? southSources.getLowestSourceY(x, 0) : sources.getLowestSourceY(x, z + 1); int westLowestSourceY = x == 0 ? westSources.getLowestSourceY(15, z) : sources.getLowestSourceY(x - 1, z); int eastLowestSourceY = x == 15 ? eastSources.getLowestSourceY(0, z) : sources.getLowestSourceY(x + 1, z); int neighborLowestSourceY = Math.max(Math.max(northLowestSourceY, southLowestSourceY), Math.max(westLowestSourceY, eastLowestSourceY)); for (int y = sectionMaxY; y >= Math.max(sectionMinY, lowestSourceY); --y) { dataLayer.set(x, SectionPos.sectionRelative(y), z, 15); if (y != lowestSourceY && y >= neighborLowestSourceY) continue; long blockNode = BlockPos.asLong(sectionMinX + x, y, sectionMinZ + z); this.enqueueIncrease(blockNode, LightEngine.QueueEntry.increaseSkySourceInDirections(y == lowestSourceY, y < northLowestSourceY, y < southLowestSourceY, y < westLowestSourceY, y < eastLowestSourceY)); } if (lowestSourceY >= sectionMinY) continue; sourcesBelow = true; } } if (!sourcesBelow) break; } } }