Events Reference¶
This reference documents all built-in events in MAID. Events are organized by their source package.
Event Base Class¶
All events inherit from Event:
from dataclasses import dataclass
from datetime import datetime
@dataclass
class Event:
"""Base class for all events."""
event_type: str # Auto-set to class name
timestamp: datetime # Auto-set to current time
cancelled: bool # Set to True to stop propagation
def cancel(self) -> None:
"""Cancel this event."""
self.cancelled = True
Engine Events (maid-engine)¶
Core lifecycle events from the engine.
TickEvent¶
Emitted at the start of each game tick.
@dataclass
class TickEvent(Event):
tick_number: int # Sequential tick counter
delta: float # Time since last tick in seconds
Use cases:
- Time-based processing
- Periodic tasks
- Animation/effect timing
Example:
async def on_tick(event: TickEvent):
if event.tick_number % 4 == 0: # Every second at 4 ticks/sec
await process_regeneration()
world.events.subscribe(TickEvent, on_tick)
EntityCreatedEvent¶
Emitted when an entity is created.
Use cases:
- Initialize entity state
- Register entity in indexes
- Trigger spawn effects
EntityDestroyedEvent¶
Emitted when an entity is destroyed.
Use cases:
- Clean up entity state
- Remove from indexes
- Trigger death/despawn effects
ComponentAddedEvent¶
Emitted when a component is added to an entity.
@dataclass
class ComponentAddedEvent(Event):
entity_id: UUID # Entity that received the component
component_type: str # Type name of the component
Use cases:
- React to capability changes
- Update system indexes
- Trigger effect application
ComponentRemovedEvent¶
Emitted when a component is removed from an entity.
@dataclass
class ComponentRemovedEvent(Event):
entity_id: UUID # Entity that lost the component
component_type: str # Type name of the component
Use cases:
- React to capability loss
- Update system indexes
- Trigger effect removal
PlayerConnectedEvent¶
Emitted when a player connects to the server.
@dataclass
class PlayerConnectedEvent(Event):
session_id: UUID # Network session ID
player_id: UUID | None # Entity ID (None if not yet logged in)
Use cases:
- Initialize session state
- Send welcome messages
- Track connection statistics
PlayerDisconnectedEvent¶
Emitted when a player disconnects from the server.
@dataclass
class PlayerDisconnectedEvent(Event):
session_id: UUID # Network session ID
player_id: UUID | None # Entity ID (None if not logged in)
Use cases:
- Save player state
- Clean up session
- Notify other players
PlayerCommandEvent¶
Emitted before a player command is processed.
@dataclass
class PlayerCommandEvent(Event):
player_id: UUID # Player entity ID
command: str # Command name
args: list[str] # Command arguments
Use cases:
- Command logging
- Rate limiting
- Command interception
Example:
async def log_commands(event: PlayerCommandEvent):
logger.info(f"Player {event.player_id} executed: {event.command}")
world.events.subscribe(PlayerCommandEvent, log_commands)
RoomEnterEvent¶
Emitted when an entity enters a room.
@dataclass
class RoomEnterEvent(Event):
entity_id: UUID # Entity entering
room_id: UUID # Room being entered
from_room_id: UUID | None # Previous room (None if spawning)
Use cases:
- Send room descriptions
- Trigger room scripts
- Notify other occupants
RoomLeaveEvent¶
Emitted when an entity leaves a room.
@dataclass
class RoomLeaveEvent(Event):
entity_id: UUID # Entity leaving
room_id: UUID # Room being left
to_room_id: UUID | None # Destination room (None if despawning)
Use cases:
- Update room state
- Notify other occupants
- Trigger exit scripts
CustomEvent¶
Generic event for ad-hoc use without creating a new event class.
@dataclass
class CustomEvent(Event):
name: str # Custom event name
data: dict[str, Any] # Event data
Use cases:
- Quick prototyping
- Dynamic event types
- Plugin communication
Example:
# Emit custom event
await world.events.emit(CustomEvent(
name="weather_change",
data={"weather": "rain", "intensity": 0.5}
))
# Subscribe to custom events
async def on_custom(event: CustomEvent):
if event.name == "weather_change":
weather = event.data["weather"]
# Handle weather change
world.events.subscribe(CustomEvent, on_custom)
Standard Library Events (maid-stdlib)¶
Common game events from the standard library.
CombatStartEvent¶
Emitted when combat begins between entities.
@dataclass
class CombatStartEvent(Event):
attacker_id: UUID # Entity initiating combat
defender_id: UUID # Entity being attacked
room_id: UUID # Room where combat occurs
Use cases:
- Initialize combat state
- Notify players
- Block certain actions during combat
CombatEndEvent¶
Emitted when combat ends.
@dataclass
class CombatEndEvent(Event):
combatant_ids: list[UUID] # All combatants involved
room_id: UUID # Room where combat occurred
Use cases:
- Clean up combat state
- Award experience
- Enable post-combat actions
DamageDealtEvent¶
Emitted when damage is dealt to an entity.
@dataclass
class DamageDealtEvent(Event):
source_id: UUID | None # Damage source (None for environmental)
target_id: UUID # Entity receiving damage
damage: int # Amount of damage
damage_type: str # Type: "physical", "fire", "ice", etc.
hit_location: str | None # Body location hit (optional)
Use cases:
- Update health displays
- Trigger damage reactions
- Log combat statistics
- Apply damage-based effects
Example:
async def on_damage(event: DamageDealtEvent):
# Check for death
target = world.entities.get(event.target_id)
if target:
health = target.try_get(HealthComponent)
if health and not health.is_alive:
await world.events.emit(EntityDeathEvent(
entity_id=event.target_id,
killer_id=event.source_id,
))
world.events.subscribe(DamageDealtEvent, on_damage)
EntityDeathEvent¶
Emitted when an entity dies.
@dataclass
class EntityDeathEvent(Event):
entity_id: UUID # Entity that died
killer_id: UUID | None # Entity that killed them (if any)
Use cases:
- Drop loot
- Award experience
- Respawn handling
- Death notifications
ItemPickedUpEvent¶
Emitted when an item is picked up.
@dataclass
class ItemPickedUpEvent(Event):
entity_id: UUID # Entity picking up
item_id: UUID # Item being picked up
room_id: UUID # Room where pickup occurred
Use cases:
- Update inventories
- Quest progress tracking
- Achievement tracking
ItemDroppedEvent¶
Emitted when an item is dropped.
@dataclass
class ItemDroppedEvent(Event):
entity_id: UUID # Entity dropping
item_id: UUID # Item being dropped
room_id: UUID # Room where drop occurred
Use cases:
- Update inventories
- Spawn item in room
- Trigger item scripts
MessageEvent¶
Emitted for communication messages (say, tell, channel).
@dataclass
class MessageEvent(Event):
sender_id: UUID | None # Sender (None for system messages)
target_ids: list[UUID] # Recipients
channel: str # Channel name: "say", "tell", "ooc", etc.
message: str # Message content
Use cases:
- Message routing
- Chat logging
- Profanity filtering
- Bridge integration
Event Priority¶
Events are processed in handler priority order:
from maid_engine.core.events import EventPriority
class EventPriority(Enum):
HIGHEST = auto() # Runs first - validation, blocking
HIGH = auto() # Pre-processing
NORMAL = auto() # Standard handlers
LOW = auto() # Post-processing
LOWEST = auto() # Runs last - logging, cleanup
Subscribing to Events¶
from maid_engine.core.events import EventPriority
# Basic subscription
world.events.subscribe(DamageDealtEvent, my_handler)
# With priority
world.events.subscribe(
DamageDealtEvent,
validation_handler,
priority=EventPriority.HIGHEST
)
# One-time subscription
world.events.subscribe(
PlayerConnectedEvent,
first_player_bonus,
once=True
)
Emitting Events¶
# Async emission (immediate)
await world.events.emit(DamageDealtEvent(
source_id=attacker.id,
target_id=target.id,
damage=25,
damage_type="physical",
))
# Sync emission (queued)
world.events.emit_sync(MyEvent(...))
# Process queued events
await world.events.process_pending()
Canceling Events¶
async def block_damage_in_safe_zone(event: DamageDealtEvent):
target = world.entities.get(event.target_id)
if target and is_in_safe_zone(target):
event.cancel() # Prevents further processing
world.events.subscribe(
DamageDealtEvent,
block_damage_in_safe_zone,
priority=EventPriority.HIGHEST
)
Creating Custom Events¶
from dataclasses import dataclass
from uuid import UUID
from maid_engine.core.events import Event
@dataclass
class QuestCompletedEvent(Event):
"""Emitted when a player completes a quest."""
player_id: UUID
quest_id: UUID
quest_name: str
experience_reward: int = 0
gold_reward: int = 0
# Use in your content pack
def get_events(self) -> list[type[Event]]:
return [QuestCompletedEvent]
Event Best Practices¶
- Keep events immutable: Don't modify event data in handlers
- Use specific event types: Avoid overly generic events
- Document event flow: Comment the expected sequence of events
- Handle errors gracefully: Don't let handler errors crash the system
- Clean up subscriptions: Unsubscribe when systems shut down
- Use appropriate priorities: Validation early, logging late