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