signal bus with data
This commit is contained in:
@@ -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),
|
||||||
|
|||||||
+73
-13
@@ -1,25 +1,85 @@
|
|||||||
package shoal
|
package shoal
|
||||||
|
|
||||||
|
// per-type signal bus, created on first use.
|
||||||
|
// one system writes a signal with data, any number of other systems read it
|
||||||
|
// after. same-frame reads only work until signal_system clears the buses at
|
||||||
|
// POST_UPDATE — see read/signal_system below for the details.
|
||||||
|
Bus :: struct($T: typeid) {
|
||||||
|
pending: [dynamic]T,
|
||||||
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
signal_buffer_b: [dynamic]string
|
Bus_Entry :: struct {
|
||||||
|
bus: rawptr,
|
||||||
|
clear: proc(b: rawptr),
|
||||||
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
signal_buffer_a: [dynamic]string
|
buses: map[typeid]Bus_Entry
|
||||||
|
|
||||||
write :: proc(name: string) {
|
@(private)
|
||||||
append(&signal_buffer_b, name)
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
read :: proc(name: string) -> bool {
|
// called once per payload type, hands signal_system a rawptr clear for that bus
|
||||||
for s in signal_buffer_a {
|
@(private)
|
||||||
if s == name {return true}
|
clear_bus :: proc($T: typeid) -> proc(b: rawptr) {
|
||||||
|
return proc(b: rawptr) {
|
||||||
|
bus := (^Bus(T))(b)
|
||||||
|
clear(&bus.pending)
|
||||||
}
|
}
|
||||||
for s in signal_buffer_b {
|
|
||||||
if s == name {return true}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user