Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff9424c8c3 |
@@ -109,7 +109,6 @@ type tlkCompiler struct {
|
|||||||
active map[string]tlkEntryData
|
active map[string]tlkEntryData
|
||||||
activeKeys map[string]struct{}
|
activeKeys map[string]struct{}
|
||||||
reservedByID map[int]string
|
reservedByID map[int]string
|
||||||
pinnedByID map[int]string
|
|
||||||
nextID int
|
nextID int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +158,6 @@ func newTLKCompiler(sourceDir string, legacy *legacyTLKData) (*tlkCompiler, erro
|
|||||||
active: map[string]tlkEntryData{},
|
active: map[string]tlkEntryData{},
|
||||||
activeKeys: map[string]struct{}{},
|
activeKeys: map[string]struct{}{},
|
||||||
reservedByID: reserved,
|
reservedByID: reserved,
|
||||||
pinnedByID: map[int]string{},
|
|
||||||
nextID: nextID,
|
nextID: nextID,
|
||||||
}
|
}
|
||||||
if legacy != nil {
|
if legacy != nil {
|
||||||
@@ -382,24 +380,12 @@ func (c *tlkCompiler) registerInlineAtID(key string, id int, entry tlkEntryData)
|
|||||||
if id < 0 {
|
if id < 0 {
|
||||||
return fmt.Errorf("TLK key %q has negative id %d", key, id)
|
return fmt.Errorf("TLK key %q has negative id %d", key, id)
|
||||||
}
|
}
|
||||||
// The pin is authoritative over the per-machine .tlk_state.json cache: a
|
|
||||||
// stale mapping that dynamically grabbed this id on an older build must
|
|
||||||
// yield so the pinned key can take it. Only a genuine clash between two
|
|
||||||
// pins in custom.tlk.yml is an author error.
|
|
||||||
if owner, ok := c.pinnedByID[id]; ok && owner != key {
|
|
||||||
return fmt.Errorf("TLK id %d is pinned by both %q and %q", id, owner, key)
|
|
||||||
}
|
|
||||||
if mapping, ok := c.state.Entries[key]; ok && mapping.ID != id {
|
if mapping, ok := c.state.Entries[key]; ok && mapping.ID != id {
|
||||||
// This key held a different cached id; release it so the pin wins.
|
return fmt.Errorf("TLK key %q changed id from %d to %d", key, mapping.ID, id)
|
||||||
if c.reservedByID[mapping.ID] == key {
|
|
||||||
delete(c.reservedByID, mapping.ID)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if owner, ok := c.reservedByID[id]; ok && owner != key {
|
if owner, ok := c.reservedByID[id]; ok && owner != key {
|
||||||
// Evict the stale owner; it gets a fresh id when next made active.
|
return fmt.Errorf("TLK id %d is already reserved by %q", id, owner)
|
||||||
delete(c.state.Entries, owner)
|
|
||||||
}
|
}
|
||||||
c.pinnedByID[id] = key
|
|
||||||
c.state.Entries[key] = tlkStateMapping{ID: id}
|
c.state.Entries[key] = tlkStateMapping{ID: id}
|
||||||
c.reservedByID[id] = key
|
c.reservedByID[id] = key
|
||||||
return c.markActive(key, entry)
|
return c.markActive(key, entry)
|
||||||
|
|||||||
@@ -2449,55 +2449,6 @@ strings:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildStandaloneTLKPinEvictsStaleStateOwner(t *testing.T) {
|
|
||||||
root := testProjectRoot(t)
|
|
||||||
mkdirAll(t, filepath.Join(root, "topdata", "data", "feat"))
|
|
||||||
mkdirAll(t, filepath.Join(root, "topdata", "tlk"))
|
|
||||||
writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
|
|
||||||
writeFile(t, filepath.Join(root, "topdata", "data", "feat", "base.json"), `{
|
|
||||||
"output": "feat.2da",
|
|
||||||
"columns": ["LABEL", "FEAT", "DESCRIPTION"],
|
|
||||||
"rows": [{
|
|
||||||
"id": 0,
|
|
||||||
"key": "feat:test",
|
|
||||||
"LABEL": "TEST_LABEL",
|
|
||||||
"FEAT": {"tlk": {"key": "feat:test.name", "text": "Test Feat"}},
|
|
||||||
"DESCRIPTION": "****"
|
|
||||||
}]
|
|
||||||
}`+"\n")
|
|
||||||
writeFile(t, filepath.Join(root, "topdata", "tlk", "custom.tlk.yml"), `schema: sow-topdata/tlk/v1
|
|
||||||
base_strref: 16777216
|
|
||||||
strings:
|
|
||||||
- key: sow.module.name
|
|
||||||
text: Shadows Over Westgate
|
|
||||||
id: 50
|
|
||||||
`)
|
|
||||||
// Simulate a per-machine state that predates the pinned taxonomy: the feat
|
|
||||||
// ref dynamically grabbed id 50 on an older build, exactly where the pin
|
|
||||||
// now lives. The build must self-heal instead of failing.
|
|
||||||
writeFile(t, filepath.Join(root, "topdata", tlkStateFile),
|
|
||||||
`{"version":1,"language":"en","entries":{"feat:test.name":{"id":50}}}`+"\n")
|
|
||||||
|
|
||||||
if _, err := BuildNative(testProject(root), nil); err != nil {
|
|
||||||
t.Fatalf("BuildNative failed on stale pin collision: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stateRaw, err := os.ReadFile(filepath.Join(root, "topdata", tlkStateFile))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("read tlk state: %v", err)
|
|
||||||
}
|
|
||||||
var state tlkStateDocument
|
|
||||||
if err := json.Unmarshal(stateRaw, &state); err != nil {
|
|
||||||
t.Fatalf("parse tlk state: %v", err)
|
|
||||||
}
|
|
||||||
if state.Entries["sow.module.name"].ID != 50 {
|
|
||||||
t.Fatalf("pin must own id 50, got %#v", state.Entries["sow.module.name"])
|
|
||||||
}
|
|
||||||
if got := state.Entries["feat:test.name"].ID; got == 50 {
|
|
||||||
t.Fatalf("stale feat ref should have been reallocated off id 50, got %d", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuildPreservesTLKStateAcrossTextChanges(t *testing.T) {
|
func TestBuildPreservesTLKStateAcrossTextChanges(t *testing.T) {
|
||||||
root := testProjectRoot(t)
|
root := testProjectRoot(t)
|
||||||
mkdirAll(t, filepath.Join(root, "topdata", "data", "skills"))
|
mkdirAll(t, filepath.Join(root, "topdata", "data", "skills"))
|
||||||
|
|||||||
Reference in New Issue
Block a user