Rooms made on the website (hosted_room, migration 0005), the REST routes in docs/hosted-rooms.md, and the realtime room.* protocol: joining with the capacity, adult and linked-account rules, seats, numbered messages with forged senders dropped and a rate limit, closing. The Rooms page lists them, with a form to make one and the owner's edit and close. Co-Authored-By: Claude Opus 5.5 <[email protected]>
5.9 KiB
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 is2100000000-<n>and its chat ID is2100000000 + n. (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 sendsroom.seat.maxUserscounts 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. Formessagesthe hub checks the JSON'suserIdis 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.joinedhas the list).{"type": "room.closed", "room": n}: the room closed, or this session is out of it: its IMVU account was unlinked or it sentsessionwith another account, or the room became adult and its account isn't. (A lowermaxUserssends 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(activitypublicroom-2100000000-<n>: joins, answerschatId,seat,lastMessageId,participantUrl),test.getRoomState(the scene, with community products as local IDs),chat.getParticipants,chat.terminateChat,test.setRoomState(owners: saved withPUT .../scene). - IMQ: subscribing to
/chat/<chatId>of a hosted room is answered by the proxy (result,JoinedQueue,CreateMounts forcontrol,messages,participants);SendMessageon it goes to the hub asroom.send, androom.messagecomes back asG2cSendMessage.inv:/room/room-2100000000-<n>is answered with a result and anodemount, and nothing more. - Room list: hosted rooms are added to
rooms_list_paginated.php's answer, androom_occupancy/2100000000-<n>queues are answered fromroom.participants.