feat(world): 区块管理器——异步生成流水线、优先级队列、时间预算、火把光源联动(含测试)
This commit is contained in:
101
internal/world/world_test.go
Normal file
101
internal/world/world_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user