package assets import ( "encoding/json" "fmt" "os" "path/filepath" ) // PackType 内容包类型(设计.md §1.3)。 type PackType uint8 const ( // PackMod 功能 Mod(脚本 + 数据 + 资源)。 PackMod PackType = iota // PackResource 素材包(覆盖默认纹理/模型/语言/音效)。 PackResource // PackShader 光影包(覆盖着色器管线)。 PackShader ) // Pack 一个内容包(目录形式;zip 支持后置)。 type Pack struct { ID string // 目录名或 manifest.id Type PackType // 包类型 Root string // 包根目录(绝对路径) Meta *PackMeta // pack.mcmeta(原生素材包) Manifest *Manifest // manifest.json(扩展格式,可选) } // Priority 返回包优先级:manifest.priority,缺省为 0。 func (p *Pack) Priority() int { if p.Manifest != nil { return p.Manifest.Priority } return 0 } // PackMeta 原生 Minecraft 资源包元数据(pack.mcmeta)。 type PackMeta struct { Pack struct { PackFormat int `json:"pack_format"` // 1.20.5 = 34 Description string `json:"description"` SupportedFormats []int `json:"supported_formats"` } `json:"pack"` } // Manifest 扩展内容包清单(manifest.json,设计.md §1.3)。 type Manifest struct { Type string `json:"type"` // mod / resource / shader ID string `json:"id"` Name string `json:"name"` Version string `json:"version"` APIVersion int `json:"api_version"` Entry string `json:"entry"` Priority int `json:"priority"` ResourceNamespace string `json:"resource_namespace"` Shaders map[string]string `json:"shaders"` Depends []struct { ID string `json:"id"` MinVersion string `json:"min_version"` } `json:"depends"` Permissions []string `json:"permissions"` Description string `json:"description"` } // Discover 扫描目录发现内容包;无清单的子目录被忽略(不报错)。 // 目录不存在视为没有包(默认回退);扫描顺序按目录名排序保证确定性。 func Discover(dir string) ([]*Pack, error) { entries, err := os.ReadDir(dir) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, fmt.Errorf("assets.Discover %s: %w", dir, err) } var packs []*Pack for _, e := range entries { if !e.IsDir() { continue // TODO: 支持 .zip 内容包(设计.md §1.3) } p, err := loadPack(e.Name(), filepath.Join(dir, e.Name())) if err != nil { return nil, fmt.Errorf("assets.Discover %s/%s: %w", dir, e.Name(), err) } if p != nil { packs = append(packs, p) } } return packs, nil } // loadPack 识别单个目录的内容包类型并解析元数据。 func loadPack(name, root string) (*Pack, error) { // 原生格式:pack.mcmeta(用户实际投放的主要格式) mcmeta := filepath.Join(root, "pack.mcmeta") if fileExists(mcmeta) { b, err := os.ReadFile(mcmeta) if err != nil { return nil, fmt.Errorf("读取 pack.mcmeta: %w", err) } var meta PackMeta if err := json.Unmarshal(b, &meta); err != nil { return nil, fmt.Errorf("解析 pack.mcmeta: %w", err) } return &Pack{ID: name, Type: PackResource, Root: root, Meta: &meta}, nil } // 扩展格式:manifest.json(功能 Mod / 光影包) manifest := filepath.Join(root, "manifest.json") if fileExists(manifest) { b, err := os.ReadFile(manifest) if err != nil { return nil, fmt.Errorf("读取 manifest.json: %w", err) } var mf Manifest if err := json.Unmarshal(b, &mf); err != nil { return nil, fmt.Errorf("解析 manifest.json: %w", err) } t := PackResource switch mf.Type { case "mod": t = PackMod case "shader": t = PackShader case "", "resource": t = PackResource } id := name if mf.ID != "" { id = mf.ID } return &Pack{ID: id, Type: t, Root: root, Manifest: &mf}, nil } return nil, nil // 不含任何清单的目录:忽略 }