github.com/df-mc/dragonfly@v0.9.13/server/world/chunk/light.go (about) 1 package chunk 2 3 import ( 4 "container/list" 5 "github.com/df-mc/dragonfly/server/block/cube" 6 ) 7 8 // insertBlockLightNodes iterates over the chunk and looks for blocks that have a light level of at least 1. 9 // If one is found, a node is added for it to the node queue. 10 func (a *lightArea) insertBlockLightNodes(queue *list.List) { 11 a.iterSubChunks(anyLightBlocks, func(pos cube.Pos) { 12 if level := a.highest(pos, LightBlocks); level > 0 { 13 queue.PushBack(node(pos, level, BlockLight)) 14 } 15 }) 16 } 17 18 // anyLightBlocks checks if there are any blocks in the SubChunk passed that emit light. 19 func anyLightBlocks(sub *SubChunk) bool { 20 for _, layer := range sub.storages { 21 for _, id := range layer.palette.values { 22 if LightBlocks[id] != 0 { 23 return true 24 } 25 } 26 } 27 return false 28 } 29 30 // insertSkyLightNodes iterates over the chunk and inserts a light node anywhere at the highest block in the 31 // chunk. In addition, any skylight above those nodes will be set to 15. 32 func (a *lightArea) insertSkyLightNodes(queue *list.List) { 33 a.iterHeightmap(func(x, z int, height, highestNeighbour, highestY, lowestY int) { 34 // If we hit a block like water or leaves (something that diffuses but does not block light), we 35 // need a node above this block regardless of the neighbours. 36 pos := cube.Pos{x, height, z} 37 a.setLight(pos, SkyLight, 15) 38 39 if pos[1] > lowestY { 40 if level := a.highest(pos.Sub(cube.Pos{0, 1}), FilteringBlocks); level != 15 && level != 0 { 41 queue.PushBack(node(pos, 15, SkyLight)) 42 } 43 } 44 for y := pos[1]; y < highestY; y++ { 45 // We can do a bit of an optimisation here: We don't need to insert nodes if the neighbours are 46 // lower than the current one, on the same Y level, or one level higher, because light in 47 // this column can't spread below that anyway. 48 if pos[1]++; pos[1] < highestNeighbour { 49 queue.PushBack(node(pos, 15, SkyLight)) 50 continue 51 } 52 // Fill the rest with full skylight. 53 a.setLight(pos, SkyLight, 15) 54 } 55 }) 56 } 57 58 // insertLightSpreadingNodes inserts light nodes into the node queue passed which, when propagated, will 59 // spread into the neighbouring chunks. 60 func (a *lightArea) insertLightSpreadingNodes(queue *list.List, lt light) { 61 a.iterEdges(a.nodesNeeded(lt), func(pa, pb cube.Pos) { 62 la, lb := a.light(pa, lt), a.light(pb, lt) 63 if la == lb || la-1 == lb || lb-1 == la { 64 // No chance for this to spread. Don't check for the highest filtering blocks on the side. 65 return 66 } 67 if filter := a.highest(pb, FilteringBlocks) + 1; la > filter && la-filter > lb { 68 queue.PushBack(node(pb, la-filter, lt)) 69 } else if filter = a.highest(pa, FilteringBlocks) + 1; lb > filter && lb-filter > la { 70 queue.PushBack(node(pa, lb-filter, lt)) 71 } 72 }) 73 } 74 75 // nodesNeeded checks if any light nodes of a specific light type are needed between two neighbouring SubChunks when 76 // spreading light between them. 77 func (a *lightArea) nodesNeeded(lt light) func(sa, sb *SubChunk) bool { 78 if lt == SkyLight { 79 return func(sa, sb *SubChunk) bool { 80 return &sa.skyLight[0] != &sb.skyLight[0] 81 } 82 } 83 return func(sa, sb *SubChunk) bool { 84 // Don't add nodes if both sub chunks are either both fully filled with light or have no light at all. 85 return &sa.blockLight[0] != &sb.blockLight[0] 86 } 87 } 88 89 // propagate spreads the next light node in the node queue passed through the lightArea a. propagate adds the neighbours 90 // of the node to the queue for as long as it is able to spread. 91 func (a *lightArea) propagate(queue *list.List) { 92 n := queue.Remove(queue.Front()).(lightNode) 93 if a.light(n.pos, n.lt) >= n.level { 94 return 95 } 96 a.setLight(n.pos, n.lt, n.level) 97 98 for _, neighbour := range a.neighbours(n) { 99 filter := a.highest(neighbour.pos, FilteringBlocks) + 1 100 if n.level > filter && a.light(neighbour.pos, n.lt) < n.level-filter { 101 neighbour.level = n.level - filter 102 queue.PushBack(neighbour) 103 } 104 } 105 } 106 107 // lightNode is a node pushed to the queue which is used to propagate light. 108 type lightNode struct { 109 pos cube.Pos 110 lt light 111 level uint8 112 } 113 114 // node creates a new lightNode using the position, level and light type passed. 115 func node(pos cube.Pos, level uint8, lt light) lightNode { 116 return lightNode{pos: pos, level: level, lt: lt} 117 }