356 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			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;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |