package assets import ( "encoding/json" "fmt" "os" "strings" ) // FontProvider 字体 provider(font/*.json,原版结构,设计.md §1.3)。 // 支持 type:bitmap(位图字形)与 reference(引用其他 provider 文件)。 type FontProvider struct { Type string `json:"type"` File string `json:"file"` // bitmap:相对 textures/ 的位图路径(无扩展名) Chars []string `json:"chars"` Height int `json:"height"` Ascent int `json:"ascent"` ID string `json:"id"` // reference:目标 provider 文件(minecraft:include/default 等) } // Font 字体定义(reference 已递归展开)。 type Font struct { Providers []FontProvider } // Glyph 一个字符的字形定位信息。 type Glyph struct { File string // 位图资源键(相对 textures/,如 font/ascii) Index int // 在 chars 拼接串中的索引 Height int // 字形高度(px) Ascent int // 基线偏移 } // LoadFont 解析 font/.json 并展开 reference provider(防环)。 func (m *Manager) LoadFont(name string) (*Font, error) { providers, err := m.loadFontProviders(name, map[string]bool{}) if err != nil { return nil, err } return &Font{Providers: providers}, nil } // loadFontProviders 递归加载 provider 文件;stack 用于循环引用检测。 func (m *Manager) loadFontProviders(name string, stack map[string]bool) ([]FontProvider, error) { path, ok := m.find("font", name, ".json") if !ok { return nil, fmt.Errorf("assets.loadFontProviders: 字体 %q 不存在", name) } if stack[name] { return nil, fmt.Errorf("assets.loadFontProviders: 字体引用循环 %q", name) } stack[name] = true defer delete(stack, name) b, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("assets.loadFontProviders %s: %w", name, err) } var def struct { Providers []FontProvider `json:"providers"` } if err := json.Unmarshal(b, &def); err != nil { return nil, fmt.Errorf("assets.loadFontProviders %s: %w", name, err) } var out []FontProvider for _, p := range def.Providers { if p.Type == "reference" { // 展开引用:minecraft:include/default → font/include/default.json refName := strings.TrimPrefix(p.ID, "minecraft:") sub, err := m.loadFontProviders(refName, stack) if err != nil { return nil, err } out = append(out, sub...) continue } out = append(out, p) } return out, nil } // Glyph 查找字符字形(按 providers 顺序,首个命中优先)。 func (f *Font) Glyph(ch rune) (Glyph, bool) { for _, p := range f.Providers { if p.Type != "bitmap" { continue } idx := charIndex(p.Chars, ch) if idx >= 0 { return Glyph{File: p.File, Index: idx, Height: p.Height, Ascent: p.Ascent}, true } } return Glyph{}, false } // charIndex 返回 rune 在 chars 拼接串中的索引;未找到返回 -1。 func charIndex(chars []string, ch rune) int { offset := 0 for _, s := range chars { for i, r := range s { if r == ch { return offset + i } } offset += len([]rune(s)) } return -1 }