Light Queue

基本信息

com.mitchej123.supernova.light.LightQueue(public final class)—— 按区块组织的插入序任务队列,单生产者 / 单消费者: 主线程投喂,该 lane 的工作线程消费。

字段 类型 作用
tasksByChunk Long2ObjectLinkedOpenHashMap<ChunkTasks> 权威表,保持插入序
inFlight ChunkTasks 已出队但未完成的那唯一一个任务
workAvailable Semaphore(0) 唤醒工作线程
blockChangeKeys LongArrayFIFOQueue 方块变更键
initialLightKeys LongArrayFIFOQueue 初始光照键

inFlight 之所以能只有一个:源码注释说明队列单消费者, 且一个任务在下一个出队前必定已完成。

两条独立的键队列

blockChangeKeys 与 initialLightKeys 分离,使 removeFirstBlockChangeTask() 能跳过纯初始光照任务 —— 源码注释:player block placement/breaking outranks chunk loading。 hasInitialLightTask() 则反过来让到达的初始光照抢占边缘检查阶段。

两条键队列只 push 不 remove(false -> true 翻转时入队)。 因此键可能指向已消失或已不合格的任务,读取方需自行跳过。

世代与重试:isSuperseded

isSuperseded(key, generation, edgePass) 判定某世代是否已被更新世代取代。 比较符号刻意不同:

通道 比较 效果
edgePass = false(传播) existing.lightGeneration > generation 严格大于才取代,溢出重试可替换自己这一代
edgePass = true(边缘) existing.lightGeneration >= generation 大于等于即取代,边缘检查不会插到传播前面

lightGeneration == 0L 视为「无世代」,一律不取代。

任务入队入口

方法 作用
queueBlockChange(x,y,z) 记录位置键 (x&15) | ((z&15)<<4) | (y<<8) 到 IntOpenHashSet
queueSectionChange(cx, sectionY, cz, empty) 写 Boolean[WorldUtil.getTotalSections()] 空区表
queueChunkLight(...) 首次排入初始光照
requeueChunkLight(..., previousAttempts) BFS 队列溢出后重试同一世代,attempts = max(attempts, previous+1)
queueInitialLightEdges(...) 所有 lane 传播完成后的边缘对账
queueEdgeCheckAllSections(cx,cz) 把全部 section 塞进 queuedEdgeChecks

queueChunkLight / requeueChunkLight 显式重置 edgePass = false 与 attempts = 0(或 previousAttempts+1), 因为 getOrCreate 跨世代返回同一个 task 对象,重试预算必须显式重置。

完成与查询

方法 说明
completeTask(task) 由工作线程 finally 调用,抛异常也不会搁浅区块
pendingWorkFuture(long key) 返回 SettableFuture<Void>,不在队列则 null
hasPendingWork(cx, cz) 在表里或正是 inFlight
removeChunk(cx, cz) 移除并 onComplete.set(null)
waitForWork() / wakeUp() Semaphore.acquire() 后 drainPermits()

chunksQueued 统计在 getOrCreate 新建 task 时自增 (stats 可为 null,见 Light Stats)。

相关条目

源码

  • src/main/java/com/mitchej123/supernova/light/LightQueue.java