Skip to content

@web-engine-dev/events

Double-buffered event system with frame lifecycle for web-engine-dev. Provides a frame-synchronized event system where events written in one frame are available for reading in the next frame, ensuring deterministic behavior.

Features

  • Double Buffering: Events written this frame are read next frame
  • Frame Synchronization: Deterministic event ordering
  • Type-Safe Events: Generic event readers and writers
  • Memory Management: Automatic buffer clearing with configurable limits

Installation

bash
npm install @web-engine-dev/events
# or
pnpm add @web-engine-dev/events

Quick Start

typescript
import { createEventSystem, defineEvent } from '@web-engine-dev/events';

// Create the event system
const events = createEventSystem();

// Define event types
const COLLISION_EVENT = defineEvent('collision');
interface CollisionEvent {
  type: typeof COLLISION_EVENT;
  entityA: number;
  entityB: number;
}

// Write events
const writer = events.writer<CollisionEvent>(COLLISION_EVENT);
writer.send({ type: COLLISION_EVENT, entityA: 1, entityB: 2 });

// Advance frame (swap buffers)
events.update();

// Read events
const reader = events.reader<CollisionEvent>(COLLISION_EVENT);
for (const event of reader.read()) {
  console.log(`Collision: ${event.entityA} <-> ${event.entityB}`);
}

API Reference

EventSystem

MethodDescription
registerEvent()Pre-register an event type
reader<T>(type)Get a reader for event type
writer<T>(type)Get a writer for event type
update()Advance frame and swap buffers
currentFrame()Get current frame number
clear()Clear all events from all channels

EventReader

MethodDescription
read()Iterator of events from last frame
isEmpty()Check if any events available
length()Number of events available
clear()Mark events as consumed

EventWriter

MethodDescription
send(event)Emit a single event
sendBatch()Emit multiple events

Game Loop Integration

typescript
const events = createEventSystem();

function gameLoop() {
  // Start of frame - swap buffers
  events.update();

  // Systems can now read last frame's events
  const damageReader = events.reader<DamageEvent>(DAMAGE_EVENT);
  for (const event of damageReader.read()) {
    applyDamage(event.target, event.amount);
  }

  // Systems can write new events
  const damageWriter = events.writer<DamageEvent>(DAMAGE_EVENT);
  if (collision) {
    damageWriter.send({ type: DAMAGE_EVENT, target: entity, amount: 10 });
  }
}

Configuration

typescript
const events = createEventSystem({
  maxEventsPerType: 1000, // Prevent memory leaks
  retentionFrames: 2, // Number of frames to retain
});

Advanced Usage

Event Descriptors

For advanced use cases (validation, custom lifetime), use defineEventDescriptor.

typescript
import { defineEventDescriptor } from '@web-engine-dev/events';

const DamageEvent = defineEventDescriptor<{ amount: number }>('DamageEvent', {
  validate: (data) => data.amount > 0,
  lifetime: 1, // Persist for only 1 frame
});

Filtered Readers

Create readers that only see specific events.

typescript
const criticalEvents = events.filteredReader(LOG_EVENT, (e) => e.severity === 'critical');
for (const event of criticalEvents.read()) {
  // Only critical events
}

Priority Handlers

Register handlers that execute immediately when events are processed (during update()).

typescript
events.onEvent(COLLISION_EVENT, (event) => {
  // Handle immediately
}, 100); // Priority 100 (higher runs first)

Async Events

Send events that will be queued for the next frame asynchronously.

typescript
await writer.sendAsync(event);

Performance Tips

For maximum performance in tight loops, use forEach instead of read() iterator.

typescript
// Faster than for..of loop
reader.forEach((event) => {
  process(event);
});

Proprietary software. All rights reserved.