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

136 lines
4.0 KiB
Go
Raw Normal View History

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 // 不含任何清单的目录:忽略
}