From 194164a77eb4f2479b94c19e6f0e1278ab608781 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Fri, 18 Sep 2026 16:13:49 -0400 Subject: fix: more casino, economy, gameplay, security, and world data hardening gaps --- internal/behavior/store.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'internal/behavior') diff --git a/internal/behavior/store.go b/internal/behavior/store.go index cd7a6b7..a558f87 100644 --- a/internal/behavior/store.go +++ b/internal/behavior/store.go @@ -95,23 +95,27 @@ func SuccessChance(cfg SuccessFormula, level int, requiredLevel int) float64 { } var ( - dropIndex map[string]string + dropIndexes = map[string]map[string]string{} dropIndexMu sync.Mutex ) func loadDropIndex(dataDir string) map[string]string { dropIndexMu.Lock() defer dropIndexMu.Unlock() - if dropIndex == nil { - dropIndex = BuildPathIndex(filepath.Join(dataDir, "drops")) - } - return dropIndex + // Keyed by dataDir: a single process can host more than one data dir + // (tests), and a shared index would resolve ids against the first one. + if idx, ok := dropIndexes[dataDir]; ok { + return idx + } + idx := BuildPathIndex(filepath.Join(dataDir, "drops")) + dropIndexes[dataDir] = idx + return idx } func ClearDropIndex() { dropIndexMu.Lock() defer dropIndexMu.Unlock() - dropIndex = nil + dropIndexes = map[string]map[string]string{} } func LoadDropTable(dataDir, id string) (*DropTableDef, error) { @@ -161,6 +165,11 @@ func pickWeighted(drops []DropEntry) *DropEntry { // cycles at startup. const maxDropTableDepth = 32 +// maxDropRolls bounds a table-reference entry's `quantity` (how many times the +// referenced table is rolled). An accidental large value would run that many +// weighted picks on the owner tick and stall the whole world. +const maxDropRolls = 64 + func ResolveDrop(dataDir string, drops []DropEntry) *DropEntry { return resolveDrop(dataDir, drops, 0) } @@ -227,6 +236,12 @@ func resolveDropList(dataDir string, drops []DropEntry, depth int) []DropEntry { if rolls <= 0 { rolls = 1 } + // Bound authored quantities: an accidental `quantity: 1000000000` on a + // table reference would otherwise run a billion weighted picks on the + // owner tick and stall the whole world. + if rolls > maxDropRolls { + rolls = maxDropRolls + } var out []DropEntry for i := 0; i < rolls; i++ { if resolved := resolveDrop(dataDir, sub.Drops, depth+1); resolved != nil && resolved.ItemID != "" { -- cgit v1.2.3