Starship Horizons
Log In
Knowledge BasePackets And Topics

Packets And Topics

State lives on the server. The client is told about it through the WebSocket, and what arrives is distributed inside the page over a broadcast bus. These are two separate mechanisms and confusing them is the source of a lot of trouble.

Packets: what the server sends

A packet is a named message from the server — MAP, VESSEL-WAYPOINTS, ALERT, DAMAGE-TEAMS, ACTIVE-CONSOLES, and dozens more. Packets are upper‑case and hyphenated by convention.

The server does not send everything to everyone. A console asks for the packets it needs:

this.AcceptPacket("DAMAGE-TEAMS");

Two things follow:

  • Accepting is refcounted. Several widgets can want the same packet. The socket tracks owners and only sends a rejection to the server when the last one releases it. This is why widgets must claim through this.AcceptPacket rather than the global — see Widgets.
  • Accepting delivers immediately. The server answers a new claim by sending the current value straight away, rather than waiting for the next change. This is convenient and it is also a race: a widget that mounts while the page is loading something over REST can have the socket's answer arrive first and win.

A screen can also declare packets it always wants, in its screen registration, which is how admin stations get message and debug traffic from the outset.

Topics: what the page distributes

When a packet arrives, the client updates its state and then broadcasts one or more topics — lower‑case, hyphenated. Widgets subscribe to topics, not to packets:

this.Subscribe("vessel-shields", (m) => this.Render());

The mapping is not one to one. One packet can raise several topics — the MAP packet updates the current map and raises map-info, and related packets raise location-current and planetary-system-detail. And one topic can be raised by several packets, or by page code with no packet involved at all.

This is exactly why releasing a topic subscription says nothing about whether a packet is still needed, and why the two are tracked separately.

The topics you will actually use

There are over a hundred. These are the ones nearly every widget wants:

TopicRaised when
readyThe page is up and the socket is connected. Claim packets here, not in the constructor, if you need the socket.
resetThe session reset. Re‑render, and remember this is not startup.
resizeThe console changed size.
vesselThe bound vessel's general state changed.
vessel-idThis console has been given a vessel.
vessel-initializedThe vessel is fully populated. Prefer this to vessel-id — it is a superset, and a widget that renders on vessel-id often finds half the data missing.
vessel-componentsThe component set changed.
vessel-shields, vessel-damage, vessel-alertCombat state.
contacts, contact-updated, contact-removedThe sensor picture.
target-flight, target-tactical, target-scienceThe three separate target selections.
map-info, location-currentWhere the ship is.
mission, game-start, sessionSession lifecycle.

Traps worth knowing in advance

Contact topics arrive in storms. In a busy mission contact-updated can fire many times per second. A widget that re‑renders its whole list on every one will dominate the frame. Coalesce, or render on a timer and let the topic only mark you dirty.

The session state, not the mission state, is authoritative for whether a mission is actually running. A mission can be loaded without being started, and code that checks the wrong one acts too early.

Editor pages must guard against live packets. An editor holds one specific record it loaded over REST. A packet describing whatever the server currently has loaded is a different thing, and letting it land replaces the record being edited. The map editor sets a flag for exactly this reason.

Sending state the other way

See Server Commands.

Last updated 5 September 2026