Skip to main content

observer

Attribute Macro observer 

Source
#[observer]
Expand description

Implements the Observer trait for your struct.

This is the main macro that ties everything together. Apply it to an impl block that contains event handler methods marked with #[on_*] attributes.

§What It Does

  • Automatically implements the Observer trait
  • Generates code to call all handler methods at appropriate times
  • Sets up interest flags based on which handlers are defined
  • Decodes protobuf messages and passes them to handlers
  • Filters events based on optional string arguments

§Handler Attributes

Use these attributes inside the impl block to mark event handlers:

  • #[on_tick_start] - Called at the start of each tick
  • #[on_tick_end] - Called at the end of each tick
  • #[on_entity] - Called when entities change
  • #[on_entity("ClassName")] - Only for specific entity classes
  • #[on_message] - Called for protobuf messages (type inferred from param)
  • #[on_game_event] - Called for all game events
  • #[on_game_event("event_name")] - Only for specific events
  • #[on_string_table] - Called when string tables update
  • #[on_string_table("table_name")] - Only for specific tables
  • #[on_stop] - Called when replay ends
  • #[on_combat_log] - Called for combat log entries (Dota 2 only)

§Trait Attributes

Apply these to the impl block or individual methods to enable tracking:

  • #[uses_entities] - Enable entity tracking
  • #[uses_string_tables] - Enable string table tracking
  • #[uses_game_events] - Enable game event tracking
  • #[uses_combat_log] - Enable combat log tracking (Dota 2 only)

§Parameter Guidelines

Handlers can have these parameters (all optional except &mut self):

  • ctx: &Context - Current replay state (always optional)
  • Specific parameters depending on the handler:
    • event: EntityEvents (on_entity)
    • entity: &Entity (on_entity)
    • ge: &GameEvent (on_game_event)
    • table: &StringTable (on_string_table)
    • modified: &[i32] (on_string_table)
    • cle: &CombatLogEntry (on_combat_log)
    • Protobuf message types (on_message)

§Examples

§Basic observer

#[derive(Default)]
struct BasicObserver;

#[observer]
impl BasicObserver {
    #[on_tick_start]
    fn on_tick_start(&mut self, ctx: &Context) -> ObserverResult {
        println!("Tick: {}", ctx.tick());
        Ok(())
    }
}

§With entity tracking

#[derive(Default)]
struct EntityTracker;

#[observer]
#[uses_entities]
impl EntityTracker {
    #[on_entity]
    fn on_hero_created(&mut self, event: EntityEvents, entity: &Entity) -> ObserverResult {
        if event == EntityEvents::Created && entity.class().name().starts_with("CDOTA_Unit_Hero_") {
            println!("Hero created: {}", entity.class().name());
        }
        Ok(())
    }
}

§With multiple handlers

#[derive(Default)]
struct ComplexObserver {
    ticks: u32,
    messages: u32,
}

#[observer]
impl ComplexObserver {
    #[on_tick_start]
    fn on_tick(&mut self, ctx: &Context) -> ObserverResult {
        self.ticks += 1;
        Ok(())
    }

    #[on_message]
    fn on_chat(&mut self, msg: CDotaUserMsgChatMessage) -> ObserverResult {
        self.messages += 1;
        println!("Message: {}", msg.message_text());
        Ok(())
    }

    #[on_stop]
    fn on_replay_end(&mut self) -> ObserverResult {
        println!("Total ticks: {}, messages: {}", self.ticks, self.messages);
        Ok(())
    }
}

§With game event filtering

#[derive(Default)]
struct DeathTracker {
    deaths: u32,
}

#[observer]
impl DeathTracker {
    #[on_game_event("player_death")]
    fn on_death(&mut self, ctx: &Context, ge: &GameEvent) -> ObserverResult {
        self.deaths += 1;
        Ok(())
    }
}

§Interest Flags

The macro automatically determines which interest flags to set based on which handlers are defined. For example:

  • If you have #[on_tick_start], Interests::TICK_START is added
  • If you have #[on_entity], both ENABLE_ENTITY and TRACK_ENTITY are added
  • If you have #[on_message] handlers, appropriate message interest flags are added

You can also use trait attributes like #[uses_entities] to manually ensure certain interests are set.