Asset-owned topdata
This commit is contained in:
@@ -267,7 +267,10 @@ documents otherwise.
|
|||||||
`generated_assets.topdata_2da` is for HAK repositories that need a small
|
`generated_assets.topdata_2da` is for HAK repositories that need a small
|
||||||
native-topdata subset packaged as HAK resources. It builds only `.2da` output,
|
native-topdata subset packaged as HAK resources. It builds only `.2da` output,
|
||||||
injects those generated files into `build-haks`, and rejects TLK-backed text
|
injects those generated files into `build-haks`, and rejects TLK-backed text
|
||||||
because this path does not produce TLK packages.
|
because this path does not produce TLK packages. Static generated groups can
|
||||||
|
omit `autogen.mode`; in that case the selected datasets are compiled directly,
|
||||||
|
and `package_root` plus the repository's HAK include patterns determine where
|
||||||
|
the resources are packaged.
|
||||||
|
|
||||||
For `cachedmodels_rows` generated assets, the configured autogen scan appends
|
For `cachedmodels_rows` generated assets, the configured autogen scan appends
|
||||||
discovered model stems to the selected `cachedmodels` dataset without `.mdl`
|
discovered model stems to the selected `cachedmodels` dataset without `.mdl`
|
||||||
|
|||||||
@@ -2398,6 +2398,124 @@ func TestBuildHAKsGeneratesParts2DAAssetsFromLocalModels(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildHAKsPackagesStaticGeneratedTopDataByConfiguredRoot(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
mustMkdir(t, filepath.Join(root, "src"))
|
||||||
|
mustMkdir(t, filepath.Join(root, "assets"))
|
||||||
|
mustMkdir(t, filepath.Join(root, "topdata", "data", "tailmodel"))
|
||||||
|
mustMkdir(t, filepath.Join(root, "topdata", "data", "skyboxes"))
|
||||||
|
mustMkdir(t, filepath.Join(root, "build"))
|
||||||
|
|
||||||
|
mustWriteFile(t, filepath.Join(root, "nwn-tool.json"), `{
|
||||||
|
"module": {
|
||||||
|
"name": "Test Module",
|
||||||
|
"resref": "testmod",
|
||||||
|
"hak_order": ["group:sow_part", "group:sow_envi"]
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"source": "src",
|
||||||
|
"assets": "assets",
|
||||||
|
"build": "build"
|
||||||
|
},
|
||||||
|
"generated_assets": {
|
||||||
|
"topdata_2da": [
|
||||||
|
{
|
||||||
|
"id": "part_topdata",
|
||||||
|
"source": "topdata",
|
||||||
|
"output": "{paths.cache}/generated-assets/part-topdata-2da",
|
||||||
|
"include_datasets": ["tailmodel"],
|
||||||
|
"package_root": "part"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "envi_topdata",
|
||||||
|
"source": "topdata",
|
||||||
|
"output": "{paths.cache}/generated-assets/envi-topdata-2da",
|
||||||
|
"include_datasets": ["skyboxes"],
|
||||||
|
"package_root": "envi"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"haks": [
|
||||||
|
{
|
||||||
|
"name": "sow_part",
|
||||||
|
"priority": 1,
|
||||||
|
"max_bytes": 0,
|
||||||
|
"split": false,
|
||||||
|
"include": ["part/**"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sow_envi",
|
||||||
|
"priority": 2,
|
||||||
|
"max_bytes": 0,
|
||||||
|
"split": false,
|
||||||
|
"include": ["envi/**"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
mustWriteFile(t, filepath.Join(root, "topdata", "data", "tailmodel", "base.json"), `{
|
||||||
|
"output": "tailmodel.2da",
|
||||||
|
"columns": ["LABEL", "MODEL", "ENVMAP"],
|
||||||
|
"rows": [
|
||||||
|
{"id": 0, "LABEL": "(None)", "MODEL": "****", "ENVMAP": "****"},
|
||||||
|
{"id": 1, "LABEL": "Lizard", "MODEL": "c_tailliz", "ENVMAP": "default"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
mustWriteFile(t, filepath.Join(root, "topdata", "data", "skyboxes", "base.json"), `{
|
||||||
|
"output": "skyboxes.2da",
|
||||||
|
"columns": ["LABEL", "STRING_REF", "CYCLICAL", "DAWN", "DAY", "DUSK", "NIGHT"],
|
||||||
|
"rows": [
|
||||||
|
{"id": 0, "LABEL": "(None)", "STRING_REF": "****", "CYCLICAL": "****", "DAWN": "****", "DAY": "****", "DUSK": "****", "NIGHT": "****"},
|
||||||
|
{"id": 1, "LABEL": "Grass_Clear", "STRING_REF": "****", "CYCLICAL": "1", "DAWN": "Skyda_001", "DAY": "Sky_001", "DUSK": "Skyd_001", "NIGHT": "Skyn_001"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
p, err := project.Load(root)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("load project: %v", err)
|
||||||
|
}
|
||||||
|
if err := p.ValidateLayout(); err != nil {
|
||||||
|
t.Fatalf("validate layout: %v", err)
|
||||||
|
}
|
||||||
|
if err := p.Scan(); err != nil {
|
||||||
|
t.Fatalf("scan: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := BuildHAKs(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("build haks: %v", err)
|
||||||
|
}
|
||||||
|
if len(result.HAKPaths) != 2 {
|
||||||
|
t.Fatalf("expected two HAKs, got %#v", result.HAKPaths)
|
||||||
|
}
|
||||||
|
raw, err := os.ReadFile(filepath.Join(root, "build", "haks.json"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("read haks manifest: %v", err)
|
||||||
|
}
|
||||||
|
manifestText := string(raw)
|
||||||
|
for _, want := range []string{
|
||||||
|
`"part/tailmodel.2da"`,
|
||||||
|
`"envi/skyboxes.2da"`,
|
||||||
|
} {
|
||||||
|
if !strings.Contains(manifestText, want) {
|
||||||
|
t.Fatalf("expected generated static 2da asset %q in manifest, got:\n%s", want, manifestText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.Contains(manifestText, `"part/skyboxes.2da"`) || strings.Contains(manifestText, `"envi/tailmodel.2da"`) {
|
||||||
|
t.Fatalf("expected generated static 2da assets to follow configured package roots, got:\n%s", manifestText)
|
||||||
|
}
|
||||||
|
for _, generated := range []string{
|
||||||
|
filepath.Join(root, ".cache", "generated-assets", "part-topdata-2da", "tailmodel.2da"),
|
||||||
|
filepath.Join(root, ".cache", "generated-assets", "envi-topdata-2da", "skyboxes.2da"),
|
||||||
|
} {
|
||||||
|
if _, err := os.Stat(generated); err != nil {
|
||||||
|
t.Fatalf("expected generated 2da %s: %v", generated, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildHAKsRejectsTLKBackedGeneratedParts2DAAssets(t *testing.T) {
|
func TestBuildHAKsRejectsTLKBackedGeneratedParts2DAAssets(t *testing.T) {
|
||||||
root := t.TempDir()
|
root := t.TempDir()
|
||||||
mustMkdir(t, filepath.Join(root, "src"))
|
mustMkdir(t, filepath.Join(root, "src"))
|
||||||
|
|||||||
@@ -1457,10 +1457,11 @@ func validateGeneratedConfig(cfg GeneratedConfig) []error {
|
|||||||
failures = append(failures, fmt.Errorf("%s.include_datasets must contain at least one pattern", fieldPrefix))
|
failures = append(failures, fmt.Errorf("%s.include_datasets must contain at least one pattern", fieldPrefix))
|
||||||
}
|
}
|
||||||
failures = append(failures, validateGlobList(fieldPrefix+".include_datasets", top2da.IncludeDatasets)...)
|
failures = append(failures, validateGlobList(fieldPrefix+".include_datasets", top2da.IncludeDatasets)...)
|
||||||
switch strings.TrimSpace(top2da.Autogen.Mode) {
|
autogenMode := strings.TrimSpace(top2da.Autogen.Mode)
|
||||||
|
switch autogenMode {
|
||||||
case "parts_rows", "cachedmodels_rows":
|
case "parts_rows", "cachedmodels_rows":
|
||||||
case "":
|
case "":
|
||||||
failures = append(failures, fmt.Errorf("%s.autogen.mode is required", fieldPrefix))
|
continue
|
||||||
default:
|
default:
|
||||||
failures = append(failures, fmt.Errorf("%s.autogen.mode %q is not supported", fieldPrefix, top2da.Autogen.Mode))
|
failures = append(failures, fmt.Errorf("%s.autogen.mode %q is not supported", fieldPrefix, top2da.Autogen.Mode))
|
||||||
}
|
}
|
||||||
@@ -1473,7 +1474,7 @@ func validateGeneratedConfig(cfg GeneratedConfig) []error {
|
|||||||
}
|
}
|
||||||
failures = append(failures, validateGlobList(fieldPrefix+".autogen.include", top2da.Autogen.Include)...)
|
failures = append(failures, validateGlobList(fieldPrefix+".autogen.include", top2da.Autogen.Include)...)
|
||||||
failures = append(failures, validateAutogenDeriveConfig(fieldPrefix+".autogen.derive", top2da.Autogen.Derive)...)
|
failures = append(failures, validateAutogenDeriveConfig(fieldPrefix+".autogen.derive", top2da.Autogen.Derive)...)
|
||||||
if strings.TrimSpace(top2da.Autogen.Mode) == "parts_rows" && partsRowsConfigConfigured(top2da.Autogen.PartsRows) {
|
if autogenMode == "parts_rows" && partsRowsConfigConfigured(top2da.Autogen.PartsRows) {
|
||||||
failures = append(failures, validatePartsRowsConfig(fieldPrefix+".autogen.parts_rows", top2da.Autogen.PartsRows)...)
|
failures = append(failures, validatePartsRowsConfig(fieldPrefix+".autogen.parts_rows", top2da.Autogen.PartsRows)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1092,6 +1092,13 @@ func TestValidateLayoutAcceptsGeneratedTopData2DAConfig(t *testing.T) {
|
|||||||
Derive: AutogenDeriveConfig{Kind: "model_stem", GroupFrom: "first_path_segment"},
|
Derive: AutogenDeriveConfig{Kind: "model_stem", GroupFrom: "first_path_segment"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ID: "static_topdata",
|
||||||
|
Source: "topdata",
|
||||||
|
Output: "{paths.cache}/generated-assets/static-topdata-2da",
|
||||||
|
IncludeDatasets: []string{"tailmodel", "wingmodel"},
|
||||||
|
PackageRoot: "part",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1106,6 +1113,41 @@ func TestValidateLayoutAcceptsGeneratedTopData2DAConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateLayoutRejectsUnsupportedGeneratedTopDataAutogenMode(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
mkdirAll(t, filepath.Join(root, "assets"))
|
||||||
|
mkdirAll(t, filepath.Join(root, "build"))
|
||||||
|
proj := Project{
|
||||||
|
Root: root,
|
||||||
|
Config: Config{
|
||||||
|
Module: ModuleConfig{Name: "Test", ResRef: "testmod"},
|
||||||
|
Paths: PathConfig{Assets: "assets"},
|
||||||
|
Generated: GeneratedConfig{
|
||||||
|
TopData2DA: []GeneratedTopData2DAConfig{
|
||||||
|
{
|
||||||
|
ID: "bad",
|
||||||
|
Source: "topdata",
|
||||||
|
Output: "{paths.cache}/generated-assets/bad-2da",
|
||||||
|
IncludeDatasets: []string{"tailmodel"},
|
||||||
|
PackageRoot: "part",
|
||||||
|
Autogen: AutogenConsumerConfig{
|
||||||
|
Mode: "unknown_rows",
|
||||||
|
Root: "part",
|
||||||
|
Include: []string{"**/*.mdl"},
|
||||||
|
Derive: AutogenDeriveConfig{Kind: "model_stem", GroupFrom: "first_path_segment"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := proj.ValidateLayout()
|
||||||
|
if err == nil || !strings.Contains(err.Error(), `generated_assets.topdata_2da[0].autogen.mode "unknown_rows" is not supported`) {
|
||||||
|
t.Fatalf("expected unsupported generated autogen mode validation error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateLayoutRejectsEscapingGeneratedTopData2DAConfig(t *testing.T) {
|
func TestValidateLayoutRejectsEscapingGeneratedTopData2DAConfig(t *testing.T) {
|
||||||
root := t.TempDir()
|
root := t.TempDir()
|
||||||
mkdirAll(t, filepath.Join(root, "assets"))
|
mkdirAll(t, filepath.Join(root, "assets"))
|
||||||
|
|||||||
Reference in New Issue
Block a user