signal bus with data

This commit is contained in:
wooshings
2026-10-27 19:04:05 -07:00
parent c69bf5cabf
commit 56adba8ea5
3 changed files with 78 additions and 30 deletions
-12
View File
@@ -1,19 +1,7 @@
package shoal package shoal
import "core:strings"
import "core:thread"
import ml "vendor:microui"
import rl "vendor:raylib" import rl "vendor:raylib"
update_pool: thread.Pool
@(private)
sparse_entity_limit: int
get_entity_limit :: proc() -> int {
return sparse_entity_limit
}
App :: struct { App :: struct {
systems: map[int][dynamic]proc(), systems: map[int][dynamic]proc(),
plugins: [dynamic]proc(app: ^App), plugins: [dynamic]proc(app: ^App),
+77 -17
View File
@@ -1,25 +1,85 @@
package shoal package shoal
@(private) // per-type signal bus, created on first use.
signal_buffer_b: [dynamic]string // one system writes a signal with data, any number of other systems read it
@(private) // after. same-frame reads only work until signal_system clears the buses at
signal_buffer_a: [dynamic]string // POST_UPDATE — see read/signal_system below for the details.
Bus :: struct($T: typeid) {
write :: proc(name: string) { pending: [dynamic]T,
append(&signal_buffer_b, name)
} }
read :: proc(name: string) -> bool { @(private)
for s in signal_buffer_a { Bus_Entry :: struct {
if s == name {return true} bus: rawptr,
} clear: proc(b: rawptr),
for s in signal_buffer_b {
if s == name {return true}
}
return false
} }
@(private)
buses: map[typeid]Bus_Entry
@(private)
get_bus :: proc($T: typeid) -> ^Bus(T) {
if entry, exists := buses[T]; exists {
return (^Bus(T))(entry.bus)
}
bus := new(Bus(T))
buses[T] = {
bus = bus,
clear = clear_bus(T),
}
return bus
}
// called once per payload type, hands signal_system a rawptr clear for that bus
@(private)
clear_bus :: proc($T: typeid) -> proc(b: rawptr) {
return proc(b: rawptr) {
bus := (^Bus(T))(b)
clear(&bus.pending)
}
}
// queue a signal, readable until signal_system clears it at POST_UPDATE.
// note POST_UPDATE runs before PRE_DRAW/DRAW, so a reader sees exactly what's
// been written since the last POST_UPDATE: UPDATE systems see same-frame
// writes, but a DRAW system only sees writes made after this frame's
// POST_UPDATE (by PRE_DRAW or earlier DRAW systems)
// queue an empty payload by passing the bare type: write(MySignal)
write :: proc {
write_empty,
write_with_data,
}
write_empty :: proc($T: typeid) {
bus := get_bus(T)
append(&bus.pending, T{})
}
write_with_data :: proc(data: $T) {
bus := get_bus(T)
append(&bus.pending, data)
}
// every signal of type T written since the last POST_UPDATE, non-consuming,
// empty if none. iterate it: for s in read(MySignal)
// copied into context.temp_allocator so write() during iteration can't
// reallocate the pending buffer out from under you — but that means the result
// dies with the frame's temp arena, don't hold onto it past that
read :: proc($T: typeid) -> []T {
bus := get_bus(T)
if len(bus.pending) == 0 {return nil}
result := make([]T, len(bus.pending), context.temp_allocator)
copy(result, bus.pending[:])
return result
}
// runs at POST_UPDATE, clears every bus so the next frame starts fresh.
// writes made after it (e.g. in DRAW) survive until the *next* frame's
// POST_UPDATE, so the rest of this frame and the next frame's
// PRE_UPDATE/UPDATE systems still read them — but the next frame's DRAW
// never does, POST_UPDATE gets there first
signal_system :: proc() { signal_system :: proc() {
signal_buffer_a = signal_buffer_b for _, e in buses {
clear(&signal_buffer_b) e.clear(e.bus)
}
} }
+1 -1
View File
@@ -6,7 +6,7 @@ import rl "vendor:raylib"
DefaultPlugin :: proc(app: ^App) { DefaultPlugin :: proc(app: ^App) {
add_system(app, STARTUP, startup_window) add_system(app, STARTUP, startup_window)
add_system(app, DRAW, draw_sprite) add_system(app, DRAW, draw_sprite)
add_system(app, UPDATE, signal_system) add_system(app, POST_UPDATE, signal_system)
add_system(app, UPDATE, run_timers) add_system(app, UPDATE, run_timers)
add_system(app, UPDATE, update_cursor_position) add_system(app, UPDATE, update_cursor_position)
add_system(app, PRE_DRAW, proc() {rl.BeginDrawing() add_system(app, PRE_DRAW, proc() {rl.BeginDrawing()