Events are transient messages relayed to the other members of your room — shots fired, emotes, chat lines, "round started" pings. One call to send, one signal to receive.

Send and receive

# Sender — fire and forget, no await:
SpawnWeaver.send_event("player_fired", {"dir": [0, 1], "weapon": "bow"})

# Every OTHER member receives it:
SpawnWeaver.event_received.connect(func(event_name, data, sender):
    match event_name:
        "player_fired":
            spawn_arrow(sender.id, data["dir"], data["weapon"]))
  • event_name is any string you choose; data is any JSON-serializable Dictionary.
  • sender is the SWPlayer who sent it — use sender.id and sender.display_name().
  • You must be in a room; events go to your current room's members only.

Sender-excluded semantics

The sender never receives its own event. Apply the local effect immediately when you send, and handle event_received for everyone else:

func fire(dir: Vector2) -> void:
    spawn_arrow(SpawnWeaver.player.id, [dir.x, dir.y], "bow")   # local, instant
    SpawnWeaver.send_event("player_fired", {"dir": [dir.x, dir.y], "weapon": "bow"})

This is the opposite of state changes (room_state_changed / entity_changed), which do echo back to the sender as the acknowledgment.

Fire-and-forget, with queuing during reconnect

send_event() returns immediately — there is no success reply to await. Failures (rate limit, oversized payload, not in a room) arrive on the error signal:

SpawnWeaver.error.connect(func(err):
    if err.code == "rate-limited":
        pass   # back off — see Limits
)

If the connection is mid-reconnect, events are queued automatically and flushed the moment the session resumes (up to 128 queued messages; oldest are dropped beyond that). If you are fully offline (start() never succeeded, or after stop()), the event is dropped with an editor warning instead.

Example: chat

# --- sending ---
func _on_chat_submitted(text: String) -> void:
    if text.strip_edges().is_empty():
        return
    _append_line(SpawnWeaver.player.display_name(), text)   # your own line, locally
    SpawnWeaver.send_event("chat", {"text": text})
    _input.clear()

# --- receiving ---
func _ready() -> void:
    SpawnWeaver.event_received.connect(_on_event)

func _on_event(event_name: String, data: Dictionary, sender: SWPlayer) -> void:
    if event_name == "chat":
        _append_line(sender.display_name(), str(data.get("text", "")))

func _append_line(who: String, text: String) -> void:
    _log.append_text("[b]%s:[/b] %s\n" % [who, text])

Chat is a perfect event: transient (late joiners don't need old lines), sender-known, and low-rate. If you do want history for late joiners, keep the last N lines in host-written room state instead.

Events vs. state

Question Events State
Does a late joiner need it? No — events are never replayed Yes — the snapshot arrives on join
Is it a moment or a value? A moment ("fired", "emoted") A value (position, hp, phase)
Delivery Relayed once to current members Kept on the server, changes broadcast
Sender receives it? No Yes (the ack copy)

Rule of thumb: transient → event, persistent → state. A door opening is an event if it's cosmetic; it's state ({"door_3": "open"}) if players joining later must see the door open. When in doubt, make it state — it survives reconnects for free.

Rate and size budget

Events share the connection's message budget: 20 messages/s sustained (burst 40) and 16 KB per message. Exceeding them raises rate-limited (retryable — back off briefly) or payload-too-large on the error signal. Don't stream continuous values through events — that's what SpawnSync and its dirty-check are for.

Common mistakes

Mistake What happens Fix
Waiting for your own event to render the effect Nothing happens — sender is excluded Apply the local effect when sending
Sending movement every frame via events rate-limited, jittery remotes Use SpawnSync / entities for continuous values
Sending before joining a room error signal with not-in-room Events need a current room
Using events for "current game phase" Late joiners are lost Put it in host-written room state
Huge payloads (base64 images, full inventories) payload-too-large Keep events small; store big data in storage

Next