From bd998748ee5362ec4f6bc203f65484fb0a7e4708 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Sun, 27 Sep 2026 23:55:07 +0300 Subject: [PATCH] fix(leaks): the compass keeps its marks - two lists swapped a frame, a pool of marks reused Co-Authored-By: Claude Opus 5.5 --- packages/ludic.compass/gather.ludic | 35 ++++++++++++++++++++++++----- packages/ludic.compass/marks.ludic | 3 +++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/ludic.compass/gather.ludic b/packages/ludic.compass/gather.ludic index bec0f109..03b6524c 100644 --- a/packages/ludic.compass/gather.ludic +++ b/packages/ludic.compass/gather.ludic @@ -1,30 +1,55 @@ # gather.ludic - a frame's marks: begin, every provider marks, end sorts and culls. While a # capture is open the marks go to whoever is listening instead, whatever the tier. -export function compass_begin(compass_st: mut CompassState) -> void { compass_st.cmp_list = new []CompassMark } +# a frame's list and marks are the state's own, emptied and reused rather than made again +export function compass_begin(compass_st: mut CompassState) -> void { + if compass_st.cmp_list == null { + compass_st.cmp_list = new []CompassMark + compass_st.cmp_back = new []CompassMark + compass_st.cmp_pool = new []CompassMark + } + List.clear(compass_st.cmp_list) + compass_st.cmp_used = 0 +} # a provider's verb: a thing at (x, z), shown from compass `tier` up export function compass_mark(compass_st: mut CompassState, x: float, z: float, icon: int, colour: int, tier: int, prio: int, label: string) -> void { if compass_st.cmp_list == null { compass_begin(compass_st) } - let m = cmp_new(x, z, icon, colour, tier, prio, label) if compass_st.cmp_capturing { - push(compass_st.cmp_caught, m) + push(compass_st.cmp_caught, cmp_new(x, z, icon, colour, tier, prio, label)) # kept by the listener return } if CompassWorld.tier() < tier { return } - push(compass_st.cmp_list, m) + push(compass_st.cmp_list, cmp_take(compass_st, x, z, icon, colour, tier, prio, label)) } # highest priority first, the providers' order kept within one; then the cap export function compass_end(compass_st: mut CompassState) -> void { if compass_st.cmp_list == null { compass_begin(compass_st) } - let out = new []CompassMark + let out = compass_st.cmp_back + List.clear(out) for p in 0 .. 3 { let want = COMPASS_TRACKED - p for i in 0 .. len(compass_st.cmp_list) { if cmp_band(compass_st.cmp_list[i].prio) == want and len(out) < compass_st.cmp_cap { push(out, compass_st.cmp_list[i]) } } } + compass_st.cmp_back = compass_st.cmp_list compass_st.cmp_list = out } +# the pool's next mark, filled (one made only when the pool is short) +function cmp_take(compass_st: mut CompassState, x: float, z: float, icon: int, colour: int, tier: int, prio: int, label: string) -> CompassMark { + if compass_st.cmp_used >= len(compass_st.cmp_pool) { push(compass_st.cmp_pool, new CompassMark) } + let m = compass_st.cmp_pool[compass_st.cmp_used] + compass_st.cmp_used += 1 + m.x = x + m.z = z + m.icon = icon + m.colour = colour + m.tier = tier + m.prio = prio + m.label = label + return m +} + function cmp_band(p: int) -> int { return Math.clamp(p, COMPASS_PLAIN, COMPASS_TRACKED) } # once a frame: ask every registered provider, in the registry's order diff --git a/packages/ludic.compass/marks.ludic b/packages/ludic.compass/marks.ludic index ec2a546d..1d053def 100644 --- a/packages/ludic.compass/marks.ludic +++ b/packages/ludic.compass/marks.ludic @@ -25,6 +25,9 @@ export open registry CompassProviders of CompassProvider as COMPASS_FROM export state CompassState { cmp_cap: int = 64 cmp_list: []CompassMark = null # this frame's, sorted and culled + cmp_back: []CompassMark = null # the list the sort fills, then swapped with cmp_list + cmp_pool: []CompassMark = null # a frame's marks, reused: Ludic frees nothing + cmp_used: int = 0 cmp_capturing: bool = false cmp_caught: []CompassMark = null # what providers said while someone was listening cmp_ranges: []float = null