minecraft-src/net/minecraft/world/level/lighting/SkyLightEngine.java
2025-07-04 02:00:41 +03:00

356 lines
13 KiB
Java

package net.minecraft.world.level.lighting;
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 org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.VisibleForTesting;
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 sectionStorage) {
super(chunkSource, sectionStorage);
this.emptyChunkSources = new ChunkSkyLightSources(chunkSource.getLevel());
}
private static boolean isSourceLevel(int level) {
return level == 15;
}
private int getLowestSourceY(int x, int z, int defaultReturnValue) {
ChunkSkyLightSources chunkSkyLightSources = this.getChunkSources(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z));
return chunkSkyLightSources == null
? defaultReturnValue
: chunkSkyLightSources.getLowestSourceY(SectionPos.sectionRelative(x), SectionPos.sectionRelative(z));
}
@Nullable
private ChunkSkyLightSources getChunkSources(int chunkX, int chunkZ) {
LightChunk lightChunk = this.chunkSource.getChunkForLighting(chunkX, chunkZ);
return lightChunk != null ? lightChunk.getSkyLightSources() : null;
}
@Override
protected void checkNode(long packedPos) {
int i = BlockPos.getX(packedPos);
int j = BlockPos.getY(packedPos);
int k = BlockPos.getZ(packedPos);
long l = SectionPos.blockToSection(packedPos);
int m = this.storage.lightOnInSection(l) ? this.getLowestSourceY(i, k, Integer.MAX_VALUE) : Integer.MAX_VALUE;
if (m != Integer.MAX_VALUE) {
this.updateSourcesInColumn(i, k, m);
}
if (this.storage.storingLightForSection(l)) {
boolean bl = j >= m;
if (bl) {
this.enqueueDecrease(packedPos, REMOVE_SKY_SOURCE_ENTRY);
this.enqueueIncrease(packedPos, ADD_SKY_SOURCE_ENTRY);
} else {
int n = this.storage.getStoredLevel(packedPos);
if (n > 0) {
this.storage.setStoredLevel(packedPos, 0);
this.enqueueDecrease(packedPos, LightEngine.QueueEntry.decreaseAllDirections(n));
} else {
this.enqueueDecrease(packedPos, PULL_LIGHT_IN_ENTRY);
}
}
}
}
private void updateSourcesInColumn(int x, int z, int lowestY) {
int i = SectionPos.sectionToBlockCoord(this.storage.getBottomSectionY());
this.removeSourcesBelow(x, z, lowestY, i);
this.addSourcesAbove(x, z, lowestY, i);
}
private void removeSourcesBelow(int x, int z, int minY, int bottomSectionY) {
if (minY > bottomSectionY) {
int i = SectionPos.blockToSectionCoord(x);
int j = SectionPos.blockToSectionCoord(z);
int k = minY - 1;
for (int l = SectionPos.blockToSectionCoord(k); this.storage.hasLightDataAtOrBelow(l); l--) {
if (this.storage.storingLightForSection(SectionPos.asLong(i, l, j))) {
int m = SectionPos.sectionToBlockCoord(l);
int n = m + 15;
for (int o = Math.min(n, k); o >= m; o--) {
long p = BlockPos.asLong(x, o, z);
if (!isSourceLevel(this.storage.getStoredLevel(p))) {
return;
}
this.storage.setStoredLevel(p, 0);
this.enqueueDecrease(p, o == minY - 1 ? REMOVE_TOP_SKY_SOURCE_ENTRY : REMOVE_SKY_SOURCE_ENTRY);
}
}
}
}
}
private void addSourcesAbove(int x, int z, int maxY, int bottomSectionY) {
int i = SectionPos.blockToSectionCoord(x);
int j = SectionPos.blockToSectionCoord(z);
int k = 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 l = Math.max(maxY, bottomSectionY);
for (long m = SectionPos.asLong(i, SectionPos.blockToSectionCoord(l), j); !this.storage.isAboveData(m); m = SectionPos.offset(m, Direction.UP)) {
if (this.storage.storingLightForSection(m)) {
int n = SectionPos.sectionToBlockCoord(SectionPos.y(m));
int o = n + 15;
for (int p = Math.max(n, l); p <= o; p++) {
long q = BlockPos.asLong(x, p, z);
if (isSourceLevel(this.storage.getStoredLevel(q))) {
return;
}
this.storage.setStoredLevel(q, 15);
if (p < k || p == maxY) {
this.enqueueIncrease(q, ADD_SKY_SOURCE_ENTRY);
}
}
}
}
}
@Override
protected void propagateIncrease(long packedPos, long queueEntry, int lightLevel) {
BlockState blockState = null;
int i = this.countEmptySectionsBelowIfAtBorder(packedPos);
for (Direction direction : PROPAGATION_DIRECTIONS) {
if (LightEngine.QueueEntry.shouldPropagateInDirection(queueEntry, direction)) {
long l = BlockPos.offset(packedPos, direction);
if (this.storage.storingLightForSection(SectionPos.blockToSection(l))) {
int j = this.storage.getStoredLevel(l);
int k = lightLevel - 1;
if (k > j) {
this.mutablePos.set(l);
BlockState blockState2 = this.getState(this.mutablePos);
int m = lightLevel - this.getOpacity(blockState2);
if (m > j) {
if (blockState == null) {
blockState = LightEngine.QueueEntry.isFromEmptyShape(queueEntry) ? Blocks.AIR.defaultBlockState() : this.getState(this.mutablePos.set(packedPos));
}
if (!this.shapeOccludes(blockState, blockState2, direction)) {
this.storage.setStoredLevel(l, m);
if (m > 1) {
this.enqueueIncrease(l, LightEngine.QueueEntry.increaseSkipOneDirection(m, isEmptyShape(blockState2), direction.getOpposite()));
}
this.propagateFromEmptySections(l, direction, m, true, i);
}
}
}
}
}
}
}
@Override
protected void propagateDecrease(long packedPos, long lightLevel) {
int i = this.countEmptySectionsBelowIfAtBorder(packedPos);
int j = LightEngine.QueueEntry.getFromLevel(lightLevel);
for (Direction direction : PROPAGATION_DIRECTIONS) {
if (LightEngine.QueueEntry.shouldPropagateInDirection(lightLevel, direction)) {
long l = BlockPos.offset(packedPos, direction);
if (this.storage.storingLightForSection(SectionPos.blockToSection(l))) {
int k = this.storage.getStoredLevel(l);
if (k != 0) {
if (k <= j - 1) {
this.storage.setStoredLevel(l, 0);
this.enqueueDecrease(l, LightEngine.QueueEntry.decreaseSkipOneDirection(k, direction.getOpposite()));
this.propagateFromEmptySections(l, direction, k, false, i);
} else {
this.enqueueIncrease(l, LightEngine.QueueEntry.increaseOnlyOneDirection(k, false, direction.getOpposite()));
}
}
}
}
}
}
private int countEmptySectionsBelowIfAtBorder(long packedPos) {
int i = BlockPos.getY(packedPos);
int j = SectionPos.sectionRelative(i);
if (j != 0) {
return 0;
} else {
int k = BlockPos.getX(packedPos);
int l = BlockPos.getZ(packedPos);
int m = SectionPos.sectionRelative(k);
int n = SectionPos.sectionRelative(l);
if (m != 0 && m != 15 && n != 0 && n != 15) {
return 0;
} else {
int o = SectionPos.blockToSectionCoord(k);
int p = SectionPos.blockToSectionCoord(i);
int q = SectionPos.blockToSectionCoord(l);
int r = 0;
while (!this.storage.storingLightForSection(SectionPos.asLong(o, p - r - 1, q)) && this.storage.hasLightDataAtOrBelow(p - r - 1)) {
r++;
}
return r;
}
}
}
private void propagateFromEmptySections(long packedPos, Direction direction, int level, boolean shouldIncrease, int emptySections) {
if (emptySections != 0) {
int i = BlockPos.getX(packedPos);
int j = BlockPos.getZ(packedPos);
if (crossedSectionEdge(direction, SectionPos.sectionRelative(i), SectionPos.sectionRelative(j))) {
int k = BlockPos.getY(packedPos);
int l = SectionPos.blockToSectionCoord(i);
int m = SectionPos.blockToSectionCoord(j);
int n = SectionPos.blockToSectionCoord(k) - 1;
int o = n - emptySections + 1;
while (n >= o) {
if (!this.storage.storingLightForSection(SectionPos.asLong(l, n, m))) {
n--;
} else {
int p = SectionPos.sectionToBlockCoord(n);
for (int q = 15; q >= 0; q--) {
long r = BlockPos.asLong(i, p + q, j);
if (shouldIncrease) {
this.storage.setStoredLevel(r, level);
if (level > 1) {
this.enqueueIncrease(r, LightEngine.QueueEntry.increaseSkipOneDirection(level, true, direction.getOpposite()));
}
} else {
this.storage.setStoredLevel(r, 0);
this.enqueueDecrease(r, LightEngine.QueueEntry.decreaseSkipOneDirection(level, direction.getOpposite()));
}
}
n--;
}
}
}
}
}
private static boolean crossedSectionEdge(Direction direction, int x, int z) {
return switch (direction) {
case NORTH -> z == 15;
case SOUTH -> z == 0;
case WEST -> x == 15;
case EAST -> x == 0;
default -> false;
};
}
@Override
public void setLightEnabled(ChunkPos chunkPos, boolean lightEnabled) {
super.setLightEnabled(chunkPos, lightEnabled);
if (lightEnabled) {
ChunkSkyLightSources chunkSkyLightSources = (ChunkSkyLightSources)Objects.requireNonNullElse(
this.getChunkSources(chunkPos.x, chunkPos.z), this.emptyChunkSources
);
int i = chunkSkyLightSources.getHighestLowestSourceY() - 1;
int j = SectionPos.blockToSectionCoord(i) + 1;
long l = SectionPos.getZeroNode(chunkPos.x, chunkPos.z);
int k = this.storage.getTopSectionY(l);
int m = Math.max(this.storage.getBottomSectionY(), j);
for (int n = k - 1; n >= m; n--) {
DataLayer dataLayer = this.storage.getDataLayerToWrite(SectionPos.asLong(chunkPos.x, n, chunkPos.z));
if (dataLayer != null && dataLayer.isEmpty()) {
dataLayer.fill(15);
}
}
}
}
@Override
public void propagateLightSources(ChunkPos chunkPos) {
long l = SectionPos.getZeroNode(chunkPos.x, chunkPos.z);
this.storage.setLightEnabled(l, true);
ChunkSkyLightSources chunkSkyLightSources = (ChunkSkyLightSources)Objects.requireNonNullElse(
this.getChunkSources(chunkPos.x, chunkPos.z), this.emptyChunkSources
);
ChunkSkyLightSources chunkSkyLightSources2 = (ChunkSkyLightSources)Objects.requireNonNullElse(
this.getChunkSources(chunkPos.x, chunkPos.z - 1), this.emptyChunkSources
);
ChunkSkyLightSources chunkSkyLightSources3 = (ChunkSkyLightSources)Objects.requireNonNullElse(
this.getChunkSources(chunkPos.x, chunkPos.z + 1), this.emptyChunkSources
);
ChunkSkyLightSources chunkSkyLightSources4 = (ChunkSkyLightSources)Objects.requireNonNullElse(
this.getChunkSources(chunkPos.x - 1, chunkPos.z), this.emptyChunkSources
);
ChunkSkyLightSources chunkSkyLightSources5 = (ChunkSkyLightSources)Objects.requireNonNullElse(
this.getChunkSources(chunkPos.x + 1, chunkPos.z), this.emptyChunkSources
);
int i = this.storage.getTopSectionY(l);
int j = this.storage.getBottomSectionY();
int k = SectionPos.sectionToBlockCoord(chunkPos.x);
int m = SectionPos.sectionToBlockCoord(chunkPos.z);
for (int n = i - 1; n >= j; n--) {
long o = SectionPos.asLong(chunkPos.x, n, chunkPos.z);
DataLayer dataLayer = this.storage.getDataLayerToWrite(o);
if (dataLayer != null) {
int p = SectionPos.sectionToBlockCoord(n);
int q = p + 15;
boolean bl = false;
for (int r = 0; r < 16; r++) {
for (int s = 0; s < 16; s++) {
int t = chunkSkyLightSources.getLowestSourceY(s, r);
if (t <= q) {
int u = r == 0 ? chunkSkyLightSources2.getLowestSourceY(s, 15) : chunkSkyLightSources.getLowestSourceY(s, r - 1);
int v = r == 15 ? chunkSkyLightSources3.getLowestSourceY(s, 0) : chunkSkyLightSources.getLowestSourceY(s, r + 1);
int w = s == 0 ? chunkSkyLightSources4.getLowestSourceY(15, r) : chunkSkyLightSources.getLowestSourceY(s - 1, r);
int x = s == 15 ? chunkSkyLightSources5.getLowestSourceY(0, r) : chunkSkyLightSources.getLowestSourceY(s + 1, r);
int y = Math.max(Math.max(u, v), Math.max(w, x));
for (int z = q; z >= Math.max(p, t); z--) {
dataLayer.set(s, SectionPos.sectionRelative(z), r, 15);
if (z == t || z < y) {
long aa = BlockPos.asLong(k + s, z, m + r);
this.enqueueIncrease(aa, LightEngine.QueueEntry.increaseSkySourceInDirections(z == t, z < u, z < v, z < w, z < x));
}
}
if (t < p) {
bl = true;
}
}
}
}
if (!bl) {
break;
}
}
}
}
}