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

101 lines
3.3 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 assets 内容包加载与资源管理。
//
// 职责(设计.md §1.3 内容包统一格式):
// - 扫描 mods/ 与 resourcepacks/,识别「原生 pack.mcmeta 素材包」与「扩展 manifest.json 内容包」;
// - 按优先级链(功能 Mod 资源 > 素材包 > 光影包 > 默认嵌入)解析资源;
// - 提供纹理、模型、方块状态、图集、语言、字体的统一访问。
//
// 引用约定:资源键使用原版命名(如 "block/stone"、"models/block/stone"
// 不含命名空间前缀与扩展名。
package assets
import (
"fmt"
"os"
"path/filepath"
"sort"
)
// Manager 资源管理器:持有按优先级排序的内容包列表与嵌入资源根目录。
type Manager struct {
packs []*Pack // 已排序内容包(高优先级在前)
embeddedRoot string // 嵌入/默认资源根(如 assets/,命名空间 minecraft
}
// New 创建空的资源管理器。
func New() *Manager { return &Manager{} }
// Load 扫描全部包目录并按优先级排序;目录不存在时视为无包(默认回退策略)。
func (m *Manager) Load(embeddedRoot string, packDirs ...string) error {
m.embeddedRoot = embeddedRoot
var packs []*Pack
for _, dir := range packDirs {
ps, err := Discover(dir)
if err != nil {
return fmt.Errorf("assets.Manager.Load %s: %w", dir, err)
}
packs = append(packs, ps...)
}
// 排序确定性Mod(0) < 素材包(1) < 光影包(2);同类型 priority 大者优先,再按 ID 字典序。
sort.SliceStable(packs, func(i, j int) bool {
a, b := packs[i], packs[j]
if a.Type != b.Type {
return a.Type < b.Type
}
if a.Priority() != b.Priority() {
return a.Priority() > b.Priority()
}
return a.ID < b.ID
})
m.packs = packs
return nil
}
// Packs 返回已加载内容包(只读视图,供日志/调试)。
func (m *Manager) Packs() []*Pack { return m.packs }
// namespaces 返回包的资源命名空间候选(原版包固定 minecraft
func (p *Pack) namespaces() []string {
if p.Manifest != nil && p.Manifest.ResourceNamespace != "" {
return []string{p.Manifest.ResourceNamespace, "minecraft"}
}
return []string{"minecraft"}
}
// find 按优先级链查找 kind 类别下的资源 key扩展名 ext返回首个存在的文件路径。
// kind 取值textures / models / blockstates / lang / font / atlases / shaders。
func (m *Manager) find(kind, key, ext string) (string, bool) {
for _, p := range m.packs {
for _, ns := range p.namespaces() {
path := filepath.Join(p.Root, "assets", ns, kind, key+ext)
if fileExists(path) {
return path, true
}
}
}
// 默认嵌入资源assets/ 根,命名空间 minecraft
if m.embeddedRoot != "" {
path := filepath.Join(m.embeddedRoot, kind, key+ext)
if fileExists(path) {
return path, true
}
}
return "", false
}
// ResolveFile 通用资源解析入口。
func (m *Manager) ResolveFile(kind, key, ext string) (string, bool) {
return m.find(kind, key, ext)
}
// ResolveTexture 解析纹理键(如 "block/stone")到 PNG 文件路径。
func (m *Manager) ResolveTexture(key string) (string, bool) {
return m.find("textures", key, ".png")
}
// fileExists 判断路径是否为已存在的普通文件。
func fileExists(path string) bool {
st, err := os.Stat(path)
return err == nil && !st.IsDir()
}