Hosted rooms' instance IDs name their owner: <owner>-<2100000000+n>
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]>
This commit is contained in:
dhkatzandClaude Opus 5.5 committed 2026-10-30 05:18:19 -07:00
1 parent f65f36c1ea
commit 43183ec7ee
7 files changed
+17 -13

No files matched your search

+1 -1
View File
@@ -227,7 +227,7 @@ export function createApp({ db, auth, imvu, realtime: rt, age: ageService, rooms
const hostedView = ({ room: r, ownerName }: { room: typeof schema.hostedRoom.$inferSelect; ownerName: string }) => ({
n: r.n,
instanceId: instanceId(r.n),
instanceId: instanceId(r.ownerImvuId, r.n),
chatId: chatId(r.n),
name: r.name,
description: r.description,
+2 -1
View File
@@ -32,7 +32,8 @@ export const decorationHash = (contents: DecorationItem[]) =>
function ownerOf(instance: string): number {
if (!decorationInstance.test(instance)) throw new DecorationError(400, "invalid", "That isn't an IMVU room.");
const owner = Number(instance.slice(0, instance.indexOf("-")));
if (owner >= hostedBase) throw new DecorationError(400, "hosted_room", "Hosted rooms keep their own scene.");
if (Number(instance.slice(instance.indexOf("-") + 1)) >= hostedBase)
throw new DecorationError(400, "hosted_room", "Hosted rooms keep their own scene.");
return owner;
}
+3 -1
View File
@@ -31,7 +31,9 @@ export const maxPlacement = 200;
export const sceneHash = (roomPid: number, contents: SceneItem[]) =>
createHash("sha256").update(JSON.stringify([roomPid, contents])).digest("hex");
export const instanceId = (n: number) => `${hostedBase}-${n}`;
// "<owner's IMVU ID>-<hostedBase + n>": the client takes a room's owner from the first part (so the owner can
// decorate it), and IMVU's own room numbers are far below hostedBase.
export const instanceId = (owner: number, n: number) => `${owner}-${hostedBase + n}`;
export const chatId = (n: number) => hostedBase + n;
// eslint-disable-next-line no-control-regex
+2 -2
View File
@@ -119,7 +119,7 @@ test("only the room's owner decorates it; contents are checked", async () => {
const put = (contents: unknown, instance = room) => call(owner, "PUT", `/api/v1/decorations/${instance}`, { contents });
for (const bad of ["502", "0502-3", "502-0", "502-3-4", "x-1", "1234567890123-1"])
expect((await put([item], bad)).status).toBe(400);
expect((await put([item], "2100000000-5")).json.error).toBe("hosted_room");
expect((await put([item], "12345-2100000005")).json.error).toBe("hosted_room");
expect((await put([[7, "AAAAAAAAAA", ""]])).status).toBe(400);
expect((await put([[7, 12345, ""]])).status).toBe(400);
expect((await put([[-1, "aaaaaaaaaa", ""]])).status).toBe(400);
@@ -157,7 +157,7 @@ test("viewers see the products they may, with a hash of what they see", async ()
// Unknown rooms have nothing.
expect((await call(undefined, "GET", "/api/v1/decorations/601-9")).json).toEqual({ instance: "601-9", contents: [], hash: hash([]) });
expect((await call(undefined, "GET", "/api/v1/decorations/601-x")).status).toBe(400);
expect((await call(undefined, "GET", "/api/v1/decorations/2100000000-1")).status).toBe(400);
expect((await call(undefined, "GET", "/api/v1/decorations/12345-2100000001")).status).toBe(400);
const put = await call(owner, "PUT", `/api/v1/decorations/${room}`, { contents: [g, a, r] });
expect(put.status).toBe(200);
+1 -1
View File
@@ -133,7 +133,7 @@ test("rooms are made, listed, changed and closed by their owners", async () => {
expect(made.status).toBe(201);
const n = made.json.room.n;
expect(made.json.room).toMatchObject({
instanceId: `2100000000-${n}`,
instanceId: `302-${2_100_000_000 + n}`,
chatId: 2_100_000_000 + n,
name: "The Lounge",
description: "A quiet place.",