panel v0.9.1 — open-source game server manager
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
package empyrionitems
|
||||
|
||||
// Template (recipe) parser for Empyrion's `Templates.ecf`.
|
||||
// Format snippet:
|
||||
//
|
||||
// { +Template Name: PlasticMaterial
|
||||
// BaseItem: true
|
||||
// CraftTime: 2
|
||||
// Target: "SurvC,SmallC,HoverC,BaseC,LargeC,AdvC"
|
||||
// { Child Inputs
|
||||
// CrushedStone: 1
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// We extract Name + outer props (CraftTime, Target, BaseItem,
|
||||
// OutputCount, etc.) and the Inputs map.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Template struct {
|
||||
Name string `json:"name"`
|
||||
BaseItem bool `json:"baseItem,omitempty"`
|
||||
CraftTime string `json:"craftTime,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
OutputCount string `json:"outputCount,omitempty"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Inputs map[string]string `json:"inputs"` // ingredientName → count token
|
||||
Extras map[string]string `json:"extras,omitempty"`
|
||||
}
|
||||
|
||||
type TemplateIndex struct {
|
||||
Templates []Template
|
||||
byName map[string]*Template
|
||||
}
|
||||
|
||||
func LoadTemplatesFromFile(path string) (*TemplateIndex, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return LoadTemplatesFromReader(f)
|
||||
}
|
||||
|
||||
func LoadTemplatesFromReader(r io.Reader) (*TemplateIndex, error) {
|
||||
idx := &TemplateIndex{byName: map[string]*Template{}}
|
||||
sc := bufio.NewScanner(r)
|
||||
sc.Buffer(make([]byte, 1024*1024), 1024*1024)
|
||||
|
||||
var current *Template
|
||||
var inInputs bool
|
||||
var depth int
|
||||
|
||||
for sc.Scan() {
|
||||
line := stripComment(sc.Text())
|
||||
trim := strings.TrimSpace(line)
|
||||
if trim == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if current == nil {
|
||||
if strings.HasPrefix(trim, "{ +Template ") || strings.HasPrefix(trim, "{+Template ") || strings.HasPrefix(trim, "{ Template ") {
|
||||
body := strings.TrimPrefix(trim, "{ +Template ")
|
||||
body = strings.TrimPrefix(body, "{+Template ")
|
||||
body = strings.TrimPrefix(body, "{ Template ")
|
||||
body = strings.TrimSpace(body)
|
||||
current = &Template{Inputs: map[string]string{}, Extras: map[string]string{}}
|
||||
for _, kv := range splitTopLevel(body, ',') {
|
||||
parts := strings.SplitN(strings.TrimSpace(kv), ":", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
k := strings.TrimSpace(parts[0])
|
||||
v := stripQuotes(strings.TrimSpace(parts[1]))
|
||||
switch strings.ToLower(k) {
|
||||
case "name":
|
||||
current.Name = v
|
||||
case "ref":
|
||||
current.Ref = v
|
||||
}
|
||||
}
|
||||
depth = 1
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
opens := strings.Count(trim, "{")
|
||||
closes := strings.Count(trim, "}")
|
||||
depth += opens - closes
|
||||
if depth <= 0 {
|
||||
if current.Name != "" {
|
||||
cp := *current
|
||||
idx.Templates = append(idx.Templates, cp)
|
||||
idx.byName[strings.ToLower(cp.Name)] = &idx.Templates[len(idx.Templates)-1]
|
||||
}
|
||||
current = nil
|
||||
inInputs = false
|
||||
depth = 0
|
||||
continue
|
||||
}
|
||||
|
||||
// Track child blocks like `{ Child Inputs`.
|
||||
if strings.HasPrefix(trim, "{ Child Inputs") || strings.HasPrefix(trim, "{Child Inputs") {
|
||||
inInputs = true
|
||||
continue
|
||||
}
|
||||
if inInputs && strings.HasPrefix(trim, "}") {
|
||||
inInputs = false
|
||||
continue
|
||||
}
|
||||
|
||||
// Property line.
|
||||
parts := strings.SplitN(trim, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(parts[0])
|
||||
val := stripQuotes(strings.TrimSpace(parts[1]))
|
||||
val = strings.TrimSpace(stripComment(val))
|
||||
|
||||
if inInputs {
|
||||
current.Inputs[key] = val
|
||||
continue
|
||||
}
|
||||
switch strings.ToLower(key) {
|
||||
case "baseitem":
|
||||
current.BaseItem = strings.EqualFold(val, "true")
|
||||
case "crafttime":
|
||||
current.CraftTime = val
|
||||
case "target":
|
||||
current.Target = val
|
||||
case "outputcount":
|
||||
current.OutputCount = val
|
||||
default:
|
||||
current.Extras[key] = val
|
||||
}
|
||||
}
|
||||
if err := sc.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return idx, nil
|
||||
}
|
||||
|
||||
func (idx *TemplateIndex) ByName(name string) *Template {
|
||||
if t, ok := idx.byName[strings.ToLower(name)]; ok {
|
||||
c := *t
|
||||
return &c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UsedIn returns the list of templates whose inputs include the named item.
|
||||
func (idx *TemplateIndex) UsedIn(itemName string) []Template {
|
||||
want := strings.ToLower(itemName)
|
||||
out := []Template{}
|
||||
for _, t := range idx.Templates {
|
||||
for k := range t.Inputs {
|
||||
if strings.ToLower(k) == want {
|
||||
out = append(out, t)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user