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