From a229f8c5d001806494a500befdfc9a19e92698e1 Mon Sep 17 00:00:00 2001 From: dhkatz Date: Thu, 29 Oct 2026 02:20:20 -0700 Subject: [PATCH] Room list, occupancy, scenes and other sessions Co-Authored-By: Claude Opus 5.5 --- docs/imq-rooms.md | 57 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/docs/imq-rooms.md b/docs/imq-rooms.md index e46190a..c8919e8 100644 --- a/docs/imq-rooms.md +++ b/docs/imq-rooms.md @@ -27,8 +27,10 @@ In order, as captured: 3. `test.getRoomState({room_instance_id})`: the scene: `room_hash`, and `room_state.room_contents`, a list of `[furniture instance ID, product ID, placement]`, where placement is a string of numbers (position, rotation, scale, parent slot...). The client caches it by hash. -4. IMQ: subscribe to `/chat/` (with property `listen_to_self: false`), then `inv:/room/room-` (contents not seen yet). +4. IMQ: subscribe to `/chat/` (with property `listen_to_self: false`). The gateway answers with + `JoinedQueue` and creates three mounts: `control` and `messages` (message type) and `participants` (state type). + Then subscribe to `inv:/room/room-`, which has one `node` mount (message type); nothing arrived + on it in a session without room edits. 5. Chat commands sent on joining (below): `*msg SeatAssignment ...`, `*seat `, `*resume ` (or `*accept `). 6. `chat.getParticipants({chatId, userId})`, then again about every 15 seconds: `[{userId, seat, ...}]`. People @@ -39,8 +41,19 @@ Leaving: `chat.terminateChat(userId, chatId)`. Booting: `chat.bootOutOfChat({use `chat.attemptInvite`, `acceptInvite`, `declineInvite`, `leaveInviteAwayNote`. Also seen: `test.getBuddyState`, `test.updateAvatarDownloadSize`. -The room list subscribes to `room_occupancy/` for each room shown (mount `occupants`), for live -occupancy counts. +## The room list + +- Rooms come from `client-dynamic.imvu.com/api/rooms/rooms_list_paginated.php` (signed in; `recent=1` for recently + visited, search parameters otherwise). The response is column-oriented JSON: one array per field + (`customers_room_id`, `name`, `customers_name`, `num_participants`, `max_users`, `participants`, `image_url`, + `description`, `rating`, `language`, `is_ap`, `is_av`, `is_vip`, ...), plus `number_of_rooms`, `rooms_per_page` + and welcome-room fields. The proxy can add hosted rooms by appending to every array. +- For live counts, the list subscribes to `room_occupancy/` for each room shown (the client + re-subscribes them as the list refreshes). Each is a state mount `occupants` whose properties are the people in + the room, keyed by user ID; the gateway sends `StateChange` from user `admin` when someone joins (value set) or + leaves (value empty). +- Joining by link (`imvu:room/...`) calls `test.getJoinPublicRoomInfo({publicroom_url})`, which returns the room + instance ID. ## IMQ @@ -69,6 +82,8 @@ reconnect. On `/chat/`: +- The gateway sends a sender's own messages back to them too (with the queue's next sequence number); the client + ignores its own because it subscribed with `listen_to_self: false`. - Mount `messages`: JSON `{"userId", "chatId", "message", "to"}` (`to` 0 for everyone). The client takes the sender from the gateway's `user_id` (the last path segment) and flags a mismatch with `userId` as forged. - Mount `control`: JSON with a `command`, such as `PlayAction` and `PlayCoopAction` (actions on avatars and @@ -88,7 +103,18 @@ handled: - Other: `*imvu:gift`, `*imvu:showGift`, `*imvu:purchase`, `*imvu:try`, `*imvu:goto`, `*imvu:recommend`, `*imvu:isPureUser`, `*imvu:txnBoot`, `*snap`, `*hiResSnap`, `*uploadSnap`. -Saving a scene: the owner's client calls `test.setRoomState({room_instance_id, room_contents, ...})`. +## The scene + +- Encoding (`*imvu:setRoomState`): the letter `c`, then base64 (with line breaks) of zlib-compressed JSON: + `{"room_state": {"room_info": {"customers_id", "room_instance_id", "room_pid", "revision_id"}, "room_contents": + [[furniture instance ID, product ID, placement], ...]}}`. `room_pid` is the room shell's product. Placement is a + string of numbers: a format number, then position, a parent reference (which slot of which furniture item it sits + on), rotation and scale (the exact layout still to be worked out). +- The owner's client broadcasts the **whole scene** with `*imvu:setRoomState` when entering the room and after a + change, and saves it with `test.setRoomState({room_instance_id, room_contents, ...})`; others only accept it from + the owner. `getRoomState` returns the same contents with a `room_hash`. +- A hosted room's scene can carry community products the same way: they're product IDs like any other (the proxy's + local IDs on each machine, global IDs on the wire). ## What the hub must do for a hosted room @@ -104,15 +130,24 @@ Through the proxy, which sees both channels: client's op IDs) and never forwards them. - **IDs**: hosted rooms need room instance IDs and chat IDs IMVU will never use, so the proxy can tell them apart (to be chosen: a reserved range, as for products). -- **Room list**: hosted rooms must appear in the client's room list, which comes from a web API (not looked at yet). +- **Room list**: add hosted rooms to `rooms_list_paginated.php` responses, answer `room_occupancy/` queues for them, + and answer `getJoinPublicRoomInfo` for their links. +- **Scenes**: store the scene from the owner's `setRoomState`, serve it from `getRoomState`, and relay the owner's + `*imvu:setRoomState` like any message. The hub keeps the authoritative participant list, seats, scene and the room's moderation (owner, moderators, bans), and checks every message's sender itself. +## Other sessions (from the code, not driven) + +- **Private rooms**: the same `getOrMakeChat` with `private: true`. +- **Chat Now** (random one-to-one) uses activity `Chat now-`. It pairs strangers, so it wasn't + driven with the test account. +- **Invitations**: `chat.attemptInvite`, `acceptInvite`, `declineInvite`, `leaveInviteAwayNote`, with + `getOrMakeChat` returning `waiting` until the other person answers. Needs a second account to capture. + ## Not yet looked at -- What's sent on `inv:/room/room-`. -- The room list's API, and joining by link (`imvu:room/...`, which calls `getJoinPublicRoomInfo`). -- Moving furniture in one's own room (a capture in My Room). -- Private rooms, invitations and "Chat Now" (one-to-one). -- What `G2cCreateMount` and state mounts are used for in rooms. +- Messages on `inv:/room/room-` (likely room edits made outside the client). +- The placement string's exact layout. +- The furniture panel: dragging furniture in (a browser page; synthesized mouse drags don't trigger it).