overlayed.sand.readyForGameEvents() must be called before any events will be emitted.Retained vs transient
Retained events are held by the module, which replays the latest copy to a client that connects late — so you never miss one by subscribing after the fact. Transient events are only seen live. Some retained events are stored per subject rather than one overall. Those have a key: a later event with the same key replaces the earlier one.party_member_joined is keyed on account_id,
player_joined on platform_id, extraction_added on extraction_id, and the trampler events on
trampler_id.
Retention has two levels. Menu-level events are held for the life of the process. Match-level
events are all dropped at match_ended, so a new expedition starts from a clean slate.
module_loaded, module_unloaded, and unsupported_game_version are emitted for Sand Raiders too,
but they arrive on the universal module — see Universal.
Positions are world space with y as height, ready to use as given.
The
type.ts block under each event below describes that event’s content object. The event
you receive wraps it: { game, type, creation_time, content }. So a handler reads
event.content.state and event.creation_time.logged_in
Retained at menu level. Emitted once and held for the life of the process.account_id is the same identity as party_member_joined.account_id. platform_id is a SteamID64
on the Steam build.
game_state_changed
Retained at menu level, replaced on each change. Expedition status, ormenu when there is no expedition.
party_changed
Retained at menu level, replaced on each change and released when the expedition ends. There is no separate disband event. The party survives into a match, so this is not menu-only in the sense of being dropped atmatch_started.
party_member_joined
Retained at menu level, keyed onaccount_id, released by party_member_left. One per party member,
including the local player. Re-emitted with the same account_id when a member’s details change,
which replaces the retained copy.
Carries the same identity fields as logged_in, plus the member’s selected character and whether
they lead the party.
party_member_left
Not retained. Releases the retainedparty_member_joined with the same account_id.
When the whole party disbands, one of these is emitted per member before party_changed is released.
inventory_changed
Retained at menu level, replaced on every change. A full storage snapshot. Currencies are ordinary items — they appear initems under their own definition and occupy slots.
slots_used counts one slot per stack whatever its size, and can exceed slots_total. Stacks
sharing a definition are summed into a single items entry, so the entry count can be lower than
slots_used.
The snapshot can stay unchanged for a while after a match before the next update arrives. The last
snapshot is held rather than being reported as empty.
inventory_item_changed
Not retained. One per definition whose total moved, emitted alongside the replacinginventory_changed.
A definition whose last row is gone reports amount 0 with a negative delta. Currency movement
arrives under its item definition — crowns as item_coinCrown — never under the aggregate name.
tech_tree_changed
Retained at menu level, replaced on every change. The full research tree state, never partial.unlocked counts the nodes with is_unlocked set, total is the node count in the tree, and
nodes is sorted by id. tier is depth in the tree, 0-based.
tech_tree_item_unlocked
Not retained. One per node that flipped from locked to unlocked, emitted alongside the replacingtech_tree_changed.
Availability changes are not broadcast — a node becoming researchable is only visible in the
retained tech_tree_changed state.
walker_changed
Retained at menu level, replaced on each change. The trampler the account has built. It is not updated during a match, but the retained copy carries across it, so the walker taken into a match is still readable while the match runs.walker_id is the same id trampler_acquired.master_server_id reports in a match, which is how the
two are matched up.
blueprint_file is a saved blueprint’s file name, without its extension. It is empty when the
walker was not built from a saved blueprint — the stock expedition walker, for instance — and is
unrelated to blueprint_unique_id.first_name_index and second_name_index are opaque name indices, meaningful only as a pair. 0,
0 means the walker is unnamed.
match_started
Retained at match level untilmatch_ended.
world_bounds is the playable extent on the xz plane — use it to map world coordinates onto a
rendered map. landmarks are the named islands and forts, the places drawn on the in-game map.
server_start_time is Unix milliseconds and marks when the match began, not when you joined it. In
Voyage it can be days in the past.
match_ended
Not retained. The game state was left — every retained match-level event is dropped with it.world_time_changed
Retained at match level, replaced on each change. Fires when the in-game hour rolls over.player_joined
Retained at match level, keyed onplatform_id. One per player on the in-game player list, which
keeps players after they disconnect. Re-emitted to replace the retained copy if their details change.
There is no counterpart leave event — a player who drops stays on the list.
player_id is the id trampler_acquired.creator_account_id also reports, so the two can be matched
directly. It is not logged_in.account_id and will never equal it; match on platform_id to tie
a player to the logged-in account.
extraction_added
Retained at match level, keyed onextraction_id, released by extraction_removed. In Storm Dive
these arrive through the match rather than all at once, so do not expect the full set at
match_started.
radius is the extraction zone radius in world units.
extraction_removed
Not retained. Releases the retainedextraction_added with the same extraction_id.
storm_changed
Retained at match level, replaced on each change. Storm Dive only.delay is the pause before the circle starts closing; moving is the close itself.
storm_changed fires on stage and state transitions only, so the live circle is not streamed to you.
To draw a closing circle, interpolate from center/radius toward
next_center/next_radius over the time remaining until end_time. At the next event, center and
radius equal the previous next_center and next_radius.
trampler_acquired
Retained at match level, keyed ontrampler_id, released by trampler_lost. One per trampler the
account owns — the one you started the match with, and any captured later.
This event is identity only. The trampler’s position arrives on trampler_changed, emitted at the
same moment.
trampler_id is scoped to the match and means nothing outside it. master_server_id is stable
across matches, and is 0 when the trampler has no such id. When it is non-zero it matches
walker_changed.walker_id, which is how you tell that the trampler you are flying is the walker you
built:
blueprint is the blueprint name — EXPEDITION_WALKER for the stock walker.
trampler_changed
Retained at match level, keyed ontrampler_id, released by trampler_lost.
Emitted on acquisition and then about every 10 seconds for as long as the trampler is owned,
whether or not it moved. It is a heartbeat, not a movement feed — do not treat a new event as proof
of motion, or the gap between two as a movement duration.
yaw is degrees about the up axis. speed is the magnitude of velocity, supplied so you do not
have to compute it.
trampler_lost
Not retained. Releases bothtrampler_acquired and trampler_changed for the same trampler_id.
This arrives promptly rather than waiting for the next trampler_changed, so a trampler can be
treated as gone as soon as you see it.

