minecraft-src/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java
2025-07-04 01:41:11 +03:00

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);
}