326 lines
		
	
	
	
		
			9.8 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			326 lines
		
	
	
	
		
			9.8 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.level.lighting;
 | |
| 
 | |
| import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
 | |
| import it.unimi.dsi.fastutil.longs.LongIterator;
 | |
| import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
 | |
| import java.util.Arrays;
 | |
| 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.phys.shapes.Shapes;
 | |
| import net.minecraft.world.phys.shapes.VoxelShape;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public abstract class LightEngine<M extends DataLayerStorageMap<M>, S extends LayerLightSectionStorage<M>> implements LayerLightEventListener {
 | |
| 	public static final int MAX_LEVEL = 15;
 | |
| 	protected static final int MIN_OPACITY = 1;
 | |
| 	protected static final long PULL_LIGHT_IN_ENTRY = LightEngine.QueueEntry.decreaseAllDirections(1);
 | |
| 	private static final int MIN_QUEUE_SIZE = 512;
 | |
| 	protected static final Direction[] PROPAGATION_DIRECTIONS = Direction.values();
 | |
| 	protected final LightChunkGetter chunkSource;
 | |
| 	protected final S storage;
 | |
| 	private final LongOpenHashSet blockNodesToCheck = new LongOpenHashSet(512, 0.5F);
 | |
| 	private final LongArrayFIFOQueue decreaseQueue = new LongArrayFIFOQueue();
 | |
| 	private final LongArrayFIFOQueue increaseQueue = new LongArrayFIFOQueue();
 | |
| 	private static final int CACHE_SIZE = 2;
 | |
| 	private final long[] lastChunkPos = new long[2];
 | |
| 	private final LightChunk[] lastChunk = new LightChunk[2];
 | |
| 
 | |
| 	protected LightEngine(LightChunkGetter chunkSource, S storage) {
 | |
| 		this.chunkSource = chunkSource;
 | |
| 		this.storage = storage;
 | |
| 		this.clearChunkCache();
 | |
| 	}
 | |
| 
 | |
| 	public static boolean hasDifferentLightProperties(BlockState state1, BlockState state2) {
 | |
| 		return state2 == state1
 | |
| 			? false
 | |
| 			: state2.getLightBlock() != state1.getLightBlock()
 | |
| 				|| state2.getLightEmission() != state1.getLightEmission()
 | |
| 				|| state2.useShapeForLightOcclusion()
 | |
| 				|| state1.useShapeForLightOcclusion();
 | |
| 	}
 | |
| 
 | |
| 	public static int getLightBlockInto(BlockState state1, BlockState state2, Direction direction, int defaultReturnValue) {
 | |
| 		boolean bl = isEmptyShape(state1);
 | |
| 		boolean bl2 = isEmptyShape(state2);
 | |
| 		if (bl && bl2) {
 | |
| 			return defaultReturnValue;
 | |
| 		} else {
 | |
| 			VoxelShape voxelShape = bl ? Shapes.empty() : state1.getOcclusionShape();
 | |
| 			VoxelShape voxelShape2 = bl2 ? Shapes.empty() : state2.getOcclusionShape();
 | |
| 			return Shapes.mergedFaceOccludes(voxelShape, voxelShape2, direction) ? 16 : defaultReturnValue;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static VoxelShape getOcclusionShape(BlockState state, Direction direction) {
 | |
| 		return isEmptyShape(state) ? Shapes.empty() : state.getFaceOcclusionShape(direction);
 | |
| 	}
 | |
| 
 | |
| 	protected static boolean isEmptyShape(BlockState state) {
 | |
| 		return !state.canOcclude() || !state.useShapeForLightOcclusion();
 | |
| 	}
 | |
| 
 | |
| 	protected BlockState getState(BlockPos pos) {
 | |
| 		int i = SectionPos.blockToSectionCoord(pos.getX());
 | |
| 		int j = SectionPos.blockToSectionCoord(pos.getZ());
 | |
| 		LightChunk lightChunk = this.getChunk(i, j);
 | |
| 		return lightChunk == null ? Blocks.BEDROCK.defaultBlockState() : lightChunk.getBlockState(pos);
 | |
| 	}
 | |
| 
 | |
| 	protected int getOpacity(BlockState state) {
 | |
| 		return Math.max(1, state.getLightBlock());
 | |
| 	}
 | |
| 
 | |
| 	protected boolean shapeOccludes(BlockState state1, BlockState state2, Direction direction) {
 | |
| 		VoxelShape voxelShape = getOcclusionShape(state1, direction);
 | |
| 		VoxelShape voxelShape2 = getOcclusionShape(state2, direction.getOpposite());
 | |
| 		return Shapes.faceShapeOccludes(voxelShape, voxelShape2);
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	protected LightChunk getChunk(int x, int z) {
 | |
| 		long l = ChunkPos.asLong(x, z);
 | |
| 
 | |
| 		for (int i = 0; i < 2; i++) {
 | |
| 			if (l == this.lastChunkPos[i]) {
 | |
| 				return this.lastChunk[i];
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		LightChunk lightChunk = this.chunkSource.getChunkForLighting(x, z);
 | |
| 
 | |
| 		for (int j = 1; j > 0; j--) {
 | |
| 			this.lastChunkPos[j] = this.lastChunkPos[j - 1];
 | |
| 			this.lastChunk[j] = this.lastChunk[j - 1];
 | |
| 		}
 | |
| 
 | |
| 		this.lastChunkPos[0] = l;
 | |
| 		this.lastChunk[0] = lightChunk;
 | |
| 		return lightChunk;
 | |
| 	}
 | |
| 
 | |
| 	private void clearChunkCache() {
 | |
| 		Arrays.fill(this.lastChunkPos, ChunkPos.INVALID_CHUNK_POS);
 | |
| 		Arrays.fill(this.lastChunk, null);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void checkBlock(BlockPos pos) {
 | |
| 		this.blockNodesToCheck.add(pos.asLong());
 | |
| 	}
 | |
| 
 | |
| 	public void queueSectionData(long sectionPos, @Nullable DataLayer data) {
 | |
| 		this.storage.queueSectionData(sectionPos, data);
 | |
| 	}
 | |
| 
 | |
| 	public void retainData(ChunkPos chunkPos, boolean retainData) {
 | |
| 		this.storage.retainData(SectionPos.getZeroNode(chunkPos.x, chunkPos.z), retainData);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void updateSectionStatus(SectionPos pos, boolean isQueueEmpty) {
 | |
| 		this.storage.updateSectionStatus(pos.asLong(), isQueueEmpty);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void setLightEnabled(ChunkPos chunkPos, boolean lightEnabled) {
 | |
| 		this.storage.setLightEnabled(SectionPos.getZeroNode(chunkPos.x, chunkPos.z), lightEnabled);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public int runLightUpdates() {
 | |
| 		LongIterator longIterator = this.blockNodesToCheck.iterator();
 | |
| 
 | |
| 		while (longIterator.hasNext()) {
 | |
| 			this.checkNode(longIterator.nextLong());
 | |
| 		}
 | |
| 
 | |
| 		this.blockNodesToCheck.clear();
 | |
| 		this.blockNodesToCheck.trim(512);
 | |
| 		int i = 0;
 | |
| 		i += this.propagateDecreases();
 | |
| 		i += this.propagateIncreases();
 | |
| 		this.clearChunkCache();
 | |
| 		this.storage.markNewInconsistencies(this);
 | |
| 		this.storage.swapSectionMap();
 | |
| 		return i;
 | |
| 	}
 | |
| 
 | |
| 	private int propagateIncreases() {
 | |
| 		int i;
 | |
| 		for (i = 0; !this.increaseQueue.isEmpty(); i++) {
 | |
| 			long l = this.increaseQueue.dequeueLong();
 | |
| 			long m = this.increaseQueue.dequeueLong();
 | |
| 			int j = this.storage.getStoredLevel(l);
 | |
| 			int k = LightEngine.QueueEntry.getFromLevel(m);
 | |
| 			if (LightEngine.QueueEntry.isIncreaseFromEmission(m) && j < k) {
 | |
| 				this.storage.setStoredLevel(l, k);
 | |
| 				j = k;
 | |
| 			}
 | |
| 
 | |
| 			if (j == k) {
 | |
| 				this.propagateIncrease(l, m, j);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return i;
 | |
| 	}
 | |
| 
 | |
| 	private int propagateDecreases() {
 | |
| 		int i;
 | |
| 		for (i = 0; !this.decreaseQueue.isEmpty(); i++) {
 | |
| 			long l = this.decreaseQueue.dequeueLong();
 | |
| 			long m = this.decreaseQueue.dequeueLong();
 | |
| 			this.propagateDecrease(l, m);
 | |
| 		}
 | |
| 
 | |
| 		return i;
 | |
| 	}
 | |
| 
 | |
| 	protected void enqueueDecrease(long packedPos1, long packedPos2) {
 | |
| 		this.decreaseQueue.enqueue(packedPos1);
 | |
| 		this.decreaseQueue.enqueue(packedPos2);
 | |
| 	}
 | |
| 
 | |
| 	protected void enqueueIncrease(long packedPos1, long packedPos2) {
 | |
| 		this.increaseQueue.enqueue(packedPos1);
 | |
| 		this.increaseQueue.enqueue(packedPos2);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean hasLightWork() {
 | |
| 		return this.storage.hasInconsistencies() || !this.blockNodesToCheck.isEmpty() || !this.decreaseQueue.isEmpty() || !this.increaseQueue.isEmpty();
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	@Override
 | |
| 	public DataLayer getDataLayerData(SectionPos sectionPos) {
 | |
| 		return this.storage.getDataLayerData(sectionPos.asLong());
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public int getLightValue(BlockPos levelPos) {
 | |
| 		return this.storage.getLightValue(levelPos.asLong());
 | |
| 	}
 | |
| 
 | |
| 	public String getDebugData(long sectionPos) {
 | |
| 		return this.getDebugSectionType(sectionPos).display();
 | |
| 	}
 | |
| 
 | |
| 	public LayerLightSectionStorage.SectionType getDebugSectionType(long sectionPos) {
 | |
| 		return this.storage.getDebugSectionType(sectionPos);
 | |
| 	}
 | |
| 
 | |
| 	protected abstract void checkNode(long packedPos);
 | |
| 
 | |
| 	protected abstract void propagateIncrease(long packedPos, long queueEntry, int lightLevel);
 | |
| 
 | |
| 	protected abstract void propagateDecrease(long packedPos, long lightLevel);
 | |
| 
 | |
| 	public static class QueueEntry {
 | |
| 		private static final int FROM_LEVEL_BITS = 4;
 | |
| 		private static final int DIRECTION_BITS = 6;
 | |
| 		private static final long LEVEL_MASK = 15L;
 | |
| 		private static final long DIRECTIONS_MASK = 1008L;
 | |
| 		private static final long FLAG_FROM_EMPTY_SHAPE = 1024L;
 | |
| 		private static final long FLAG_INCREASE_FROM_EMISSION = 2048L;
 | |
| 
 | |
| 		public static long decreaseSkipOneDirection(int level, Direction direction) {
 | |
| 			long l = withoutDirection(1008L, direction);
 | |
| 			return withLevel(l, level);
 | |
| 		}
 | |
| 
 | |
| 		public static long decreaseAllDirections(int level) {
 | |
| 			return withLevel(1008L, level);
 | |
| 		}
 | |
| 
 | |
| 		public static long increaseLightFromEmission(int level, boolean fromEmptyShape) {
 | |
| 			long l = 1008L;
 | |
| 			l |= 2048L;
 | |
| 			if (fromEmptyShape) {
 | |
| 				l |= 1024L;
 | |
| 			}
 | |
| 
 | |
| 			return withLevel(l, level);
 | |
| 		}
 | |
| 
 | |
| 		public static long increaseSkipOneDirection(int level, boolean fromEmptyShape, Direction direction) {
 | |
| 			long l = withoutDirection(1008L, direction);
 | |
| 			if (fromEmptyShape) {
 | |
| 				l |= 1024L;
 | |
| 			}
 | |
| 
 | |
| 			return withLevel(l, level);
 | |
| 		}
 | |
| 
 | |
| 		public static long increaseOnlyOneDirection(int level, boolean fromEmptyShape, Direction direction) {
 | |
| 			long l = 0L;
 | |
| 			if (fromEmptyShape) {
 | |
| 				l |= 1024L;
 | |
| 			}
 | |
| 
 | |
| 			l = withDirection(l, direction);
 | |
| 			return withLevel(l, level);
 | |
| 		}
 | |
| 
 | |
| 		public static long increaseSkySourceInDirections(boolean down, boolean north, boolean south, boolean west, boolean east) {
 | |
| 			long l = withLevel(0L, 15);
 | |
| 			if (down) {
 | |
| 				l = withDirection(l, Direction.DOWN);
 | |
| 			}
 | |
| 
 | |
| 			if (north) {
 | |
| 				l = withDirection(l, Direction.NORTH);
 | |
| 			}
 | |
| 
 | |
| 			if (south) {
 | |
| 				l = withDirection(l, Direction.SOUTH);
 | |
| 			}
 | |
| 
 | |
| 			if (west) {
 | |
| 				l = withDirection(l, Direction.WEST);
 | |
| 			}
 | |
| 
 | |
| 			if (east) {
 | |
| 				l = withDirection(l, Direction.EAST);
 | |
| 			}
 | |
| 
 | |
| 			return l;
 | |
| 		}
 | |
| 
 | |
| 		public static int getFromLevel(long entry) {
 | |
| 			return (int)(entry & 15L);
 | |
| 		}
 | |
| 
 | |
| 		public static boolean isFromEmptyShape(long entry) {
 | |
| 			return (entry & 1024L) != 0L;
 | |
| 		}
 | |
| 
 | |
| 		public static boolean isIncreaseFromEmission(long entry) {
 | |
| 			return (entry & 2048L) != 0L;
 | |
| 		}
 | |
| 
 | |
| 		public static boolean shouldPropagateInDirection(long entry, Direction direction) {
 | |
| 			return (entry & 1L << direction.ordinal() + 4) != 0L;
 | |
| 		}
 | |
| 
 | |
| 		private static long withLevel(long entry, int level) {
 | |
| 			return entry & -16L | level & 15L;
 | |
| 		}
 | |
| 
 | |
| 		private static long withDirection(long entry, Direction direction) {
 | |
| 			return entry | 1L << direction.ordinal() + 4;
 | |
| 		}
 | |
| 
 | |
| 		private static long withoutDirection(long entry, Direction direction) {
 | |
| 			return entry & ~(1L << direction.ordinal() + 4);
 | |
| 		}
 | |
| 	}
 | |
| }
 |