Files
ngzz-mc/internal/block/registry.go

143 lines
4.4 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 block 方块注册表:加载 blocks.json,提供方块定义与状态查询(设计.md §6.1)。
//
// 设计要点:
// - 运行时状态 = 数字 ID(uint16)+ meta(uint8),打包为 State(uint32);
// - ID 一旦发布不可变(ID 稳定性铁律,热更新.md §4.4);
// - isSolid / isOpaque / light 等判定为「单一来源」,光照、物理、渲染共用。
package block
import (
"encoding/json"
"fmt"
"os"
)
// State 运行时方块状态:ID(低 16 位)+ meta(高 8 位)。
type State uint32
// Air 空气(ID 0)。
const Air State = 0
// NewState 打包状态。
func NewState(id uint16, meta uint8) State { return State(uint32(id) | uint32(meta)<<16) }
// ID 返回方块 ID。
func (s State) ID() uint16 { return uint16(s & 0xFFFF) }
// Meta 返回附加数据(朝向/含水/阶段等)。
func (s State) Meta() uint8 { return uint8(s >> 16) }
// Def 方块定义(blocks.json 条目,设计.md §2.3)。
type Def struct {
ID uint16 `json:"id"`
Name string `json:"-"`
Hardness float64 `json:"hardness"`
Resistance float64 `json:"resistance"`
Light uint8 `json:"light"`
Transparent bool `json:"transparent"`
Solid bool `json:"solid"`
Cutout bool `json:"cutout"`
Translucent bool `json:"translucent"`
Fluid bool `json:"fluid"`
RequiredTier int `json:"required_tier"`
Drops string `json:"drops"`
Textures map[string]string `json:"textures"`
}
// Registry 方块注册表。
type Registry struct {
byID []Def
byName map[string]uint16
}
// Load 解析 blocks.json 构建注册表(按 id 索引;ID 0 保留给空气,其余不要求连续但必须有唯一)。
func Load(path string) (*Registry, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("block.Load %s: %w", path, err)
}
var doc struct {
Blocks map[string]Def `json:"blocks"`
}
if err := json.Unmarshal(b, &doc); err != nil {
return nil, fmt.Errorf("block.Load %s: %w", path, err)
}
r := &Registry{byName: make(map[string]uint16, len(doc.Blocks))}
maxID := uint16(0)
for name, d := range doc.Blocks {
if d.ID == 0 {
return nil, fmt.Errorf("block.Load: 方块 %q 使用保留 ID 0(空气)", name)
}
if d.ID > maxID {
maxID = d.ID
}
if _, dup := r.byName[name]; dup {
return nil, fmt.Errorf("block.Load: 方块重名 %q", name)
}
r.byName[name] = d.ID
}
// 按 ID 建立索引(下标 0 = 空气零值)
r.byID = make([]Def, int(maxID)+1)
for name, d := range doc.Blocks {
d.Name = name
if r.byID[d.ID].ID != 0 {
return nil, fmt.Errorf("block.Load: ID %d 被重复使用", d.ID)
}
r.byID[d.ID] = d
}
return r, nil
}
// Get 按 ID 取定义;越界返回零值(视为空气)。
func (r *Registry) Get(id uint16) Def {
if int(id) < len(r.byID) {
return r.byID[id]
}
return Def{}
}
// ID 按名称取 ID。
func (r *Registry) ID(name string) (uint16, bool) {
id, ok := r.byName[name]
return id, ok
}
// Name 按 ID 取名称。
func (r *Registry) Name(id uint16) string { return r.Get(id).Name }
// IsSolid 实体可站立/碰撞判定(场景/物理与碰撞.md §2)。
func (r *Registry) IsSolid(s State) bool { return s != Air && r.Get(s.ID()).Solid }
// IsOpaque 完全遮光判定(场景/光照.md §7 分类表)。
func (r *Registry) IsOpaque(s State) bool {
if s == Air {
return false
}
d := r.Get(s.ID())
return !d.Transparent
}
// Light 光源亮度(0–15,场景/光照.md §4)。
func (r *Registry) Light(s State) uint8 { return r.Get(s.ID()).Light }
// IsCutout alpha test 类型(树叶/玻璃,渲染.md §6)。
func (r *Registry) IsCutout(s State) bool { return r.Get(s.ID()).Cutout }
// IsTranslucent 半透明类型(水/彩色玻璃,需要排序 pass)。
func (r *Registry) IsTranslucent(s State) bool { return r.Get(s.ID()).Translucent }
// IsFluid 液体判定(水/岩浆)。
func (r *Registry) IsFluid(s State) bool { return r.Get(s.ID()).Fluid }
// Texture 取方块某个面的纹理键(如 "up"/"side"/"all")。
func (r *Registry) Texture(s State, face string) string {
d := r.Get(s.ID())
if t, ok := d.Textures[face]; ok {
return t
}
if t, ok := d.Textures["all"]; ok {
return t
}
return "block/" + d.Name
}