CI / test (push) Successful in 58s
The client takes a room's owner from the instance ID's first part; with 2100000000 there, the owner couldn't decorate their hosted room (putting furniture on went to their home room instead). Co-Authored-By: Claude Opus 5.5 <[email protected]>
84 lines
6.0 KiB
Markdown
84 lines
6.0 KiB
Markdown
# T5DE-hosted rooms (phase 1)
|
|
|
|
A hosted room lives on the hub (design.md, Rooms): T5DE users find it in the client's room list, join it, sit and
|
|
chat, and its owner decorates it. The client treats it as an ordinary IMVU room: T5DE's proxy answers the client's
|
|
room calls and runs the room's IMQ queue against the hub (imq-rooms.md has what the client expects). IMVU never
|
|
hears of hosted rooms.
|
|
|
|
Phase 1: rooms created on the website; joining, seats, chat (`messages` and `control` mounts), the scene (the
|
|
owner's furniture), occupancy in the room list. Later: moderators, bans and boots, invitations, private rooms.
|
|
|
|
## IDs
|
|
|
|
IDs IMVU won't use, so the proxy can tell hosted rooms apart:
|
|
|
|
- A room has a number `n` (1, 2, ...), its **instance ID** is `<owner's IMVU ID>-<2100000000 + n>` and its
|
|
**chat ID** is `2100000000 + n`. The client takes a room's owner from the instance ID's first part (so the owner
|
|
can decorate it); IMVU's own room numbers are small. (IMVU's chat IDs were about 1,390,000,000 in 2026-10, and XML-RPC integers end at
|
|
2,147,483,647; IMVU customer IDs, the first part of its instance IDs, are far below 2,100,000,000.)
|
|
- The room shell is an IMVU product (`roomPid`), chosen when the room is made (any room product; the client loads
|
|
it like any room).
|
|
|
|
## REST (bearer or cookie session)
|
|
|
|
| Route | |
|
|
| --- | --- |
|
|
| `POST /api/v1/hosted-rooms` | `{name, description, roomPid, rating, maxUsers, imvuId}`: make a room. `imvuId` is one of the person's linked IMVU accounts, shown as the owner. `rating` `general` or `adult` (adult needs an adult person). `maxUsers` 2-30, default 10. Name 1-64 characters, description up to 512. A person may own up to 10 open rooms. |
|
|
| `GET /api/v1/hosted-rooms` | (also signed out) `{rooms: [room]}`, open rooms, newest first; adult rooms only for adults. `?mine=1`: the person's own. |
|
|
| `GET /api/v1/hosted-rooms/:n` | (also signed out) `{room, scene: {contents, hash}}`; 404 `not_found` (or adult, for others), 410 `closed`. |
|
|
| `PATCH /api/v1/hosted-rooms/:n` | owner: `{name?, description?, rating?, maxUsers?, roomPid?}`. |
|
|
| `PUT /api/v1/hosted-rooms/:n/scene` | owner: `{contents, roomPid?}`, from the client's `test.setRoomState`. |
|
|
| `DELETE /api/v1/hosted-rooms/:n` | owner (or moderator console): closes it; everyone in it is told (`room.closed`). |
|
|
|
|
A room: `{n, instanceId, chatId, name, description, roomPid, rating, maxUsers, ownerImvuId, ownerName, occupancy,
|
|
createdAt}` (`ownerName`: the owner's IMVU name as linked; `occupancy`: people in it now). A room whose owner account
|
|
is no longer linked to its owner (unlinked, or moved to another person), or whose owner is banned, is hidden (not
|
|
listed, `not_found`) until it's linked again. Closed rooms stay closed; their numbers aren't reused.
|
|
|
|
The scene's `contents` is the client's `room_contents`: a list of `[furniture instance ID (integer), product ID
|
|
(integer or a hub product ID string), placement (string)]`, up to 500 items, placement up to 200 characters. `hash`
|
|
is a hash of the stored scene (the client caches scenes by it): sha256, hex, of `[roomPid, contents]` as JSON.
|
|
|
|
## Realtime (the proxy's WebSocket)
|
|
|
|
From the proxy (the session's IMVU account, sent with `session`, must be linked to the person):
|
|
|
|
- `{"type": "room.join", "room": n}`: join. Answer `{"type": "room.joined", "room": n, "chat": chatId,
|
|
"participants": [{"imvu_id", "seat"}], "sequence": s}` (the room's latest message number), or
|
|
`{"type": "room.error", "room": n, "reason": "full" | "closed" | "adult" | "not_found" | "not_linked" | "too_many",
|
|
"message": "..."}` (`too_many`: the session is in 5 hosted rooms already). Joining again (a reconnect) answers the
|
|
same. A newcomer's seat is 0 until it sends `room.seat`. `maxUsers` counts IMVU accounts, so the same account
|
|
joining from a second session isn't refused as full.
|
|
- `{"type": "room.leave", "room": n}`.
|
|
- `{"type": "room.seat", "room": n, "seat": k}`: the person sat down (`*seat k`); 0-99.
|
|
- `{"type": "room.send", "room": n, "mount": "messages" | "control", "message": "..."}` (up to 64 KB): to everyone
|
|
else in the room. For `messages` the hub checks the JSON's `userId` is the sender's IMVU account and drops it
|
|
otherwise (silently). Each session may send bursts of 20, then 10 a second; more is dropped. Malformed messages
|
|
(bad room, seat, mount, a message over 64 KB, sending to a room it isn't in) get `{"type": "error", "message"}`.
|
|
|
|
From the hub:
|
|
|
|
- `{"type": "room.message", "room": n, "from": imvuId, "mount": "...", "message": "...", "sequence": s}`: numbered
|
|
per room, from 1.
|
|
- `{"type": "room.participants", "room": n, "participants": [{"imvu_id", "seat"}]}`: whenever someone joins, leaves
|
|
or sits (not to the one joining: `room.joined` has the list).
|
|
- `{"type": "room.closed", "room": n}`: the room closed, or this session is out of it: its IMVU account was unlinked
|
|
or it sent `session` with another account, or the room became adult and its account isn't. (A lower `maxUsers`
|
|
sends no one out; it only stops newcomers.)
|
|
|
|
A session closing leaves its rooms. The hub keeps the participants and message numbers in memory; messages aren't
|
|
stored (design.md: chat retention).
|
|
|
|
## The proxy's part
|
|
|
|
- **XML-RPC** for hosted IDs, answered by the proxy (never sent to IMVU): `test.getRoomCardInfo`,
|
|
`chat.getOrMakeChat` (activity `publicroom-<owner>-<2100000000+n>`: joins, answers `chatId`, `seat`, `lastMessageId`,
|
|
`participantUrl`), `test.getRoomState` (the scene, with community products as local IDs), `chat.getParticipants`,
|
|
`chat.terminateChat`, `test.setRoomState` (owners: saved with `PUT .../scene`).
|
|
- **IMQ**: subscribing to `/chat/<chatId>` of a hosted room is answered by the proxy (result, `JoinedQueue`,
|
|
`CreateMount`s for `control`, `messages`, `participants`); `SendMessage` on it goes to the hub as `room.send`, and
|
|
`room.message` comes back as `G2cSendMessage`. `inv:/room/room-<owner>-<2100000000+n>` is answered with a result and a
|
|
`node` mount, and nothing more.
|
|
- **Room list**: hosted rooms are added to `rooms_list_paginated.php`'s answer, and `room_occupancy/<owner>-<2100000000+n>`
|
|
queues are answered from `room.participants`.
|