// 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() }