339 lines
18 KiB
Java
339 lines
18 KiB
Java
/*
|
|
* 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<SkyLightSectionStorage.SkyDataLayerStorageMap, SkyLightSectionStorage> {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|