///|
pub(all) enum EventDomain {
Frame
Fixed
Render
} derive(Eq, Debug)
///|
pub(all) struct EventReader[T] {
last_seen_by_world : Map[UInt, Int]
marker : T?
}
///|
pub(all) struct EventWriter[T] {
events : Events[T]
}
///|
pub(all) struct ObserverHandle(Int) derive(Eq, Debug, Hash)
///|
pub(all) struct Trigger[T] {
event : T
target : @entity.Entity?
}
///|
priv struct EventRecord[T] {
id : Int
event : T
}
///|
priv struct ObserverEntry[T] {
target : @entity.Entity?
callback : (Trigger[T]) -> Unit
}
///|
struct EventWorldStore[T] {
mut next_event_id : Int
mut next_observer_id : Int
mut older : Array[EventRecord[T]]
mut newer : Array[EventRecord[T]]
observers : Map[ObserverHandle, ObserverEntry[T]]
observer_queue : Array[Trigger[T]]
}
///|
pub struct Events[T] {
id : UInt
domain : EventDomain
stores : Map[UInt, EventWorldStore[T]]
marker : T?
}
///|
priv struct RegisteredBus {
id : UInt
domain : EventDomain
update : () -> Unit
dispatch : () -> Unit
}
///|
let registered_buses_by_world : Map[UInt, Array[RegisteredBus]] = Map([])
///|
let next_event_bus_id : Ref[UInt] = { val: 1U }
///|
fn allocate_event_bus_id() -> UInt {
let id = next_event_bus_id.val
next_event_bus_id.val += 1U
id
}
///|
fn current_world_id() -> UInt {
@ecs.require_current_world().id()
}
///|
fn[T] world_store(events : Events[T]) -> EventWorldStore[T] {
let world_id = current_world_id()
events.stores.get_or_init(world_id, () => {
next_event_id: 0,
next_observer_id: 0,
older: [],
newer: [],
observers: Map([]),
observer_queue: [],
})
}
///|
pub fn[T] EventReader::EventReader() -> EventReader[T] {
{ last_seen_by_world: Map([]), marker: None }
}
///|
pub fn[T] EventReader::read(
self : EventReader[T],
events : Events[T],
) -> Array[T] {
let world_id = current_world_id()
let last_seen = self.last_seen_by_world.get(world_id).unwrap_or(-1)
let store = world_store(events)
let values : Array[T] = []
let mut latest_id = last_seen
for record in store.older {
if record.id > last_seen {
values.push(record.event)
latest_id = record.id
}
}
for record in store.newer {
if record.id > last_seen {
values.push(record.event)
latest_id = record.id
}
}
self.last_seen_by_world.set(world_id, latest_id)
values
}
///|
pub fn[T] EventWriter::EventWriter(events : Events[T]) -> EventWriter[T] {
{ events, }
}
///|
pub fn[T] EventWriter::send(self : EventWriter[T], event : T) -> Unit {
self.events.send(event)
}
///|
pub fn[T] EventWriter::trigger(
self : EventWriter[T],
event : T,
target? : @entity.Entity?,
) -> Unit {
self.events.trigger(event, target.unwrap_or(None))
}
///|
pub fn[T] Events::Events(domain? : EventDomain = Frame) -> Events[T] {
{ id: allocate_event_bus_id(), domain, stores: Map([]), marker: None }
}
///|
pub fn[T] Events::register_to_world(
self : Events[T],
world : @ecs.World,
) -> Unit {
let world_id = world.id()
let buses = registered_buses_by_world.get(world_id).unwrap_or([])
for bus in buses {
if bus.id == self.id {
return
}
}
buses.push({
id: self.id,
domain: self.domain,
update: fn() { self.update() },
dispatch: fn() { self.dispatch_observers() },
})
registered_buses_by_world.set(world_id, buses)
}
///|
pub fn[T] Events::writer(self : Events[T]) -> EventWriter[T] {
EventWriter(self)
}
///|
pub fn[T] Events::read(self : Events[T], reader : EventReader[T]) -> Array[T] {
reader.read(self)
}
///|
pub fn[T] Events::send(self : Events[T], event : T) -> Unit {
let store = world_store(self)
let id = store.next_event_id
store.next_event_id += 1
store.newer.push({ id, event })
}
///|
pub fn[T] Events::trigger(
self : Events[T],
event : T,
target : @entity.Entity?,
) -> Unit {
let store = world_store(self)
self.send(event)
store.observer_queue.push({ event, target })
}
///|
pub fn[T] Events::observe_global(
self : Events[T],
callback : (Trigger[T]) -> Unit,
) -> ObserverHandle {
let store = world_store(self)
let handle = ObserverHandle(store.next_observer_id)
store.next_observer_id += 1
store.observers.set(handle, { target: None, callback })
handle
}
///|
pub fn[T] Events::observe_entity(
self : Events[T],
entity : @entity.Entity,
callback : (Trigger[T]) -> Unit,
) -> ObserverHandle {
let store = world_store(self)
let handle = ObserverHandle(store.next_observer_id)
store.next_observer_id += 1
store.observers.set(handle, { target: Some(entity), callback })
handle
}
///|
pub fn[T] Events::unobserve(self : Events[T], handle : ObserverHandle) -> Unit {
let store = world_store(self)
store.observers.remove(handle)
}
///|
pub fn[T] Events::clear(self : Events[T]) -> Unit {
let store = world_store(self)
store.older.clear()
store.newer.clear()
store.observer_queue.clear()
}
///|
pub fn[T] Events::update(self : Events[T]) -> Unit {
let store = world_store(self)
store.older = store.newer
store.newer = []
}
///|
pub fn[T] Events::dispatch_observers(self : Events[T]) -> Unit {
let store = world_store(self)
for item in store.observer_queue {
for _handle, observer in store.observers {
let deliver = match observer.target {
None => true
Some(entity) =>
match item.target {
Some(target) => target == entity
None => false
}
}
if deliver {
(observer.callback)(item)
}
}
}
store.observer_queue.clear()
}
///|
pub fn[T] read(reader : EventReader[T], events : Events[T]) -> Array[T] {
reader.read(events)
}
///|
pub fn[T] send(events : Events[T], event : T) -> Unit {
events.send(event)
}
///|
pub fn[T] trigger(
events : Events[T],
event : T,
target? : @entity.Entity?,
) -> Unit {
events.trigger(event, target.unwrap_or(None))
}
///|
pub fn[T] observe_global(
events : Events[T],
callback : (Trigger[T]) -> Unit,
) -> ObserverHandle {
events.observe_global(callback)
}
///|
pub fn[T] observe_entity(
events : Events[T],
entity : @entity.Entity,
callback : (Trigger[T]) -> Unit,
) -> ObserverHandle {
events.observe_entity(entity, callback)
}
///|
pub fn[T] unobserve(events : Events[T], handle : ObserverHandle) -> Unit {
events.unobserve(handle)
}
///|
fn update_domain(domain : EventDomain) -> Unit {
let world_id = current_world_id()
for bus in registered_buses_by_world.get(world_id).unwrap_or([]) {
if bus.domain == domain {
(bus.update)()
}
}
}
///|
pub fn event_frame_update_system(_delta : Double) -> Unit {
update_domain(Frame)
}
///|
pub fn event_fixed_update_system(_delta : Double) -> Unit {
update_domain(Fixed)
}
///|
pub fn event_render_update_system(_delta : Double) -> Unit {
update_domain(Render)
}
///|
pub fn event_observer_dispatch_system(_delta : Double) -> Unit {
let world_id = current_world_id()
for bus in registered_buses_by_world.get(world_id).unwrap_or([]) {
(bus.dispatch)()
}
}