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

136 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 // 不含任何清单的目录:忽略
}