aboutsummaryrefslogtreecommitdiff
path: root/internal/behavior
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-18 16:13:49 -0400
committerhistoria <[not public]>2026-09-18 16:13:49 -0400
commit194164a77eb4f2479b94c19e6f0e1278ab608781 (patch)
tree53413add8ed6f32a34d1733397e380cc98ef70ef /internal/behavior
parent67e01aab85607b9e601185c7d62dff576af3bb43 (diff)
downloadthehouseoficarus-194164a77eb4f2479b94c19e6f0e1278ab608781.tar.gz
fix: more casino, economy, gameplay, security, and world data hardening gaps
Diffstat (limited to 'internal/behavior')
-rw-r--r--internal/behavior/store.go27
1 files changed, 21 insertions, 6 deletions
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 != "" {