diff options
| author | historia <[not public]> | 2026-09-18 01:50:17 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-18 01:50:17 -0400 |
| commit | 87d08f0151e643951769e323995f1c5ed89ee200 (patch) | |
| tree | dc42a0b30ebb5a00dd7ba505cc4eaa44f4f308bc /internal/validate/checks.go | |
| parent | 32a3c59cebf2a16277e54d715a67adfdbe00b456 (diff) | |
| download | thehouseoficarus-87d08f0151e643951769e323995f1c5ed89ee200.tar.gz | |
fix: harden economy recovery, shutdown concurrency, world validation, and inventory-loss paths
Diffstat (limited to 'internal/validate/checks.go')
| -rw-r--r-- | internal/validate/checks.go | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/internal/validate/checks.go b/internal/validate/checks.go index 9d09eef..5eb4288 100644 --- a/internal/validate/checks.go +++ b/internal/validate/checks.go @@ -736,6 +736,7 @@ func validateDropTables(s Source) []Issue { var issues []Issue dropIDs := dropTableIDSet(s.DataDir) itemIDs := s.Items.IDSet() + issues = append(issues, validateDropTableCycles(s, dropIDs)...) for id := range dropIDs { dt, err := behavior.LoadDropTable(s.DataDir, id) @@ -838,6 +839,72 @@ func validateDropTables(s Source) []Issue { return issues } +// validateDropTableCycles reports every table that participates in a +// reference cycle. ResolveDrop recurses at runtime, so a self- or +// mutual-reference would otherwise overflow the goroutine stack and kill the +// process; a cycle is therefore a hard startup error. +func validateDropTableCycles(s Source, dropIDs map[string]bool) []Issue { + edges := make(map[string][]string, len(dropIDs)) + for id := range dropIDs { + dt, err := behavior.LoadDropTable(s.DataDir, id) + if err != nil { + continue // reported by the main pass + } + for _, d := range dt.Drops { + if d.Table != "" && dropIDs[d.Table] { + edges[id] = append(edges[id], d.Table) + } + } + } + const ( + white = 0 + gray = 1 + black = 2 + ) + color := make(map[string]int, len(dropIDs)) + reported := make(map[string]bool) + var stack []string + var issues []Issue + var visit func(id string) + visit = func(id string) { + color[id] = gray + stack = append(stack, id) + for _, next := range edges[id] { + switch color[next] { + case gray: + start := 0 + for i, m := range stack { + if m == next { + start = i + break + } + } + for _, m := range stack[start:] { + if reported[m] { + continue + } + reported[m] = true + issues = append(issues, Issue{ + Level: "ERROR", + Type: "integrity", + Message: fmt.Sprintf("Drop table %q participates in a reference cycle; drop-table recursion would overflow the stack", m), + }) + } + case white: + visit(next) + } + } + stack = stack[:len(stack)-1] + color[id] = black + } + for id := range dropIDs { + if color[id] == white { + visit(id) + } + } + return issues +} + func validateCourses(s Source) []Issue { var issues []Issue roomIndex := s.World.RoomIndex() |
