Skip to content

levilamina::event

Events: subscribing, reading a payload, editing it, cancelling.

A missing key and a value of 0 must stay apart

Contract Β§5.1 records a land-protection bypass: an event in a custom dimension could not read dim, the consumer wrote unwrap_or(0) and treated it as the overworld, and it was allowed with nothing logged. Event therefore offers typed access, where a missing key and a type mismatch are two different errors.

It also recognizes _unresolved, a marker the host injects when it cannot resolve the source of an event. Methods such as Event::dim return Err on it, so a protection decision fails closed instead of continuing with an invented 0.

Wiring does chained batch subscription and holds the handles together.

Functions

event::subscribe

Rust
1
2
3
4
pub fn subscribe(
    id: &str,
    handler: impl FnMut(&mut Event<'_>) + Send + 'static,
) -> Result<Listener>

Subscribes to an event.

Rust
1
2
3
4
5
6
7
8
let l = event::subscribe(names::PLAYER_CHAT, |ev| {
    let Ok(msg) = ev.str_at("message") else { return };
    if !msg.contains("badword") { return; }
    // cancel() returns a Result: an event that cannot be blocked says why and where.
    if let Err(e) = ev.cancel() {
        Logger::get().error(&format!("the chat filter did not take effect: {e}"));
    }
})?;
  • Parameters:
    • id : &str
    • handler : impl FnMut(&mut Event<'_>) + Send + 'static
  • Return type: Result<Listener>
  • Slots: subscribe_event

event::subscribe_with

Rust
1
2
3
4
5
pub fn subscribe_with(
    id: &str,
    priority: Priority,
    handler: impl FnMut(&mut Event<'_>) + Send + 'static,
) -> Result<Listener>

Subscribes with a priority.

  • Parameters:
    • id : &str
    • priority : Priority
    • handler : impl FnMut(&mut Event<'_>) + Send + 'static
  • Return type: Result<Listener>
  • Slots: subscribe_event

event::list

Rust
pub fn list() -> Vec<String>

Every event id the host knows, from the registry plus every synthetic event.

Reading it beats guessing when an event name is misspelled.

event::exists

Rust
pub fn exists(id: &str) -> bool

Whether this host knows a given event id.

  • Parameters:
    • id : &str
  • Return type: bool
  • Slots: list_events

Event

Rust
1
2
3
pub struct Event<'a> {
    // private fields
}

One event dispatch. This is what a callback receives.

It lives only for the duration of the callback and must not be stored, which the lifetime parameter also prevents.

  • Implements: Debug

Event::id

Rust
pub fn id(&self) -> &str

The event id, such as "ll::event::player::PlayerChatEvent" or the synthetic "BlockDestroyEvent".

  • Return type: &str

Event::snbt

Rust
pub fn snbt(&self) -> &str

The raw payload SNBT. The most direct thing while debugging.

  • Return type: &str

Event::value

Rust
pub fn value(&mut self) -> Result<&NbtValue>

The parsed payload. Parsed on the first call and reused afterwards.

  • Return type: Result<&NbtValue>

Event::value_opt

Rust
pub fn value_opt(&mut self) -> Option<&NbtValue>

Returns None rather than an error when the payload cannot be parsed, for an observing listener that treats an unparsable payload as no event at all.

  • Return type: Option<&NbtValue>

Event::str_at

Rust
pub fn str_at(&mut self, path: &str) -> Result<&str>

Reads a string by path. A missing key and a type mismatch both name the key.

  • Parameters:
    • path : &str
  • Return type: Result<&str>

Event::i64_at

Rust
pub fn i64_at(&mut self, path: &str) -> Result<i64>
  • Parameters:
    • path : &str
  • Return type: Result<i64>

Event::i32_at

Rust
pub fn i32_at(&mut self, path: &str) -> Result<i32>
  • Parameters:
    • path : &str
  • Return type: Result<i32>

Event::f64_at

Rust
pub fn f64_at(&mut self, path: &str) -> Result<f64>
  • Parameters:
    • path : &str
  • Return type: Result<f64>

Event::bool_at

Rust
pub fn bool_at(&mut self, path: &str) -> Result<bool>
  • Parameters:
    • path : &str
  • Return type: Result<bool>

Event::opt_str

Rust
pub fn opt_str(&mut self, path: &str) -> Option<String>

The lenient form: None when it cannot be read, with no explanation. Only for cases where not reading it does not matter.

  • Parameters:
    • path : &str
  • Return type: Option<String>

Event::opt_i64

Rust
pub fn opt_i64(&mut self, path: &str) -> Option<i64>
  • Parameters:
    • path : &str
  • Return type: Option<i64>

Event::unresolved

Rust
pub fn unresolved(&mut self) -> Vec<String>

The list of fields the host could not resolve, _unresolved.

When an event carries an Actor stub that is neither an online player nor present in the runtime actor table, the host records the field name here. A non-empty list means the payload is incomplete, and a protection decision should refuse rather than guess. The list is empty too when the payload could not be parsed at all, which Self::check_complete does not count as complete.

  • Return type: Vec<String>

Event::check_complete

Rust
pub fn check_complete(&mut self) -> bool

Checks whether the payload is complete: it parsed, and _unresolved is empty. A payload that did not parse is not complete, since nothing in it could be resolved.

  • Return type: bool

Event::dim

Rust
pub fn dim(&mut self) -> Result<i32>

Which dimension the event happened in.

An unreadable value is an Err. An earlier design had callers write payload.i32_at("dim").unwrap_or(0), so every event in a custom dimension, whose id is 3 or above, was judged to be in the overworld, and land protection refusing in the overworld and allowing elsewhere was bypassed with nothing logged.

  • Return type: Result<i32>

Event::player

Rust
pub fn player(&mut self) -> Option<PlayerIdentity>

The player identity inside an event.

Handles three shapes: the _player:{name,xuid,uuid} of a synthetic event, the _player the host enriched, and an older event carrying only a name. Callers used to have to know the differences themselves.

  • Return type: Option<PlayerIdentity>

Event::pos

Rust
pub fn pos(&mut self) -> Result<(i32, i32, i32)>

The block or position coordinates in an event, as the three flat fields x, y and z, which is the shape of a synthetic event.

  • Return type: Result<(i32, i32, i32)>

Event::pos_f64

Rust
pub fn pos_f64(&mut self) -> Result<(f64, f64, f64)>

As above but as floating point, for a player position and the like.

  • Return type: Result<(f64, f64, f64)>

Event::can_cancel

Rust
pub fn can_cancel(&self) -> Option<bool>

Whether this event can be cancelled.

  • Some(true): it can;
  • Some(false): it cannot, and Event::cancel returns Err;
  • None: it is not in the tables, being an event a third-party mod emits itself or a new upstream event the tables have not caught up with. cancel() then writes back as usual and nobody can confirm for you that it took effect.

  • Return type: Option<bool>

Event::cancel

Rust
pub fn cancel(&mut self) -> Result<()>

Cancels this event.

An event that cannot be cancelled returns Err and says which event to block instead, such as PlayerStartDestroyBlockEvent pointing at PlayerDestroyBlockEvent. Returning () would let a protection mod believe it had blocked something, and that belief is more dangerous than a crash, since a crash is at least visible.

An event that is not in the tables, where can_cancel() is None, does not stand in the way: it writes back as usual and returns Ok. The SDK does not pretend to know what it does not know.

An Ok means only that the cancel bit was written back to the host and not that the engine stopped: some hook points sit half updated and the host does not accept a cancel there at all. That boundary rests on the event documentation alone.

  • Return type: Result<()>

Event::cancel_lenient

Rust
pub fn cancel_lenient(&mut self) -> bool

Cancels without caring whether cancelling is possible.

There is one legitimate use: a generic forwarding or proxy component where the event id arrives at runtime and failing to block simply has to be accepted. Business code uses Event::cancel and handles the Err.

  • Return type: bool

Event::uncancel

Rust
pub fn uncancel(&mut self)

Undoes an earlier cancel by writing cancelled back to 0.

For a two-stage decision that blocks first, decides, and then finds it may allow. Note that it undoes only a cancel written by this callback itself: a cancel another mod made at an earlier priority cannot be undone, and the host bus does not allow a veto to be turned back into an approval either.

Event::set

Rust
pub fn set(&mut self, path: &str, value: NbtValue)

Edits one field of the payload.

The write-back is a difference: only the keys really touched go back to the host and untouched ones stay as they are, so two mods on the same event do not erase each other's edits.

  • Parameters:
    • path : &str
    • value : NbtValue

Event::edit

Rust
pub fn edit(&mut self, f: impl FnOnce(&mut NbtValue))

An arbitrary rewrite. The closure receives a mutable copy of the payload.

This is the one path that copies the whole payload; Event::set and Event::cancel write only the keys they touch.

  • Parameters:
    • f : impl FnOnce(&mut NbtValue)

Wiring

Rust
1
2
3
pub struct Wiring {
    // private fields
}

Batch subscription, holding the handles together.

Two things over subscribing by hand: a failed subscription is not silent, since failures are recorded in Wiring::failures and arm() can fail as a whole, and the tag goes into the log so the failing entry can be located.

Rust
1
2
3
4
5
6
let wiring = Wiring::new("plots")
    .on(names::PLAYER_DESTROY_BLOCK, "protect-break", |ev| { ... })
    .at(names::PLAYER_DISCONNECT, Priority::Low, "forget", |ev| { ... })
    .arm()?;                       // any failure fails the whole thing

// wiring going out of scope unsubscribes everything

Wiring::new

Rust
pub fn new(owner: impl Into<String>) -> Wiring
  • Parameters:
    • owner : impl Into<String>
  • Return type: Wiring

Wiring::on

Rust
1
2
3
4
5
6
pub fn on(
        self,
        id: &str,
        tag: &str,
        handler: impl FnMut(&mut Event<'_>) + Send + 'static,
    ) -> Wiring

Adds a subscription at Normal priority. tag is used only for logging.

  • Parameters:
    • id : &str
    • tag : &str
    • handler : impl FnMut(&mut Event<'_>) + Send + 'static
  • Return type: Wiring

Wiring::at

Rust
1
2
3
4
5
6
7
pub fn at(
        mut self,
        id: &str,
        priority: Priority,
        tag: &str,
        handler: impl FnMut(&mut Event<'_>) + Send + 'static,
    ) -> Wiring

Adds a subscription at a given priority.

  • Parameters:
    • id : &str
    • priority : Priority
    • tag : &str
    • handler : impl FnMut(&mut Event<'_>) + Send + 'static
  • Return type: Wiring

Wiring::arm

Rust
pub fn arm(mut self) -> Result<Wiring>

Performs the subscriptions. Any failure fails the whole thing and the ones that succeeded are unsubscribed before returning, because half-attached protection is more dangerous than none: some points block and some do not, and nobody knows which.

Wiring::arm_lenient

Rust
pub fn arm_lenient(mut self) -> Wiring

The lenient form: failures are recorded and successes attach as usual. Suited to an optional feature that is nice to have.

Wiring::failures

Rust
pub fn failures(&self) -> &[(String, String)]

The ones that did not attach, as (tag, reason).

  • Return type: &[(String, String)]

Wiring::armed

Rust
pub fn armed(&self) -> usize

How many attached.

  • Return type: usize

Wiring::forget

Rust
pub fn forget(mut self)

Switches everything to no automatic unsubscription, living until the mod unloads.

PlayerIdentity

Rust
1
2
3
4
5
pub struct PlayerIdentity {
    pub name: String,
    pub xuid: String,
    pub uuid: String,
}

The player identity inside an event.

The three fields each have their own use and must not be mixed: * xuid is unique and cannot be changed, and is the only key for permissions or economy. It may be empty in offline mode. * uuid is equally stable and suits a save key. * name is for display. A player can change their display name, and host name resolution falls back to the display name when the account name misses (see bridge::resolvePlayer), so it must not be used as an identity.

  • Implements: Debug, Clone, Default, PartialEq, Eq, From

PlayerIdentity::selector

Rust
pub fn selector(&self) -> crate::sel::PlayerSel

Returns a selector usable for calling the API.

The xuid comes first, since it cannot be forged. An empty xuid, in offline mode, falls back to the uuid and then to the name. On a fall back to Name, PlayerSel::is_stable is false, which a permission or economy decision should treat with care, since a name goes through the display-name fallback; see the sel module.

  • Return type: crate::sel::PlayerSel

PlayerIdentity::is_identified

Rust
pub fn is_identified(&self) -> bool

Whether there is a reliable identity, an xuid or a uuid. Ask this before using one as a permission key.

  • Return type: bool

Listener

Rust
1
2
3
pub struct Listener {
    // private fields
}

A subscription handle. Dropping it unsubscribes.

Listener::forget keeps it alive until the mod unloads. The host removes whatever remains at unload, really calling removeListener and reporting a failure rather than staying silent.

  • Implements: Drop

Listener::event_id

Rust
pub fn event_id(&self) -> &str

The id of the subscribed event.

  • Return type: &str

Listener::forget

Rust
pub fn forget(mut self)

Gives up automatic unsubscription. The closure leaks with it and lives until the process ends.

Priority

Rust
1
2
3
4
5
6
7
8
pub enum Priority {
        Highest = 0,
        High = 1,
        #[default]
        Normal = 2,
        Low = 3,
        Lowest = 4,
}

Dispatch priority. A lower value runs first, aligned with 0..4 in the ABI.

  • Implements: Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default

EventRef

Rust
pub type EventRef<'a> = Event<'a>;

An earlier generation called this EventRef. The name is kept and points at the same type.

EventPriority

Rust
pub type EventPriority = Priority;

An earlier generation called the priority EventPriority.