Files
ngzz-mc/internal/chunk/chunk.go

137 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package chunk 区块数据结构:一维数组 + 每区块独立锁(场景/区块管理.md §2)。
//
// 布局:16×16×256 一维 []block.State(索引 (y*16+z)*16+x),
// 天空光/方块光各 4-bit 打包存储(每字节 2 个方块)。
package chunk
import (
"sync"
"sync/atomic"
"mc/internal/block"
)
// 区块尺寸常量(设计.md §4.1)。
const (
SizeX = 16 // 水平 X 大小
SizeZ = 16 // 水平 Z 大小
Height = 256 // 垂直高度
SectionY = Height / 16 // section 数量(16×16×16)
)
// Chunk 一个区块。锁粒度:每 Chunk 一个 RWMutex(禁止全局锁,性能与内存.md §3)。
type Chunk struct {
CX, CZ int32
mu sync.RWMutex
blocks []block.State // 方块状态(一维数组,cache 友好)
sky []uint8 // 天空光 4-bit 打包
blockLt []uint8 // 方块光 4-bit 打包
Dirty atomic.Bool // mesh 需重建(渲染.md §3.3)
LightDirty atomic.Bool // 光照需重算(光照.md)
SaveDirty atomic.Bool // 需写盘(存档与持久化.md §2)
}
// New 创建全空气区块。
func New(cx, cz int32) *Chunk {
return &Chunk{
CX: cx,
CZ: cz,
blocks: make([]block.State, SizeX*SizeZ*Height),
sky: make([]uint8, SizeX*SizeZ*Height/2),
blockLt: make([]uint8, SizeX*SizeZ*Height/2),
}
}
// Index 计算方块数组索引:(y*16+z)*16+x。
func Index(x, y, z int) int { return (y*SizeZ+z)*SizeX + x }
// Block 读取方块(读锁)。
func (c *Chunk) Block(x, y, z int) block.State {
c.mu.RLock()
defer c.mu.RUnlock()
return c.blocks[Index(x, y, z)]
}
// SetBlock 写入方块并标脏(mesh + 光照 + 存档)。
func (c *Chunk) SetBlock(x, y, z int, s block.State) {
c.mu.Lock()
c.blocks[Index(x, y, z)] = s
c.mu.Unlock()
c.Dirty.Store(true)
c.LightDirty.Store(true)
c.SaveDirty.Store(true)
}
// Fill 初始化填充(生成器专用,不标存档脏:生成结果属纯函数可重算)。
func (c *Chunk) Fill(x, y, z int, s block.State) {
c.blocks[Index(x, y, z)] = s
}
// FillAll 整块填充(生成器初始化)。
func (c *Chunk) FillAll(s block.State) {
for i := range c.blocks {
c.blocks[i] = s
}
}
// SkyLight 读取天空光(4-bit,0–15)。
func (c *Chunk) SkyLight(x, y, z int) uint8 { return nibble(c.sky, Index(x, y, z)) }
// SetSkyLight 写入天空光。
func (c *Chunk) SetSkyLight(x, y, z int, v uint8) { setNibble(c.sky, Index(x, y, z), v) }
// BlockLight 读取方块光(4-bit,0–15)。
func (c *Chunk) BlockLight(x, y, z int) uint8 { return nibble(c.blockLt, Index(x, y, z)) }
// SetBlockLight 写入方块光。
func (c *Chunk) SetBlockLight(x, y, z int, v uint8) { setNibble(c.blockLt, Index(x, y, z), v) }
// nibble 读取半字节(偶数索引 → 低 4 位,奇数索引 → 高 4 位)。
func nibble(arr []uint8, idx int) uint8 {
if idx&1 == 0 {
return arr[idx/2] & 0xF
}
return arr[idx/2] >> 4
}
// setNibble 写入半字节。
func setNibble(arr []uint8, idx int, v uint8) {
v &= 0xF
if idx&1 == 0 {
arr[idx/2] = (arr[idx/2] & 0xF0) | v
} else {
arr[idx/2] = (arr[idx/2] & 0x0F) | (v << 4)
}
}
// Snapshot 无锁快照(mesh 构建 goroutine 读取用;调用方需保证期间无写入)。
func (c *Chunk) Snapshot() []block.State {
c.mu.RLock()
defer c.mu.RUnlock()
out := make([]block.State, len(c.blocks))
copy(out, c.blocks)
return out
}
// LightBytes 复制天空光/方块光原始字节(存档序列化用)。
func (c *Chunk) LightBytes() (sky, blockLt []byte) {
c.mu.RLock()
defer c.mu.RUnlock()
sky = make([]byte, len(c.sky))
bl := make([]byte, len(c.blockLt))
copy(sky, c.sky)
copy(bl, c.blockLt)
return sky, bl
}
// SetAll 整体覆盖方块与光照数据(存档载入用;不标脏——载入即当前状态)。
func (c *Chunk) SetAll(blocks []block.State, sky, blockLt []byte) {
c.mu.Lock()
copy(c.blocks, blocks)
copy(c.sky, sky)
copy(c.blockLt, blockLt)
c.mu.Unlock()
}