From 7b0deda1a625f23700147e8680e88ddb80c75dcf Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 00:58:45 +0300 Subject: [PATCH 1/2] fix(leaks): the compass's capture reuses its list and a pool of marks; what is caught is good until the next capture The guide captured the story's marks every frame into a new list of new marks. Co-Authored-By: Claude Opus 5.5 --- packages/ludic.compass/capture.ludic | 26 ++++++++++++++++++++++++++ packages/ludic.compass/gather.ludic | 27 ++------------------------- packages/ludic.compass/index.ludic | 1 + packages/ludic.compass/marks.ludic | 6 ++++-- 4 files changed, 33 insertions(+), 27 deletions(-) create mode 100644 packages/ludic.compass/capture.ludic diff --git a/packages/ludic.compass/capture.ludic b/packages/ludic.compass/capture.ludic new file mode 100644 index 00000000..e5797dc6 --- /dev/null +++ b/packages/ludic.compass/capture.ludic @@ -0,0 +1,26 @@ +# capture.ludic - someone else wants to know where the providers point (a guide's arrow): until the +# end every mark is theirs, whatever the tier, and the frame's list is left alone +export function compass_capture_begin(compass_st: mut CompassState) -> void { + if compass_st.cmp_caught == null { + compass_st.cmp_caught = new []CompassMark + compass_st.cmp_cpool = new []CompassMark + } + List.clear(compass_st.cmp_caught) + compass_st.cmp_cused = 0 + compass_st.cmp_capturing = true +} + +# what was caught: the compass's own list and marks, good until the next capture begins +export function compass_capture_end(compass_st: mut CompassState) -> []CompassMark { + compass_st.cmp_capturing = false + if compass_st.cmp_caught == null { compass_st.cmp_caught = new []CompassMark } + return compass_st.cmp_caught +} + +# the capture pool's next mark, filled (one made only when the pool is short) +function cmp_catch(compass_st: mut CompassState, x: float, z: float, icon: int, colour: int, tier: int, prio: int, label: string) -> CompassMark { + if compass_st.cmp_cpool == null { compass_st.cmp_cpool = new []CompassMark } + if compass_st.cmp_cused >= len(compass_st.cmp_cpool) { push(compass_st.cmp_cpool, new CompassMark) } + compass_st.cmp_cused += 1 + return cmp_fill(compass_st.cmp_cpool[compass_st.cmp_cused - 1], x, z, icon, colour, tier, prio, label) +} diff --git a/packages/ludic.compass/gather.ludic b/packages/ludic.compass/gather.ludic index 03b6524c..9011d893 100644 --- a/packages/ludic.compass/gather.ludic +++ b/packages/ludic.compass/gather.ludic @@ -15,7 +15,7 @@ export function compass_begin(compass_st: mut CompassState) -> void { 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) } if compass_st.cmp_capturing { - push(compass_st.cmp_caught, cmp_new(x, z, icon, colour, tier, prio, label)) # kept by the listener + push(compass_st.cmp_caught, cmp_catch(compass_st, x, z, icon, colour, tier, prio, label)) return } if CompassWorld.tier() < tier { return } @@ -38,16 +38,8 @@ export function compass_end(compass_st: mut CompassState) -> void { # 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 + return cmp_fill(compass_st.cmp_pool[compass_st.cmp_used - 1], x, z, icon, colour, tier, prio, label) } function cmp_band(p: int) -> int { return Math.clamp(p, COMPASS_PLAIN, COMPASS_TRACKED) } @@ -63,19 +55,4 @@ export function compass_gather(compass_st: mut CompassState) -> void { compass_end(compass_st) } -# someone else wants to know where the providers point (a guide's arrow): until the end every -# mark is theirs, whatever the tier, and the frame's list is left alone -export function compass_capture_begin(compass_st: mut CompassState) -> void { - compass_st.cmp_capturing = true - compass_st.cmp_caught = new []CompassMark -} - -export function compass_capture_end(compass_st: mut CompassState) -> []CompassMark { - compass_st.cmp_capturing = false - var got = compass_st.cmp_caught - compass_st.cmp_caught = new []CompassMark - if got == null { got = new []CompassMark } - return got -} - export function compass_capturing(compass_st: CompassState) -> bool { return compass_st.cmp_capturing } diff --git a/packages/ludic.compass/index.ludic b/packages/ludic.compass/index.ludic index c016bdd9..bfaea651 100644 --- a/packages/ludic.compass/index.ludic +++ b/packages/ludic.compass/index.ludic @@ -7,5 +7,6 @@ import "ludic.base" import "ports.ludic" import "marks.ludic" import "gather.ludic" +import "capture.ludic" import "bearing.ludic" import "radar.ludic" diff --git a/packages/ludic.compass/marks.ludic b/packages/ludic.compass/marks.ludic index 1d053def..75c70294 100644 --- a/packages/ludic.compass/marks.ludic +++ b/packages/ludic.compass/marks.ludic @@ -30,6 +30,8 @@ export state CompassState { cmp_used: int = 0 cmp_capturing: bool = false cmp_caught: []CompassMark = null # what providers said while someone was listening + cmp_cpool: []CompassMark = null # the capture's marks, reused from one capture to the next + cmp_cused: int = 0 cmp_ranges: []float = null cmp_radar_from: int = 2 # marks of at least this tier are blips; the tracked one always is } @@ -40,8 +42,8 @@ export function compass_config(compass_st: mut CompassState, cap: int) -> void { compass_begin(compass_st) } -function cmp_new(x: float, z: float, icon: int, colour: int, tier: int, prio: int, label: string) -> CompassMark { - let m = new CompassMark +# a pooled mark filled in place +function cmp_fill(m: CompassMark, x: float, z: float, icon: int, colour: int, tier: int, prio: int, label: string) -> CompassMark { m.x = x m.z = z m.icon = icon From b841efe4bafeb67434f49a6b4d7138a2c729d3bf Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 00:58:53 +0300 Subject: [PATCH 2/2] docs(compass): what a capture hands back lasts until the next one Co-Authored-By: Claude Opus 5.5 --- packages/ludic.compass/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/ludic.compass/README.md b/packages/ludic.compass/README.md index 5efb82e7..729b5c41 100644 --- a/packages/ludic.compass/README.md +++ b/packages/ludic.compass/README.md @@ -45,6 +45,9 @@ story_marks() let marks = compass_capture_end() ``` +The list and its marks are the compass's own, reused, and good until the next capture begins: read +them then, or copy what must last longer. + ## The port ```ludic