Current event-family contracts

TypeOrder and failureOwnership trap
EventSubscription order, snapshot dispatch; logs and re-raises the first listener failure.Removal during dispatch does not cancel a handler already in the snapshot.
SafeEventSame ordered snapshot, but isolates each listener failure and continues.An on... name does not prove this type.
SynchronousEventAwaits listeners sequentially and suppresses their logged failures.Own completion by awaiting the returned client future.
ContextEventExact-context set, then all-context set; no defined order, snapshot, or exception isolation.A delegate in both buckets can run twice; mutation during dispatch is unsafe.
GUI EventBusScope- and event-type-based lists with separate dispatch and completion rules.It is not interchangeable with the core Event family.

Ordinary listeners are strongly retained. Snapshot dispatch protects iteration structure, not owner lifetime. A one-shot callback that raises can remain subscribed unless removal is in a finally path.

Subscriber and suspension lifetimes

The current EventsSubscriber clears ordinary records on unsubscribe but retains its context-subscription records. Treat one instance as belonging to one lifecycle: unsubscribe it, then discard it rather than recycling it across repeated starts.

Suspended events retain queued arguments strongly and replay them in FIFO order. Clearing the manager discards queued calls and terminally clears its managed events. Bound the queue and make replayed handlers reject objects whose context ended while the event was suspended.

FIFO describes queued invocation extraction, not all listener work. During SuspendedEventManager.resume(), the manager is already active, so a listener's nested event normally runs immediately before the next queued invocation.

A listener failure aborts the drain after the current invocation has been removed. Earlier effects remain, and the remaining queue is retained. Unless a listener changed suspension state, another resume() does nothing because the manager is already active. Define an owner-level failure policy instead of assuming batch completion or blindly replaying side effects.

Version-sensitive Python control flow, verified in the Event implementation at latest public source. This does not establish production usage or a live client failure.

State hooks can leave partial transition bookkeeping

Current public source; version-sensitive. A state's entry or exit flag changes before its corresponding hook runs. The core machine appends a target before calling its entry method, but removes an exiting state only after its exit method returns. An entry-hook exception can therefore leave the target listed and flagged entered before observer notification; an exit-hook exception can leave an inactive source in the machine's entered-state list. Flag, membership, hook completion and notification are distinct observations.

The lobby machine's post() catches and logs ordinary Exception from its guarded core-processing call, releases the current-event marker and can process queued navigation. Normal return does not establish that the transition completed or rolled back. Errors before that guarded call have a different boundary. Keep this separate from navigation intent and asynchronous hook completion when diagnosing readiness.

Selected-source checks used controlled state hooks, transition selection and navigation events. They establish conditional Python bookkeeping, not automatic recovery, full transition-domain selection, native event delivery or a failed live route.

Machine shutdown has a separate cleanup path

On the normal path for a running framework state machine, core stop() clears observers, history, the entered-state list and the state graph. The recursive clear path resets state flags without calling State.exit, _onExited or exit notifications. An observer's clear() is its own teardown call. Already-stopped core machines return early, so stop is not an unconditional graph-clear operation. An observer-clear exception can interrupt the sequence after the running flag has changed; while it stays stopped, another core stop does not retry pending cleanup.

Resources needed during both state exit and graph destruction need cleanup through the appropriate owner paths. The gameplay waiting state cancels its waiting callback from both clear and _onExited. The lobby subclass posts stop transitions before delegating in its normal running branch; its behavior includes that additional phase. These current-source and controlled-graph checks establish Python call ordering, not live callback cancellation, lobby transition completion or service-manager finalization.

Common mistakes

  • Assuming every event isolates listener exceptions.
  • Subscribing on every screen refresh without an idempotent guard.
  • Removing a newly created lambda instead of the original callable.
  • Doing blocking I/O inside synchronous dispatch.
  • Allowing an old view or arena listener to retain dead objects.

Verification trail

Inspect the event implementation and every fire site. A name such as “safe” or “global” is not enough; confirm actual exception and lifetime behavior.

Mechanic state publication order

MechanicStatesEvents has four distinct signals. Preparation stores the first current state; transition uses the new state's predicate and publishes before replacing retained state; observation stores then publishes every supplied update; ticks republish the retained object. Field changes can therefore produce observation without a transition, and time-reading properties can change across ticks without a new snapshot.

Late subscription directly calls prepared and observation handlers with current state before registration. These are initialization callbacks rather than replayed history, and their exceptions are outside ordinary SafeEvent isolation. Transition listeners can also reenter updates: an inner observation can be followed by the outer update overwriting retained state.

Prefer event arguments at the documented publication boundary and avoid assuming all getters agree during dispatch. Retain/remove listener identity and let the interval-event owner stop its ticks when no listeners remain. See state interpretation.

Visual-script outputs can evaluate state

A method decorated with vse_event_out can dispatch through a native visual-script context even if its visible body is pass. Inspect the decorator/aspect wrapper and registered dispatcher before calling such a method unimplemented.

VisualScriptEquipment.canActivate emits a graph evaluation event before reading context availability/error fields. It is not a passive snapshot getter; repeated diagnostics can evaluate the plan. Keyword arguments reaching the Python body are not necessarily part of the wrapper's positional dispatcher tuple. Preserve the actual boundary contract.

Use the client's Python runtime when checking dispatcher construction. Class-member introspection differs between Python 2 and Python 3, so a Python 3 harness that creates no dispatchers does not establish that the client lacks them. Native graph execution still requires its own check.