Files
TarnaWijaya 26ed99fda6
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
up3
2026-07-09 08:33:57 +08:00

132 lines
4.7 KiB
Go

package chunk
import "github.com/df-mc/dragonfly/server/block/cube"
// insertBlockLightNodes iterates over the chunk and looks for blocks that have a light level of at least 1.
// If one is found, a node is added for it to the node queue.
func (a *lightArea) insertBlockLightNodes(queue *lightQueue) {
a.iterSubChunks(a.anyLightBlocks, func(pos cube.Pos) {
if level := a.highest(pos, a.br.LightBlock); level > 0 {
queue.push(node(pos, level, BlockLight))
}
})
}
// anyLightBlocks checks if there are any blocks in the SubChunk passed that emit light.
func (a *lightArea) anyLightBlocks(sub *SubChunk) bool {
for _, layer := range sub.storages {
for _, id := range layer.palette.values {
if a.br.LightBlock(id) != 0 {
return true
}
}
}
return false
}
// insertSkyLightNodes iterates over the chunk and inserts a light node anywhere at the highest block in the
// chunk. In addition, any skylight above those nodes will be set to 15.
func (a *lightArea) insertSkyLightNodes(queue *lightQueue) {
a.iterHeightmap(func(x, z int, height, highestNeighbour, highestY, lowestY int) {
pos := cube.Pos{x, height, z}
if height <= a.r.Max() {
// Only set light if we're not at the top of the world.
a.setLight(pos, SkyLight, 15)
if pos[1] > lowestY {
if level := a.highest(pos.Sub(cube.Pos{0, 1}), a.br.FilteringBlock); level != 15 && level != 0 {
// If we hit a block like water or leaves (something that diffuses but does not block light), we
// need a node above this block regardless of the neighbours.
queue.push(node(pos, 15, SkyLight))
}
}
}
for y := pos[1]; y < highestY; y++ {
// We can do a bit of an optimisation here: We don't need to insert nodes if the neighbours are
// lower than the current one, on the same Y level, or one level higher, because light in
// this column can't spread below that anyway.
if pos[1]++; pos[1] < highestNeighbour {
queue.push(node(pos, 15, SkyLight))
continue
}
// Fill the rest with full skylight.
a.setLight(pos, SkyLight, 15)
}
})
}
// insertLightSpreadingNodes inserts light nodes into the node queue passed which, when propagated, will
// spread into the neighbouring chunks.
func (a *lightArea) insertLightSpreadingNodes(queue *lightQueue, lt light) {
a.iterEdges(a.nodesNeeded(lt), func(pa, pb cube.Pos) {
la, lb := a.light(pa, lt), a.light(pb, lt)
if la == lb || la-1 == lb || lb-1 == la {
// No chance for this to spread. Don't check for the highest filtering blocks on the side.
return
}
if filter := a.highest(pb, a.br.FilteringBlock) + 1; la > filter && la-filter > lb {
queue.push(node(pb, la-filter, lt))
} else if filter = a.highest(pa, a.br.FilteringBlock) + 1; lb > filter && lb-filter > la {
queue.push(node(pa, lb-filter, lt))
}
})
}
// nodesNeeded checks if any light nodes of a specific light type are needed between two neighbouring SubChunks when
// spreading light between them.
func (a *lightArea) nodesNeeded(lt light) func(sa, sb *SubChunk) bool {
if lt == SkyLight {
return func(sa, sb *SubChunk) bool {
return &sa.skyLight[0] != &sb.skyLight[0]
}
}
return func(sa, sb *SubChunk) bool {
// Don't add nodes if both sub chunks are either both fully filled with light or have no light at all.
return &sa.blockLight[0] != &sb.blockLight[0]
}
}
// propagate spreads the next light node in the node queue passed through the lightArea a. propagate adds the neighbours
// of the node to the queue for as long as it is able to spread.
func (a *lightArea) propagate(queue *lightQueue) {
n, ok := queue.pop()
if !ok {
return
}
if a.light(n.pos, n.lt) >= n.level {
return
}
a.setLight(n.pos, n.lt, n.level)
x, y, z := n.pos[0], n.pos[1], n.pos[2]
a.propagateNeighbour(queue, n.lt, n.level, x+1, y, z)
a.propagateNeighbour(queue, n.lt, n.level, x-1, y, z)
a.propagateNeighbour(queue, n.lt, n.level, x, y+1, z)
a.propagateNeighbour(queue, n.lt, n.level, x, y-1, z)
a.propagateNeighbour(queue, n.lt, n.level, x, y, z+1)
a.propagateNeighbour(queue, n.lt, n.level, x, y, z-1)
}
func (a *lightArea) propagateNeighbour(queue *lightQueue, lt light, level uint8, x, y, z int) {
if y < a.r.Min() || y > a.r.Max() || x < a.baseX || z < a.baseZ || x >= a.baseX+a.w*16 || z >= a.baseZ+a.w*16 {
return
}
pos := cube.Pos{x, y, z}
filter := a.highest(pos, a.br.FilteringBlock) + 1
if level > filter && a.light(pos, lt) < level-filter {
queue.push(node(pos, level-filter, lt))
}
}
// lightNode is a node pushed to the queue which is used to propagate light.
type lightNode struct {
pos cube.Pos
lt light
level uint8
}
// node creates a new lightNode using the position, level and light type passed.
func node(pos cube.Pos, level uint8, lt light) lightNode {
return lightNode{pos: pos, level: level, lt: lt}
}