# Tutorial: first messages

Prefer **`hi.actors` / `Actors.*`** for find, send, and (when needed) name-keyed component fields. Delivery is deferred until after entity updates in the same Play tick. Zig and Kawa both use the World event dispatcher; recipients run Zig handlers first, then Kawa handlers.

Deep reference: [Actor communication](/docs/systems/entity-messaging.md). Sample: `hikari-games/example/scenes/messaging.json` and the `message_*` entities.

## Concepts in one minute

| Call | Meaning |
|---|---|
| `hi.actors.send` / `Actors.send` | One target (handle or scene id) |
| `hi.actors.emit` / `Actors.emit` | Every registered listener |
| Zig `onMessage` | Receiver on logic components (runs before Kawa) |
| Kawa `on_message` | Receiver on each script slot that declares it |
| `isAlive` / `is_alive` | Generation still live — refs never pin actors |

Stable scene `"id"` strings are the usual address.

---

## Zig → Zig / Kawa

Sender (sample pad — fires once on trigger enter). Prefer **self-as-sender** helpers so you don’t pass `id` as `.from`:

```zig
const hi = @import("hikari_game");

// Inside a component Data struct:
pub fn onCollision(_: *@This(), actor: hi.ActorContext, info: *const hi.CollisionInfo) void {
    if (info.phase != .enter) return;
    actor.sendEventTo("msg_door_zig", "open", .{});
    actor.sendEventTo("msg_door_kawa", "open", .{});
    actor.emitEvent("ping", .{});
}
```

Zig receiver (on a `defineComponent` data type):

```zig
pub fn onMessage(_: *@This(), _: hi.ActorContext, msg: *const hi.Message) void {
    if (hi.actors.messageNameEql(msg.name, "open")) {
        // open door …
    }
}
```

Inside a callback with `actor: hi.ActorContext`, you can also branch on a live handle:

```zig
if (hi.actors.tryFind("msg_door_zig")) |door| {
    hi.actors.sendEventFrom(actor.id, door, "open", .{});
}
// Fire-and-forget does not need isAlive — dead targets no-op.
```

---

## Kawa → doors / listeners

Kawa receiver (`assets/scenes/messaging/scripts/message_door.kawa`):

```kawa
fn on_message(name, from, a0, a1, a2, a3) {
    if (name == "open") {
        // Actor.set_position(...)
    }
}
```

Send from script (preferred namespace):

```kawa
Actors.send("msg_door_zig", "open");
Actors.emit("ping");

let door = Actors.find("msg_door_kawa");
if (Actors.is_alive(door)) {
    Actors.send(door, "open");
}
```

`World.send` / `World.find` still work; use `Actors.*` for new scripts.

Script-primary door archetype: `message_kawa_door_entity.zig` (default script `asset://./scenes/messaging/scripts/message_door`).

---

## Optional: read a field on another actor

When you need data rather than an event. Prefer **component-bound** helpers so the component name is not repeated as a string (numbers / bools / enums here; string and asset field helpers are also available — see [Actor communication](/docs/systems/entity-messaging.md)):

```zig
// Health = hi.defineComponent(.{ .name = "health", .data = struct { hp: f32 = 100 } });
if (hi.actors.tryFind("enemy_00")) |enemy| {
    if (enemy.component(Health).has()) {
        const hp = enemy.component(Health).get(.hp) orelse 0;
        _ = enemy.component(Health).set(.hp, hp - 10);
    }
}
```

```kawa
if (Actors.has(enemy, "health")) {
    let hp = Actors.get_number(enemy, "health", "hp");
    Actors.set_number(enemy, "health", "hp", hp - 10);
}
```

Same-actor Zig siblings with known types: prefer `ActorContext.get(Health)` in lifecycle callbacks (see [Actor communication — ActorContext](/docs/systems/entity-messaging.md#same-actor-typed-zig-actorcontext)).

---

## Try the messaging scene

1. Play `physics_playground.json` (or any scene with the sample session HUD).
2. Choose **Browse scenes → Tutorials → Messaging**, or open `scenes/messaging.json` directly.
3. Both doors start **closed** (low). The falling weight cube lands on the pad — Zig (blue) and Kawa (pink) doors lift; listeners receive `ping`. You can also walk onto the pad yourself after Reload.
4. Reload / re-Play should restore both doors closed until the pad fires again.

---

## Gotchas

- Messages flush **after** updates — do not expect synchronous side effects in the same `update`. Game modules enqueue via `hi.actors.send` / `sendToSceneId` / `emit` only (no HostApi `sendNow` for games).
- Kawa’s standalone `Events.*` bus is disabled in Hikari, including its `events_*` aliases. Use `Actors.*` for gameplay messages.
- **`ActorRef` is weak** — despawn/unload invalidates handles; check `isAlive`. Refs never keep an actor loaded.
- Additive layers require **unique** entity `id`s across all loaded layers.
- Cross-actor “call a function by name” is **not** an API — send a message (or set a field).

## Next

- [Actor communication](/docs/systems/entity-messaging.md) — full API, fan-out, field access
- [Play, Edit, and scenes](/docs/tutorials/play-edit-and-scenes.md)
- [First entity](/docs/tutorials/first-entity.md) — add your own door / pad pair
- [Game-facing refs](/docs/systems/refs.md) — `ActorRef` rules
