webstation-broker
DeveloperPython reference

session

Single-session state and room fanout.

Single-session state and room fanout.

The container hosts exactly one play session at a time, so session and room state are module globals.

attributelog
= logging.getLogger(__name__)
attributeSESSIONOptional[dict[str, Any]]
= None

The active play session, or None.

Shape:

\{
  "id", "active", "created_at",
  "user": \{...\}, "emulator": "pcsx2", "rom": \{...\}, "rom_file": str,
  "save": \{...\} | None, "callback": \{...\} | None, "multiplayer": bool,
  "controller_token",
  "viewers": [\{"token","slot","mk_control","username","permission"\}...],
  "controller_slot", "mk_owner_token", "designated_speaker",
  "save_baseline": float, "emulator_obj": Emulator,
\}
attributeLAST_EXITOptional[dict[str, Any]]
= None

What is left of the session that just exited: {"id", "rom", "emulator_obj"}.

RomM files the exit state in its library after the teardown has answered, so the emulator that captured it has to outlive the session for the read routes to find the file. Dropped at the next activate.

attributeROOMdict[str, Any]
= {'controller': None, 'viewers': {}, 'cooldowns': {}}

Live websocket connections for the room.

controller is the controller's connection info dict (or None while offline), viewers maps each online viewer's token to its connection info dict, and cooldowns tracks per-token rate limits.

func_session_id(raw) -> str

Reduce a caller-supplied id to what is safe in the export filename.

The exit archive is named after the session, and the export routes reject anything with path structure in it, so an id carrying a slash or a dot would produce an archive the parent could never fetch back.

paramrawobject

The session_id from the activate payload, of any type or None.

Returns

str

raw stringified and stripped to [A-Za-z0-9_-], at most 64

funcnew_session(payload, emulator_obj, rom_file) -> dict[str, Any]

Replace the module-level session with a fresh one built from the activate payload.

The controller starts with gamepad 1 and mouse/keyboard control; the viewer list starts empty and the save baseline is the moment of creation.

parampayloaddict[str, Any]

The activate request body; emulator is required, the rest (session_id, user, rom, save, callback, multiplayer) is optional.

paramemulator_objEmulator

The emulator instance driving this session.

paramrom_filestr

The resolved path of the ROM file being played.

Returns

dict

The new session dict, which is also stored in SESSION.

funcretire_session() -> None

End the session, keeping what the state routes still have to answer with.

Exit captures a state and then tears the session down, but RomM only asks for that state once the teardown has replied, so clearing outright is what made every post-exit read a 409. Only the emulator is kept, and only the read routes consult it: nothing here can be played, written to or resumed.

Returns

None
funcfind_viewer(token) -> Optional[dict[str, Any]]

Look up a viewer entry in the active session by its token.

paramtokenstr

The viewer's streaming token.

Returns

typing.Optional

The viewer dict, or None when there is no session or no viewer holds

funcadd_viewer(permission, user=None) -> dict[str, Any]

Mint a viewer token and add the viewer to the active session.

A re-join by the same user (matched by id, else username) replaces the old entry and invalidates its token.

parampermissionstr

The viewer's permission level, e.g. readonly.

paramuserOptional[dict[str, Any]]
= None

The joining user's details (id, display_name, username), or None for an anonymous viewer who gets a generated username.

Returns

dict

The new viewer dict: `{"token", "user_id", "slot", "mk_control",

funcbroadcast_to_room(payload) -> None

Send a JSON message to every connected room member.

Sends run concurrently; a failure on one socket is logged and does not stop delivery to the others.

parampayloaddict[str, Any]

The JSON-serializable message, normally carrying a type key.

Returns

None
funcbroadcast_binary_to_room(payload, sender_ws) -> None

Relay a binary media frame to every connected room member except its sender.

parampayloadbytes

The raw frame in the room's binary wire format.

paramsender_wsWebSocket

The websocket the frame arrived on; it is skipped.

Returns

None
funcbroadcast_state() -> None

Broadcast a state_update describing every room member to the room.

The controller is listed first, then each viewer with its online status, mouse/keyboard ownership and, while connected, its public id. Does nothing when there is no session.

Returns

None
funchandle_assign_slot(viewer_token, slot) -> None

Assign a gamepad slot to a room member, or take theirs away.

A slot can only be held by one member, so whoever held it before is unassigned first. The new token map is pushed to selkies and a gamepad_change notification is broadcast for every change made, followed by a state update. Unknown tokens are logged and ignored.

paramviewer_tokenstr

The token of the member to change; the controller's own token targets the controller.

paramslotOptional[int]

The gamepad slot to assign, or None to unassign.

Returns

None
funchandle_assign_mk(target_token) -> None

Hand mouse and keyboard control to a room member.

The controller's own token and None both mean control returns to the controller. A no-op when the target already holds it; otherwise the token map is pushed to selkies and an mk_change notification and a state update are broadcast.

paramtarget_tokenOptional[str]

The token of the viewer to receive control, or None (or the controller's token) to give it back to the controller.

Returns

None
funcnotify_session_ended() -> None

Tell the room the session has ended and forget every connection.

Returns

None