feat(assets): 内容包加载器——原生 pack.mcmeta 素材包、图集/语言/字体/模型解析
This commit is contained in:
135
internal/assets/pack.go
Normal file
135
internal/assets/pack.go
Normal file
@@ -0,0 +1,135 @@
|
||||
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 // 不含任何清单的目录:忽略
|
||||
}
|
||||
Reference in New Issue
Block a user