176 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.level.lighting;
 | |
| 
 | |
| import it.unimi.dsi.fastutil.longs.Long2ByteMap;
 | |
| import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
 | |
| import it.unimi.dsi.fastutil.longs.LongArrayList;
 | |
| import it.unimi.dsi.fastutil.longs.LongList;
 | |
| import java.util.function.LongPredicate;
 | |
| import net.minecraft.util.Mth;
 | |
| 
 | |
| public abstract class DynamicGraphMinFixedPoint {
 | |
| 	public static final long SOURCE = Long.MAX_VALUE;
 | |
| 	private static final int NO_COMPUTED_LEVEL = 255;
 | |
| 	protected final int levelCount;
 | |
| 	private final LeveledPriorityQueue priorityQueue;
 | |
| 	private final Long2ByteMap computedLevels;
 | |
| 	private volatile boolean hasWork;
 | |
| 
 | |
| 	protected DynamicGraphMinFixedPoint(int firstQueuedLevel, int width, int height) {
 | |
| 		if (firstQueuedLevel >= 254) {
 | |
| 			throw new IllegalArgumentException("Level count must be < 254.");
 | |
| 		} else {
 | |
| 			this.levelCount = firstQueuedLevel;
 | |
| 			this.priorityQueue = new LeveledPriorityQueue(firstQueuedLevel, width);
 | |
| 			this.computedLevels = new Long2ByteOpenHashMap(height, 0.5F) {
 | |
| 				@Override
 | |
| 				protected void rehash(int i) {
 | |
| 					if (i > height) {
 | |
| 						super.rehash(i);
 | |
| 					}
 | |
| 				}
 | |
| 			};
 | |
| 			this.computedLevels.defaultReturnValue((byte)-1);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected void removeFromQueue(long position) {
 | |
| 		int i = this.computedLevels.remove(position) & 255;
 | |
| 		if (i != 255) {
 | |
| 			int j = this.getLevel(position);
 | |
| 			int k = this.calculatePriority(j, i);
 | |
| 			this.priorityQueue.dequeue(position, k, this.levelCount);
 | |
| 			this.hasWork = !this.priorityQueue.isEmpty();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public void removeIf(LongPredicate predicate) {
 | |
| 		LongList longList = new LongArrayList();
 | |
| 		this.computedLevels.keySet().forEach(l -> {
 | |
| 			if (predicate.test(l)) {
 | |
| 				longList.add(l);
 | |
| 			}
 | |
| 		});
 | |
| 		longList.forEach(this::removeFromQueue);
 | |
| 	}
 | |
| 
 | |
| 	private int calculatePriority(int oldLevel, int newLevel) {
 | |
| 		return Math.min(Math.min(oldLevel, newLevel), this.levelCount - 1);
 | |
| 	}
 | |
| 
 | |
| 	protected void checkNode(long levelPos) {
 | |
| 		this.checkEdge(levelPos, levelPos, this.levelCount - 1, false);
 | |
| 	}
 | |
| 
 | |
| 	protected void checkEdge(long fromPos, long toPos, int newLevel, boolean isDecreasing) {
 | |
| 		this.checkEdge(fromPos, toPos, newLevel, this.getLevel(toPos), this.computedLevels.get(toPos) & 255, isDecreasing);
 | |
| 		this.hasWork = !this.priorityQueue.isEmpty();
 | |
| 	}
 | |
| 
 | |
| 	private void checkEdge(long fromPos, long toPos, int newLevel, int previousLevel, int propagationLevel, boolean isDecreasing) {
 | |
| 		if (!this.isSource(toPos)) {
 | |
| 			newLevel = Mth.clamp(newLevel, 0, this.levelCount - 1);
 | |
| 			previousLevel = Mth.clamp(previousLevel, 0, this.levelCount - 1);
 | |
| 			boolean bl = propagationLevel == 255;
 | |
| 			if (bl) {
 | |
| 				propagationLevel = previousLevel;
 | |
| 			}
 | |
| 
 | |
| 			int i;
 | |
| 			if (isDecreasing) {
 | |
| 				i = Math.min(propagationLevel, newLevel);
 | |
| 			} else {
 | |
| 				i = Mth.clamp(this.getComputedLevel(toPos, fromPos, newLevel), 0, this.levelCount - 1);
 | |
| 			}
 | |
| 
 | |
| 			int j = this.calculatePriority(previousLevel, propagationLevel);
 | |
| 			if (previousLevel != i) {
 | |
| 				int k = this.calculatePriority(previousLevel, i);
 | |
| 				if (j != k && !bl) {
 | |
| 					this.priorityQueue.dequeue(toPos, j, k);
 | |
| 				}
 | |
| 
 | |
| 				this.priorityQueue.enqueue(toPos, k);
 | |
| 				this.computedLevels.put(toPos, (byte)i);
 | |
| 			} else if (!bl) {
 | |
| 				this.priorityQueue.dequeue(toPos, j, this.levelCount);
 | |
| 				this.computedLevels.remove(toPos);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected final void checkNeighbor(long fromPos, long toPos, int sourceLevel, boolean isDecreasing) {
 | |
| 		int i = this.computedLevels.get(toPos) & 255;
 | |
| 		int j = Mth.clamp(this.computeLevelFromNeighbor(fromPos, toPos, sourceLevel), 0, this.levelCount - 1);
 | |
| 		if (isDecreasing) {
 | |
| 			this.checkEdge(fromPos, toPos, j, this.getLevel(toPos), i, isDecreasing);
 | |
| 		} else {
 | |
| 			boolean bl = i == 255;
 | |
| 			int k;
 | |
| 			if (bl) {
 | |
| 				k = Mth.clamp(this.getLevel(toPos), 0, this.levelCount - 1);
 | |
| 			} else {
 | |
| 				k = i;
 | |
| 			}
 | |
| 
 | |
| 			if (j == k) {
 | |
| 				this.checkEdge(fromPos, toPos, this.levelCount - 1, bl ? k : this.getLevel(toPos), i, isDecreasing);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected final boolean hasWork() {
 | |
| 		return this.hasWork;
 | |
| 	}
 | |
| 
 | |
| 	protected final int runUpdates(int toUpdateCount) {
 | |
| 		if (this.priorityQueue.isEmpty()) {
 | |
| 			return toUpdateCount;
 | |
| 		} else {
 | |
| 			while (!this.priorityQueue.isEmpty() && toUpdateCount > 0) {
 | |
| 				toUpdateCount--;
 | |
| 				long l = this.priorityQueue.removeFirstLong();
 | |
| 				int i = Mth.clamp(this.getLevel(l), 0, this.levelCount - 1);
 | |
| 				int j = this.computedLevels.remove(l) & 255;
 | |
| 				if (j < i) {
 | |
| 					this.setLevel(l, j);
 | |
| 					this.checkNeighborsAfterUpdate(l, j, true);
 | |
| 				} else if (j > i) {
 | |
| 					this.setLevel(l, this.levelCount - 1);
 | |
| 					if (j != this.levelCount - 1) {
 | |
| 						this.priorityQueue.enqueue(l, this.calculatePriority(this.levelCount - 1, j));
 | |
| 						this.computedLevels.put(l, (byte)j);
 | |
| 					}
 | |
| 
 | |
| 					this.checkNeighborsAfterUpdate(l, i, false);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			this.hasWork = !this.priorityQueue.isEmpty();
 | |
| 			return toUpdateCount;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public int getQueueSize() {
 | |
| 		return this.computedLevels.size();
 | |
| 	}
 | |
| 
 | |
| 	protected boolean isSource(long pos) {
 | |
| 		return pos == Long.MAX_VALUE;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Computes level propagated from neighbors of specified position with given existing level, excluding the given source position.
 | |
| 	 */
 | |
| 	protected abstract int getComputedLevel(long pos, long excludedSourcePos, int level);
 | |
| 
 | |
| 	protected abstract void checkNeighborsAfterUpdate(long pos, int level, boolean isDecreasing);
 | |
| 
 | |
| 	protected abstract int getLevel(long chunkPos);
 | |
| 
 | |
| 	protected abstract void setLevel(long chunkPos, int level);
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns level propagated from start position with specified level to the neighboring end position.
 | |
| 	 */
 | |
| 	protected abstract int computeLevelFromNeighbor(long startPos, long endPos, int startLevel);
 | |
| }
 |