# 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 `-<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--<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/` 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--<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/-<2100000000+n>` queues are answered from `room.participants`.