pebble-engine 0.13.0

A modular, ECS-style graphics/app framework for Rust.
Documentation
# Events

Resources are good for "the current state of X." Events are for "something happened" — damage was dealt, a file finished loading, a button was clicked. `Events<T>` is a double-buffered queue: an event sent during tick `N` stays visible to every reader for the rest of `N` and all of `N + 1`, then is dropped.

## Sending and reading

```rust
struct Damage(u32);

app.add_event::<Damage>();

fn deal_damage(mut writer: EventWriter<Damage>) {
    writer.send(Damage(5));
}

fn on_damage(mut reader: EventReader<Damage>) {
    for event in reader.iter() {
        println!("took {} damage", event.0);
    }
}
```

`app.add_event::<T>()` does two things: inserts the `Events<T>` resource, and registers the per-tick aging step that gives the two-tick guarantee above. An `EventWriter<T>`/`EventReader<T>` used before this call panics with a hint pointing back at `add_event` — same hard-requirement mechanism as `Res<T>` from the previous chapter, just checking for `Events<T>` specifically.

## Why two ticks?

Systems run in a fixed order within a tick, so a reader registered *before* the writer in the stage order would never see a same-tick send if events only lived for the tick they were sent in — it already ran by the time the writer fires. Keeping an event visible through the *next* tick as well means every reader sees every event exactly once, regardless of where in the pipeline it happens to run relative to the writer. Each `EventReader<T>` tracks its own read cursor privately (the same way `Local<T>` persists per-system state), so multiple independent readers of the same event type never interfere with each other.

## `Option<EventReader<T>>` / `Option<EventWriter<T>>`

Exactly like `Option<Res<T>>`: use these when a system should just skip its event-related work if `T` hasn't been registered yet, instead of hard-panicking:

```rust
fn maybe_log_damage(reader: Option<EventReader<Damage>>) {
    let Some(mut reader) = reader else { return };
    for event in reader.iter() {
        // ...
    }
}
```

This matters most for library-ish code — a plugin that optionally reacts to an event type the *host* application may or may not have registered, without forcing that application to always register it.

Events whose payload arrives from a background task — a downloaded file, a GPU readback — use a different constructor, `add_async_event`, covered in the next chapter alongside the rest of Pebble's async story.