feat(world): 区块管理器——异步生成流水线、优先级队列、时间预算、火把光源联动(含测试)

This commit is contained in:
NianGao Dev
2026-08-15 22:37:15 +08:00
parent 90b22f8c87
commit 8bdd5b047f
2 changed files with 412 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
package world
import (
"path/filepath"
"testing"
"time"
"mc/internal/block"
"mc/internal/worldgen"
)
// loadWorldReg 加载注册表。
func loadWorldReg(t *testing.T) *block.Registry {
t.Helper()
r, err := block.Load(filepath.Join("..", "..", "assets", "config", "blocks.json"))
if err != nil {
t.Fatalf("加载注册表失败: %v", err)
}
return r
}
// newTestWorld 创建小视距世界。
func newTestWorld(t *testing.T, view int) *World {
t.Helper()
reg := loadWorldReg(t)
gen, err := worldgen.New(reg, 42, 0.005, 0.01, 0.1)
if err != nil {
t.Fatalf("创建生成器失败: %v", err)
}
w, err := New(reg, gen, view)
if err != nil {
t.Fatalf("创建世界失败: %v", err)
}
t.Cleanup(w.Close)
return w
}
// waitChunk 轮询 Update 直到指定区块激活(异步生成流水线)。
func waitChunk(t *testing.T, w *World, cx, cz int32) {
t.Helper()
deadline := time.Now().Add(15 * time.Second)
for time.Now().Before(deadline) {
w.Update(float64(cx*16+8), 64, float64(cz*16+8), 8*time.Millisecond)
if w.chunkOf(int32(cx*16+8), int32(cz*16+8)) != nil {
return
}
time.Sleep(2 * time.Millisecond)
}
t.Fatalf("区块 (%d,%d) 未在时限内生成", cx, cz)
}
// TestLoadChunk 验证异步生成:基岩层存在、地表可站。
func TestLoadChunk(t *testing.T) {
w := newTestWorld(t, 3)
waitChunk(t, w, 0, 0)
bedrock, _ := w.reg.ID("bedrock")
if w.Block(8, 0, 8).ID() != bedrock {
t.Fatal("y=0 应为基岩")
}
// 地表:从顶向下找第一个非空气方块,其下应有实体方块
var surf int32 = -1
for y := int32(255); y >= 0; y-- {
if w.Block(8, y, 8) != block.Air {
surf = y
break
}
}
if surf < 8 {
t.Fatalf("地表高度异常: %d", surf)
}
}
// TestTorchLightCycle 验证火把放置→照亮→移除→重算的完整闭环(方块交互与动画.md §9)。
func TestTorchLightCycle(t *testing.T) {
w := newTestWorld(t, 2)
waitChunk(t, w, 0, 0)
// 找地表
var surf int32
for y := int32(255); y >= 0; y-- {
if w.Block(8, y, 8) != block.Air {
surf = y
break
}
}
// 放置火把:地表上一格(地表方块不透明不受光,其周围空气受光)
torch, _ := w.reg.ID("torch")
w.SetBlock(8, surf+1, 8, block.NewState(torch, 0))
if got := w.BlockLight(8, surf+1, 8); got != 14 {
t.Fatalf("火把自身光期望 14,实际 %d", got)
}
if got := w.BlockLight(7, surf+1, 8); got != 13 {
t.Fatalf("火把侧邻光期望 13,实际 %d", got)
}
// 移除火把:光应清零(relight)
w.SetBlock(8, surf+1, 8, block.Air)
if got := w.BlockLight(7, surf+1, 8); got != 0 {
t.Fatalf("移除火把后侧邻光期望 0,实际 %d", got)
}
}