diff --git a/apps/api/src/api.app.ts b/apps/api/src/api.app.ts index 1efef41..701d047 100644 --- a/apps/api/src/api.app.ts +++ b/apps/api/src/api.app.ts @@ -12,6 +12,7 @@ import storefrontGiftDrop300 from '../static/storefronts-v3-giftdropstore-300.js import { DEFAULT_AVATAR_ITEMS } from './default-avatar-items' import { defaultSettings } from './default-settings' import { validateAndGetAccountId } from './jwt' +import { getRoomById, getRoomByName, getRoomsByCreator, getRoomsByIds } from './rooms-db' import type { Context } from 'hono' import type { App } from './context' @@ -69,64 +70,6 @@ function queryIds(c: Context): number[] { ) } -/** Unity scene id for the dorm (matches the match worker's instance location). */ -const DORM_SCENE_ID = '76d98498-60a1-430c-ab76-b54a29b7a163' - -/** - * Full room payload (PascalCase), mirroring the C#'s `BuildRoomResponse` / - * `RoomserverRoomsBulk`. With no Rooms DB, room 1 is the dorm and other ids get - * a generic published room. The SubRoom carries the UnitySceneId/DataBlob the - * client needs to load the scene. - */ -function buildRoomResponse(roomId: number) { - const isDorm = roomId === 1 - return { - RoomId: roomId, - Name: isDorm ? 'DormRoom' : `Room${roomId}`, - Description: isDorm ? 'Your private room' : '', - CreatorAccountId: 1, - ImageName: 'DefaultRoomImage.jpg', - State: 0, - Accessibility: 0, - SupportsLevelVoting: false, - IsRRO: false, - IsDorm: isDorm, - CloningAllowed: false, - SupportsVRLow: true, - SupportsQuest2: true, - SupportsMobile: true, - SupportsScreens: true, - SupportsWalkVR: true, - SupportsTeleportVR: true, - SupportsJuniors: true, - MinLevel: 0, - WarningMask: 0, - CustomWarning: null, - DisableMicAutoMute: false, - DisableRoomComments: false, - EncryptVoiceChat: false, - CreatedAt: '2026-01-18T02:31:37.6171131', - Stats: { CheerCount: 0, FavoriteCount: 0, VisitorCount: 1, VisitCount: 1 }, - SubRooms: [ - { - SubRoomId: 1, - Name: '', - DataBlob: '', - IsSandbox: false, - MaxPlayers: 4, - Accessibility: 0, - UnitySceneId: isDorm ? DORM_SCENE_ID : '', - DataSavedAt: '2026-01-18T02:31:37.6171131', - }, - ], - Roles: [], - LoadScreens: [], - PromoImages: [], - PromoExternalContent: [], - Tags: [], - } -} - /** * Photon access-token response (`/roomserver/photon_access_token`). The 2023 * client calls this to get its room permissions + the instance id it's spawning @@ -524,34 +467,39 @@ const app = new Hono({ strict: false }) ) // ---- Room server ---------------------------------------------------------- + // Room data is read from the shared `rec-rooms` D1 (owned by the rooms worker). // Register specific paths before the `/:id` param route. - .get('/roomserver/rooms/bulk', (c) => { + .get('/roomserver/rooms/bulk', async (c) => { const idParam = c.req.query('id') const nameParam = c.req.query('name') if (!idParam && !nameParam) { return c.text("Either 'id' or 'name' query parameter is required", 400) } - // Synthesize a room per requested id (the client needs SubRooms to load). - // TODO: query Rooms + related tables once a DB binding exists. - const ids = (idParam ?? '') - .split(',') - .map((s) => Number.parseInt(s.trim(), 10)) - .filter((n) => !Number.isNaN(n)) - return c.json(ids.map(buildRoomResponse)) + if (idParam) { + const ids = idParam + .split(',') + .map((s) => Number.parseInt(s.trim(), 10)) + .filter((n) => !Number.isNaN(n)) + return c.json(await getRoomsByIds(c.env.DB, ids)) + } + const room = await getRoomByName(c.env.DB, nameParam ?? '') + return c.json(room ? [room] : []) }) // Photon access token + room permissions the client needs to spawn into a room. .get('/roomserver/photon_access_token', (c) => c.json(photonAccessToken())) .get('/roomserver/rooms/hot', (c) => c.json({ Results: [], TotalResults: 0 })) .get('/roomserver/roomsandplaylists/hot', (c) => c.json({ Results: [], TotalResults: 0 })) - .get('/roomserver/rooms/createdby/me', (c) => c.json([buildRoomResponse(1)])) + .get('/roomserver/rooms/createdby/me', async (c) => + c.json(await getRoomsByCreator(c.env.DB, (await authedId(c)) ?? 1)) + ) .get('/roomserver/rooms/:id/interactionby/me', (c) => c.json({ Cheered: false, Favorited: false }) ) - .get('/roomserver/rooms/:id', (c) => { + .get('/roomserver/rooms/:id', async (c) => { const roomId = Number.parseInt(c.req.param('id'), 10) if (Number.isNaN(roomId)) return c.notFound() - // No Rooms binding → synthesize the room so the client can load it. - return c.json(buildRoomResponse(roomId)) + const room = await getRoomById(c.env.DB, roomId) + return room ? c.json(room) : c.notFound() }) export default app diff --git a/apps/api/src/context.ts b/apps/api/src/context.ts index 329ac40..a9d4fc1 100644 --- a/apps/api/src/context.ts +++ b/apps/api/src/context.ts @@ -2,7 +2,9 @@ import type { HonoApp } from '@repo/hono-helpers' import type { SharedHonoEnv, SharedHonoVariables } from '@repo/hono-helpers/src/types' export type Env = SharedHonoEnv & { - // add additional Bindings here + // Shared rooms database (schema/migrations owned by the `rooms` worker). Used + // read-only here for the /roomserver/rooms/* endpoints. + DB: D1Database } /** Variables can be extended */ diff --git a/apps/api/src/rooms-db.ts b/apps/api/src/rooms-db.ts new file mode 100644 index 0000000..c5baf97 --- /dev/null +++ b/apps/api/src/rooms-db.ts @@ -0,0 +1,50 @@ +/** + * Read helpers for the shared `rec-rooms` D1 database. The schema, migrations, + * and seed are owned by the `rooms` worker (apps/rooms/src/rooms-db.ts + + * migrations); this worker binds the same database read-only for its + * `/roomserver/rooms/*` endpoints. Keep these queries in sync with the rooms + * worker's. + */ + +/** A stored room — the parsed JSON blob (full client-facing room response). */ +export type Room = Record + +interface RoomRow { + data: string +} + +const parseOne = (row: RoomRow | null): Room | null => (row ? (JSON.parse(row.data) as Room) : null) +const parseAll = (rows: RoomRow[]): Room[] => rows.map((r) => JSON.parse(r.data) as Room) + +export async function getRoomById(db: D1Database, roomId: number): Promise { + return parseOne( + await db.prepare('SELECT data FROM rooms WHERE room_id = ?1').bind(roomId).first() + ) +} + +export async function getRoomByName(db: D1Database, name: string): Promise { + return parseOne( + await db + .prepare('SELECT data FROM rooms WHERE name_lower = ?1') + .bind(name.toLowerCase()) + .first() + ) +} + +export async function getRoomsByIds(db: D1Database, ids: number[]): Promise { + if (ids.length === 0) return [] + const placeholders = ids.map((_, i) => `?${i + 1}`).join(',') + const { results } = await db + .prepare(`SELECT data FROM rooms WHERE room_id IN (${placeholders})`) + .bind(...ids) + .all() + return parseAll(results) +} + +export async function getRoomsByCreator(db: D1Database, accountId: number): Promise { + const { results } = await db + .prepare('SELECT data FROM rooms WHERE creator_account_id = ?1') + .bind(accountId) + .all() + return parseAll(results) +} diff --git a/apps/api/src/test/integration/api.test.ts b/apps/api/src/test/integration/api.test.ts index e1aee73..8ba07f1 100644 --- a/apps/api/src/test/integration/api.test.ts +++ b/apps/api/src/test/integration/api.test.ts @@ -1,12 +1,45 @@ +import { env } from 'cloudflare:test' import { exports } from 'cloudflare:workers' -import { describe, expect, test } from 'vitest' +import { beforeAll, describe, expect, test } from 'vitest' import '../../api.app' import { DEFAULT_AVATAR_ITEMS } from '../../default-avatar-items' +import type { Env } from '../../context' + +declare module 'cloudflare:test' { + interface ProvidedEnv extends Env {} +} + const ORIGIN = 'https://api.rec.djdevin.net' +// The /roomserver/rooms/* routes read from the shared rec-rooms D1. Set up the +// schema (matching the rooms worker's migration) + a couple of rooms for tests. +const TEST_ROOMS = [ + { + RoomId: 1, + Name: 'DormRoom', + IsDorm: true, + CreatorAccountId: 1, + SubRooms: [{ SubRoomId: 1, UnitySceneId: '76d98498-60a1-430c-ab76-b54a29b7a163' }], + }, + { RoomId: 2, Name: 'RecCenter', IsDorm: false, CreatorAccountId: 1, SubRooms: [{ SubRoomId: 2 }] }, +] + +beforeAll(async () => { + await env.DB.prepare( + `CREATE TABLE IF NOT EXISTS rooms ( + data TEXT NOT NULL, + room_id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.RoomId')) VIRTUAL, + name_lower TEXT GENERATED ALWAYS AS (lower(json_extract(data, '$.Name'))) VIRTUAL, + creator_account_id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.CreatorAccountId')) VIRTUAL + )` + ).run() + const insert = env.DB.prepare('INSERT OR IGNORE INTO rooms (data) VALUES (?1)') + await env.DB.batch(TEST_ROOMS.map((r) => insert.bind(JSON.stringify(r)))) +}) + // Mint a token the way the `auth` worker does, using the same dev secret, so the // api worker's validation accepts it. Kept inline to avoid a cross-package import. const DEV_SECRET = 'dev-insecure-signing-key-change-me' @@ -251,12 +284,17 @@ describe('room server', () => { expect(res.status).toBe(400) }) - test('GET /roomserver/rooms/bulk with id returns rooms with SubRooms', async () => { + test('GET /roomserver/rooms/bulk with id returns rooms from D1', async () => { const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/bulk?id=1,2`) expect(res.status).toBe(200) - const rooms = (await res.json()) as Array<{ RoomId: number; SubRooms: unknown[] }> - expect(rooms.map((r) => r.RoomId)).toEqual([1, 2]) - expect(rooms[0].SubRooms).toHaveLength(1) + const rooms = (await res.json()) as Array<{ RoomId: number; Name: string }> + expect(rooms.map((r) => r.RoomId).sort((a, b) => a - b)).toEqual([1, 2]) + }) + + test('GET /roomserver/rooms/bulk?name= resolves from D1', async () => { + const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/bulk?name=reccenter`) + const rooms = (await res.json()) as Array<{ Name: string }> + expect(rooms.map((r) => r.Name)).toEqual(['RecCenter']) }) test('GET /roomserver/photon_access_token returns permissions + instance id', async () => { @@ -277,7 +315,7 @@ describe('room server', () => { expect(await res.json()).toEqual({ Results: [], TotalResults: 0 }) }) - test('GET /roomserver/rooms/:id synthesizes a room with a SubRoom', async () => { + test('GET /roomserver/rooms/:id returns the room from D1', async () => { const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/1`) expect(res.status).toBe(200) const room = (await res.json()) as { @@ -289,6 +327,11 @@ describe('room server', () => { expect(room.SubRooms[0].UnitySceneId).toBe('76d98498-60a1-430c-ab76-b54a29b7a163') }) + test('GET /roomserver/rooms/:id 404s for an unknown room', async () => { + const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/99999`) + expect(res.status).toBe(404) + }) + test('GET /roomserver/rooms/:id/interactionby/me', async () => { const res = await exports.default.fetch(`${ORIGIN}/roomserver/rooms/5/interactionby/me`) expect(await res.json()).toEqual({ Cheered: false, Favorited: false }) diff --git a/apps/api/wrangler.jsonc b/apps/api/wrangler.jsonc index 789290c..f66ce29 100644 --- a/apps/api/wrangler.jsonc +++ b/apps/api/wrangler.jsonc @@ -10,6 +10,14 @@ "custom_domain": true } ], + // Shared rooms DB (created + migrated by the `rooms` worker; bound read-only here). + "d1_databases": [ + { + "binding": "DB", + "database_name": "rec-rooms", + "database_id": "d44083e1-5bfe-4467-aa9a-f13c5c2496d5" + } + ], "logpush": false, "upload_source_maps": true, "observability": { diff --git a/apps/rooms/migrations/0001_init.sql b/apps/rooms/migrations/0001_init.sql new file mode 100644 index 0000000..fc23f5d --- /dev/null +++ b/apps/rooms/migrations/0001_init.sql @@ -0,0 +1,17 @@ +-- Rooms stored as a JSON blob with generated (virtual) columns for querying. +-- Generated from src/rooms-db.ts (SCHEMA_DDL + DORM_ROOM) — keep in sync. + +CREATE TABLE IF NOT EXISTS rooms ( + data TEXT NOT NULL, + room_id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.RoomId')) VIRTUAL, + name TEXT GENERATED ALWAYS AS (json_extract(data, '$.Name')) VIRTUAL, + name_lower TEXT GENERATED ALWAYS AS (lower(json_extract(data, '$.Name'))) VIRTUAL, + creator_account_id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.CreatorAccountId')) VIRTUAL, + is_dorm INTEGER GENERATED ALWAYS AS (json_extract(data, '$.IsDorm')) VIRTUAL + ); +CREATE UNIQUE INDEX IF NOT EXISTS idx_rooms_room_id ON rooms (room_id); +CREATE INDEX IF NOT EXISTS idx_rooms_name_lower ON rooms (name_lower); +CREATE INDEX IF NOT EXISTS idx_rooms_creator ON rooms (creator_account_id); + +-- Seed the default dorm (RoomId 1). +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":1,"Name":"DormRoom","Description":"Your private room","CreatorAccountId":1,"ImageName":"DefaultRoomImage.jpg","State":0,"Accessibility":0,"SupportsLevelVoting":false,"IsRRO":false,"IsDorm":true,"CloningAllowed":false,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsJuniors":true,"MinLevel":0,"WarningMask":0,"CustomWarning":null,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"CreatedAt":"2026-01-18T02:31:37.6171131","Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":1,"VisitCount":1},"SubRooms":[{"SubRoomId":1,"Name":"","DataBlob":"","IsSandbox":false,"MaxPlayers":4,"Accessibility":0,"UnitySceneId":"76d98498-60a1-430c-ab76-b54a29b7a163","DataSavedAt":"2026-01-18T02:31:37.6171131"}],"Roles":[],"LoadScreens":[],"PromoImages":[],"PromoExternalContent":[],"Tags":[]}'); diff --git a/apps/rooms/migrations/0002_import_rooms.sql b/apps/rooms/migrations/0002_import_rooms.sql new file mode 100644 index 0000000..bfe5de2 --- /dev/null +++ b/apps/rooms/migrations/0002_import_rooms.sql @@ -0,0 +1,49 @@ +-- Seed rooms from static/ImportRooms.json (45 rooms incl. the dorm). +-- Generated — do not edit by hand. Regenerate from ImportRooms.json. + +DELETE FROM rooms; +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":1,"Name":"DormRoom","Description":"Your private room","ImageName":"DormRoom.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.317921Z","PublishedAt":"2026-03-11T06:48:51.317921Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":true,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":4,"CloningAllowed":false,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"1","SubRooms":[{"SubRoomId":1,"RoomId":1,"CreatorAccountId":null,"UnitySceneId":"76d98498-60a1-430c-ab76-b54a29b7a163","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":4,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":2,"Name":"RecCenter","Description":"A social hub to meet and mingle with friends new and old.","ImageName":"RecCenter.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.318650Z","PublishedAt":"2026-03-11T06:48:51.318650Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":12,"CloningAllowed":false,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"2","SubRooms":[{"SubRoomId":2,"RoomId":2,"CreatorAccountId":null,"UnitySceneId":"cbad71af-0831-44d8-b8ef-69edafa841f6","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":12,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":3,"Name":"3DCharades","Description":"Take turns drawing, acting, and guessing funny phrases with your friends!","ImageName":"3DCharades.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":false,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.319060Z","PublishedAt":"2026-03-11T06:48:51.319060Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":8,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":true,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"3","SubRooms":[{"SubRoomId":3,"RoomId":3,"CreatorAccountId":null,"UnitySceneId":"a673712c-877f-4749-b69a-4a4c6310d545","Name":"InkSpaceHome","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":4,"Name":"Legacy3DCharades","Description":"Take turns drawing, acting, and guessing funny phrases with your friends!","ImageName":"Legacy3DCharades.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":false,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":false,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.319471Z","PublishedAt":"2026-03-11T06:48:51.319471Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":8,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":true,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"4","SubRooms":[{"SubRoomId":4,"RoomId":4,"CreatorAccountId":null,"UnitySceneId":"4078dfed-24bb-4db7-863f-578ba48d726b","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":5,"Name":"TheInkSpace","Description":"A cozy event venue with a stage. The entire room is made of props, so customize to your heart''s content!","ImageName":"TheInkSpace.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":false,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.319872Z","PublishedAt":"2026-03-11T06:48:51.319872Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":8,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":true,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"5","SubRooms":[{"SubRoomId":5,"RoomId":5,"CreatorAccountId":null,"UnitySceneId":"1fa06e3c-c307-4c11-a91b-1fabcddb8a96","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":8,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":6,"Name":"DiscGolfLake","Description":"A leisurely stroll through the grass. Throw your disc into the goal. Sounds easy, right?","ImageName":"DiscGolfLake.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.320270Z","PublishedAt":"2026-03-11T06:48:51.320270Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":4,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"6","SubRooms":[{"SubRoomId":6,"RoomId":6,"CreatorAccountId":null,"UnitySceneId":"f6f7256c-e438-4299-b99e-d20bef8cf7e0","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":4,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":7,"Name":"DiscGolfPropulsion","Description":"Throw your disc through hazards and around wind machines on this challenging course!","ImageName":"DiscGolfPropulsion.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.320653Z","PublishedAt":"2026-03-11T06:48:51.320653Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":4,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"7","SubRooms":[{"SubRoomId":7,"RoomId":7,"CreatorAccountId":null,"UnitySceneId":"d9378c9f-80bc-46fb-ad1e-1bed8a674f55","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":4,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":8,"Name":"Dodgeball","Description":"Throw dodgeballs to knock out your friends in this gym classic!","ImageName":"Dodgeball.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.321125Z","PublishedAt":"2026-03-11T06:48:51.321125Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":6,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"8","SubRooms":[{"SubRoomId":8,"RoomId":8,"CreatorAccountId":null,"UnitySceneId":"3d474b26-26f7-45e9-9a36-9b02847d5e6f","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":6,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":9,"Name":"Paddleball","Description":"A simple rally game between two players in a plexiglass tube with a zero-g ball.","ImageName":"Paddleball.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":false,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.321686Z","PublishedAt":"2026-03-11T06:48:51.321686Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":2,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"9","SubRooms":[{"SubRoomId":9,"RoomId":9,"CreatorAccountId":null,"UnitySceneId":"d89f74fa-d51e-477a-a425-025a891dd499","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":2,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":10,"Name":"Paintball","Description":"Red and Blue teams splat each other in capture the flag and team battle.","ImageName":"Paintball.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":true,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.322204Z","PublishedAt":"2026-03-11T06:48:51.322204Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":8,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"10","SubRooms":[{"SubRoomId":10,"RoomId":10,"CreatorAccountId":null,"UnitySceneId":"e122fe98-e7db-49e8-a1b1-105424b6e1f0","Name":"River","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":11,"RoomId":10,"CreatorAccountId":null,"UnitySceneId":"a785267d-c579-42ea-be43-fec1992d1ca7","Name":"Homestead","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":12,"RoomId":10,"CreatorAccountId":null,"UnitySceneId":"ff4c6427-7079-4f59-b22a-69b089420827","Name":"Quarry","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":13,"RoomId":10,"CreatorAccountId":null,"UnitySceneId":"380d18b5-de9c-49f3-80f7-f4a95c1de161","Name":"Clearcut","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":14,"RoomId":10,"CreatorAccountId":null,"UnitySceneId":"58763055-2dfb-4814-80b8-16fac5c85709","Name":"Spillway","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":15,"RoomId":10,"CreatorAccountId":null,"UnitySceneId":"65ddbb48-5a01-4e3e-972d-e5c7419e2bc3","Name":"Drive-in","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"pvp","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":11,"Name":"PaintballVR","Description":"Red and Blue teams splat each other in capture the flag and team battle.","ImageName":"PaintballVR.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":true,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":false,"SupportsWalkVR":false,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.322714Z","PublishedAt":"2026-03-11T06:48:51.322714Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":8,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"11","SubRooms":[{"SubRoomId":16,"RoomId":11,"CreatorAccountId":null,"UnitySceneId":"e122fe98-e7db-49e8-a1b1-105424b6e1f0","Name":"River","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":17,"RoomId":11,"CreatorAccountId":null,"UnitySceneId":"a785267d-c579-42ea-be43-fec1992d1ca7","Name":"Homestead","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":18,"RoomId":11,"CreatorAccountId":null,"UnitySceneId":"ff4c6427-7079-4f59-b22a-69b089420827","Name":"Quarry","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":19,"RoomId":11,"CreatorAccountId":null,"UnitySceneId":"380d18b5-de9c-49f3-80f7-f4a95c1de161","Name":"Clearcut","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":20,"RoomId":11,"CreatorAccountId":null,"UnitySceneId":"58763055-2dfb-4814-80b8-16fac5c85709","Name":"Spillway","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":21,"RoomId":11,"CreatorAccountId":null,"UnitySceneId":"65ddbb48-5a01-4e3e-972d-e5c7419e2bc3","Name":"Drive-in","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":12,"Name":"GoldenTrophy","Description":"The goblin king stole Coach''s Golden Trophy. Team up and embark on an epic quest to recover it!","ImageName":"GoldenTrophy.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.323222Z","PublishedAt":"2026-03-11T06:48:51.323222Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":4,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"12","SubRooms":[{"SubRoomId":22,"RoomId":12,"CreatorAccountId":null,"UnitySceneId":"91e16e35-f48f-4700-ab8a-a1b79e50e51b","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":4,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"quest","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":13,"Name":"Orientation","Description":"An introductory tour of Rec Room!","ImageName":"Orientation.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.323621Z","PublishedAt":"2026-03-11T06:48:51.323621Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":1,"CloningAllowed":false,"DisableMicAutoMute":false,"DisableRoomComments":true,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"13","SubRooms":[{"SubRoomId":23,"RoomId":13,"CreatorAccountId":null,"UnitySceneId":"c79709d8-a31b-48aa-9eb8-cc31ba9505e8","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":1,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":14,"Name":"TheRiseofJumbotron","Description":"Robot invaders threaten the galaxy! Team up with your friends and bring the laser heat!","ImageName":"TheRiseofJumbotron.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.324014Z","PublishedAt":"2026-03-11T06:48:51.324014Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":4,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"14","SubRooms":[{"SubRoomId":24,"RoomId":14,"CreatorAccountId":null,"UnitySceneId":"acc06e66-c2d0-4361-b0cd-46246a4c455c","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":4,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"quest","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":15,"Name":"CrimsonCauldron","Description":"Can your band of adventurers brave the enchanted wilds, and lift the curse of the crimson cauldron?","ImageName":"CrimsonCauldron.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.324419Z","PublishedAt":"2026-03-11T06:48:51.324419Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":4,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"15","SubRooms":[{"SubRoomId":25,"RoomId":15,"CreatorAccountId":null,"UnitySceneId":"949fa41f-4347-45c0-b7ac-489129174045","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":4,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"quest","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":16,"Name":"IsleOfLostSkulls","Description":"Can your pirate crew get to the Isle, defeat its fearsome guardian, and escape with the gold?","ImageName":"IsleOfLostSkulls.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.324810Z","PublishedAt":"2026-03-11T06:48:51.324810Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":3,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"16","SubRooms":[{"SubRoomId":26,"RoomId":16,"CreatorAccountId":null,"UnitySceneId":"7e01cfe0-820a-406f-b1b3-0a5bf575235c","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":3,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"quest","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":17,"Name":"Soccer","Description":"Teams of three run around slamming themselves into an over-sized soccer ball. Goal!","ImageName":"Soccer.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.325197Z","PublishedAt":"2026-03-11T06:48:51.325197Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":6,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"17","SubRooms":[{"SubRoomId":27,"RoomId":17,"CreatorAccountId":null,"UnitySceneId":"6d5eea4b-f069-4ed0-9916-0e2f07df0d03","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":6,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":18,"Name":"LaserTag","Description":"Teams battle each other and waves of robots.","ImageName":"LaserTag.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":true,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.325598Z","PublishedAt":"2026-03-11T06:48:51.325598Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":8,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"18","SubRooms":[{"SubRoomId":28,"RoomId":18,"CreatorAccountId":null,"UnitySceneId":"239e676c-f12f-489f-bf3a-d4c383d692c3","Name":"Hangar","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":29,"RoomId":18,"CreatorAccountId":null,"UnitySceneId":"9d6456ce-6264-48b4-808d-2d96b3d91038","Name":"CyberJunkCity","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"pvp","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":19,"Name":"LaserTagCyberJunk","Description":"Teams battle each other and waves of robots in a totally cyber neon future city.","ImageName":"LaserTagCyberJunk.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":0,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.326002Z","PublishedAt":"2026-03-11T06:48:51.326002Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":8,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"19","SubRooms":[{"SubRoomId":30,"RoomId":19,"CreatorAccountId":null,"UnitySceneId":"9d6456ce-6264-48b4-808d-2d96b3d91038","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":0,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":20,"Name":"RecRoyaleSquads","Description":"Squads of three battle it out on Frontier Island. Last squad standing wins!","ImageName":"RecRoyale.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.326394Z","PublishedAt":"2026-03-11T06:48:51.326394Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":18,"CloningAllowed":false,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"20","SubRooms":[{"SubRoomId":31,"RoomId":20,"CreatorAccountId":null,"UnitySceneId":"253fa009-6e65-4c90-91a1-7137a56a267f","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":18,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":21,"Name":"RecRoyaleSolos","Description":"Battle it out on Frontier Island. Last person standing wins!","ImageName":"RecRoyale.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.326850Z","PublishedAt":"2026-03-11T06:48:51.326850Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":16,"CloningAllowed":false,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"21","SubRooms":[{"SubRoomId":32,"RoomId":21,"CreatorAccountId":null,"UnitySceneId":"b010171f-4875-4e89-baba-61e878cd41e1","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":16,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":22,"Name":"Lounge","Description":"A low-key lounge to chill with your friends. Great for private parties!","ImageName":"Lounge.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.327299Z","PublishedAt":"2026-03-11T06:48:51.327299Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"22","SubRooms":[{"SubRoomId":33,"RoomId":22,"CreatorAccountId":null,"UnitySceneId":"a067557f-ca32-43e6-b6e5-daaec60b4f5a","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":23,"Name":"PerformanceHall","Description":"A theater for plays, music, comedy and other performances.","ImageName":"PerformanceHall.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.327737Z","PublishedAt":"2026-03-11T06:48:51.327737Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":40,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"23","SubRooms":[{"SubRoomId":34,"RoomId":23,"CreatorAccountId":null,"UnitySceneId":"9932f88f-3929-43a0-a012-a40b5128e346","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":40,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":24,"Name":"MakerRoom","Description":"This room is a blank canvas. Make it into whatever you like!","ImageName":"MakerRoom.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.328171Z","PublishedAt":"2026-03-11T06:48:51.328171Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"24","SubRooms":[{"SubRoomId":35,"RoomId":24,"CreatorAccountId":null,"UnitySceneId":"a75f7547-79eb-47c6-8986-6767abcb4f92","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":12,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":25,"Name":"Park","Description":"A sprawling park with amphitheater, play fields, and a cave.","ImageName":"Park.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.328556Z","PublishedAt":"2026-03-11T06:48:51.328556Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"25","SubRooms":[{"SubRoomId":36,"RoomId":25,"CreatorAccountId":null,"UnitySceneId":"0a864c86-5a71-4e18-8041-8124e4dc9d98","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":26,"Name":"River","Description":"The original outdoor paintball course. Simple, balanced, classic.","ImageName":"Paintball.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.328941Z","PublishedAt":"2026-03-11T06:48:51.328941Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"26","SubRooms":[{"SubRoomId":37,"RoomId":26,"CreatorAccountId":null,"UnitySceneId":"e122fe98-e7db-49e8-a1b1-105424b6e1f0","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":27,"Name":"Homestead","Description":"A day on the farm. Great for asymmetrical battles!","ImageName":"Homestead.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.329347Z","PublishedAt":"2026-03-11T06:48:51.329347Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"27","SubRooms":[{"SubRoomId":38,"RoomId":27,"CreatorAccountId":null,"UnitySceneId":"a785267d-c579-42ea-be43-fec1992d1ca7","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":28,"Name":"Quarry","Description":"The sun sets on this construction site in the desert. Great for sniping battles!","ImageName":"Quarry.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.329761Z","PublishedAt":"2026-03-11T06:48:51.329761Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"28","SubRooms":[{"SubRoomId":39,"RoomId":28,"CreatorAccountId":null,"UnitySceneId":"ff4c6427-7079-4f59-b22a-69b089420827","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":29,"Name":"Clearcut","Description":"The sun rises on this logging camp in the forest. Great for mid-range splatting!","ImageName":"Clearcut.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.330167Z","PublishedAt":"2026-03-11T06:48:51.330167Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"29","SubRooms":[{"SubRoomId":40,"RoomId":29,"CreatorAccountId":null,"UnitySceneId":"380d18b5-de9c-49f3-80f7-f4a95c1de161","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":30,"Name":"Spillway","Description":"Night shift at the hydroelectric plant. Great for stealthy splatting!","ImageName":"Spillway.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.330562Z","PublishedAt":"2026-03-11T06:48:51.330562Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"30","SubRooms":[{"SubRoomId":41,"RoomId":30,"CreatorAccountId":null,"UnitySceneId":"58763055-2dfb-4814-80b8-16fac5c85709","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":31,"Name":"Lake","Description":"A leisurely trail to the lake and beyond.","ImageName":"DiscGolfLake.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.330960Z","PublishedAt":"2026-03-11T06:48:51.330960Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"31","SubRooms":[{"SubRoomId":42,"RoomId":31,"CreatorAccountId":null,"UnitySceneId":"f6f7256c-e438-4299-b99e-d20bef8cf7e0","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":32,"Name":"PropulsionTestRange","Description":"The science department left some of their equipment laying around. What will you do with it?","ImageName":"DiscGolfPropulsion.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.331353Z","PublishedAt":"2026-03-11T06:48:51.331353Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"32","SubRooms":[{"SubRoomId":43,"RoomId":32,"CreatorAccountId":null,"UnitySceneId":"d9378c9f-80bc-46fb-ad1e-1bed8a674f55","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":33,"Name":"Gym","Description":"A school gymnasium for smaller sporting events.","ImageName":"Dodgeball.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.331779Z","PublishedAt":"2026-03-11T06:48:51.331779Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"33","SubRooms":[{"SubRoomId":44,"RoomId":33,"CreatorAccountId":null,"UnitySceneId":"3d474b26-26f7-45e9-9a36-9b02847d5e6f","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":34,"Name":"Stadium","Description":"A professional stadium for larger sporting events.","ImageName":"Soccer.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.332483Z","PublishedAt":"2026-03-11T06:48:51.332483Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"34","SubRooms":[{"SubRoomId":45,"RoomId":34,"CreatorAccountId":null,"UnitySceneId":"6d5eea4b-f069-4ed0-9916-0e2f07df0d03","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":35,"Name":"Hangar","Description":"A late-eighties-style laser tag battle arcade.","ImageName":"LaserTag.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.333009Z","PublishedAt":"2026-03-11T06:48:51.333009Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"35","SubRooms":[{"SubRoomId":46,"RoomId":35,"CreatorAccountId":null,"UnitySceneId":"239e676c-f12f-489f-bf3a-d4c383d692c3","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":36,"Name":"CyberJunkCity","Description":"A late-2080s-style cyberpunk dystopian laser tag battle arcade","ImageName":"CyberJunkCity.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.333466Z","PublishedAt":"2026-03-11T06:48:51.333466Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"36","SubRooms":[{"SubRoomId":47,"RoomId":36,"CreatorAccountId":null,"UnitySceneId":"9d6456ce-6264-48b4-808d-2d96b3d91038","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":37,"Name":"DodgeballVR","Description":"Throw dodgeballs to knock out your friends in this gym classic!","ImageName":"Dodgeball.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":false,"SupportsWalkVR":false,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.333880Z","PublishedAt":"2026-03-11T06:48:51.333880Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":6,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"37","SubRooms":[{"SubRoomId":48,"RoomId":37,"CreatorAccountId":null,"UnitySceneId":"3d474b26-26f7-45e9-9a36-9b02847d5e6f","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":6,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":38,"Name":"Crescendo","Description":"Gather your vampire hunting crew, conquer a legendary castle, and restore peace to Rec Room!","ImageName":"Crescendo.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.334308Z","PublishedAt":"2026-03-11T06:48:51.334308Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":3,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"38","SubRooms":[{"SubRoomId":49,"RoomId":38,"CreatorAccountId":null,"UnitySceneId":"49cb8993-a956-43e2-86f4-1318f279b22a","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":3,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"quest","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":39,"Name":"Bowling","Description":"Hang out with friends, bowl a few games, eat snacks!","ImageName":"Bowling.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.334714Z","PublishedAt":"2026-03-11T06:48:51.334714Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":8,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"39","SubRooms":[{"SubRoomId":50,"RoomId":39,"CreatorAccountId":null,"UnitySceneId":"ae929543-9a07-41d5-8ee9-dbbee8c36800","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":8,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":40,"Name":"BowlingAlley","Description":"Lone Shoe Lanes is the hottest hangout in town. ","ImageName":"Bowling.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.335102Z","PublishedAt":"2026-03-11T06:48:51.335102Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":8,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"40","SubRooms":[{"SubRoomId":51,"RoomId":40,"CreatorAccountId":null,"UnitySceneId":"ae929543-9a07-41d5-8ee9-dbbee8c36800","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":8,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":41,"Name":"StuntRunner","Description":"Sprint, climb, and wall jump in this high-speed obstacle course where every second counts!","ImageName":"StuntRunner.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":1,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":false,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.335494Z","PublishedAt":"2026-03-11T06:48:51.335494Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":12,"CloningAllowed":false,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"41","SubRooms":[{"SubRoomId":52,"RoomId":41,"CreatorAccountId":null,"UnitySceneId":"b7281665-a715-4051-826b-8e08e69c6172","Name":"StuntRunner","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":12,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null},{"SubRoomId":53,"RoomId":41,"CreatorAccountId":null,"UnitySceneId":"3a636bd2-f896-424c-9225-c184522c0d87","Name":"TheMainEvent","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":4,"Accessibility":1,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":42,"Name":"StuntRunnerBaseRoom","Description":"A soundstage warehouse used to film Stunt Runner and other RecTV shows.","ImageName":"StuntRunner.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.335904Z","PublishedAt":"2026-03-11T06:48:51.335904Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"42","SubRooms":[{"SubRoomId":54,"RoomId":42,"CreatorAccountId":null,"UnitySceneId":"882e9b96-7115-4b03-86f6-c0c9d8e22e00","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":43,"Name":"Registration","Description":"Registration","ImageName":"Registration.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.336373Z","PublishedAt":"2026-03-11T06:48:51.336373Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":1,"CloningAllowed":false,"DisableMicAutoMute":true,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"43","SubRooms":[{"SubRoomId":55,"RoomId":43,"CreatorAccountId":null,"UnitySceneId":"cf61556d-68fd-4288-9ae5-7a512621e569","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":false,"MaxPlayers":1,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":44,"Name":"ARRoom","Description":"Your real room!","ImageName":"ARRoom.jpg","WarningMask":32,"CustomWarning":"Use Augmented Reality to play Rec Room in your real-world space! Please be aware of your surroundings. Note: this room cannot be saved.","CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.336778Z","PublishedAt":"2026-03-11T06:48:51.336778Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":4,"CloningAllowed":false,"DisableMicAutoMute":true,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"44","SubRooms":[{"SubRoomId":56,"RoomId":44,"CreatorAccountId":null,"UnitySceneId":"bf268f5f-b55b-41af-8628-32fa4b5d70b6","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":4,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); +INSERT OR IGNORE INTO rooms (data) VALUES ('{"RoomId":51,"Name":"DriveIn","Description":"A drive-in theater, diner, and truck stop make this a multi-area paint arena","ImageName":"DriveIn.jpg","WarningMask":0,"CustomWarning":null,"CreatorAccountId":1,"State":0,"Accessibility":2,"PublishState":0,"SupportsLevelVoting":false,"IsRRO":true,"IsRecRoomApproved":false,"ExcludeFromLists":false,"ExcludeFromSearch":false,"SupportsScreens":true,"SupportsWalkVR":true,"SupportsTeleportVR":true,"SupportsVRLow":true,"SupportsQuest2":true,"SupportsMobile":true,"SupportsJuniors":true,"MinLevel":0,"AgeRating":2,"CreatedAt":"2026-03-11T06:48:51.342473Z","PublishedAt":"2026-03-11T06:48:51.342473Z","BecameRRStudioRoomAt":null,"Stats":{"CheerCount":0,"FavoriteCount":0,"VisitorCount":0,"VisitCount":0},"RankingContext":null,"IsDorm":false,"IsPlacePlay":false,"MaxPlayerCalculationMode":0,"MaxPlayers":20,"CloningAllowed":true,"DisableMicAutoMute":false,"DisableRoomComments":false,"EncryptVoiceChat":false,"ToxmodEnabled":true,"LoadScreenLocked":false,"UgcVersion":1,"PersistenceVersion":0,"UgcSubVersion":null,"MinUgcSubVersion":null,"AutoLocalizeRoom":false,"LocalizationContext":{"TargetLocale":null,"Scope":null,"LocalizedFields":["Description","CustomWarning"]},"IsDeveloperOwned":true,"RankedEntityId":"51","SubRooms":[{"SubRoomId":63,"RoomId":51,"CreatorAccountId":null,"UnitySceneId":"65ddbb48-5a01-4e3e-972d-e5c7419e2bc3","Name":"Home","LastModeratedSaveModerationState":0,"IsSandbox":true,"MaxPlayers":20,"Accessibility":2,"ShouldAutoStageSaves":true,"StagedSubRoomDataSaveId":null}],"Roles":[{"AccountId":1,"Role":255,"LastChangedByAccountId":null,"InvitedRole":0},{"AccountId":2,"Role":30,"LastChangedByAccountId":null,"InvitedRole":0}],"IsJuniorCreated":false,"Tags":[{"Tag":"rro","Type":2},{"Tag":"base","Type":0}],"PromoImages":[],"PromoExternalContent":[],"LoadScreens":[],"RestrictedCircuitsAllowListNames":[]}'); diff --git a/apps/rooms/src/context.ts b/apps/rooms/src/context.ts index 329ac40..a7ca618 100644 --- a/apps/rooms/src/context.ts +++ b/apps/rooms/src/context.ts @@ -2,7 +2,8 @@ import type { HonoApp } from '@repo/hono-helpers' import type { SharedHonoEnv, SharedHonoVariables } from '@repo/hono-helpers/src/types' export type Env = SharedHonoEnv & { - // add additional Bindings here + // D1 database holding rooms (JSON blob + generated columns). See rooms-db.ts. + DB: D1Database } /** Variables can be extended */ diff --git a/apps/rooms/src/jwt.ts b/apps/rooms/src/jwt.ts new file mode 100644 index 0000000..d3c5d2d --- /dev/null +++ b/apps/rooms/src/jwt.ts @@ -0,0 +1,58 @@ +/** + * Minimal HS256 JWT validation, mirroring the C# `JwtTokenService.ValidateAndGetAccountId`. + * + * Uses the same placeholder dev secret as the `auth` worker (`apps/auth/src/jwt.ts`). + * Swap both for a shared secret binding before this is used for anything real. + */ +const DEV_SECRET = 'dev-insecure-signing-key-change-me' + +function base64urlToBytes(input: string): Uint8Array { + const padded = input.replace(/-/g, '+').replace(/_/g, '/') + const binary = atob(padded + '='.repeat((4 - (padded.length % 4)) % 4)) + const bytes = new Uint8Array(binary.length) + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i) + } + return bytes +} + +/** + * Validate an HS256 token and return its `sub` (account id) claim, or `null` + * when the token is malformed, has a bad signature, or is expired. + */ +export async function validateAndGetAccountId( + token: string, + secret: string = DEV_SECRET +): Promise { + const parts = token.split('.') + if (parts.length !== 3) return null + const [header, payload, signature] = parts + + const key = await crypto.subtle.importKey( + 'raw', + new TextEncoder().encode(secret), + { name: 'HMAC', hash: 'SHA-256' }, + false, + ['verify'] + ) + const valid = await crypto.subtle.verify( + 'HMAC', + key, + base64urlToBytes(signature), + new TextEncoder().encode(`${header}.${payload}`) + ) + if (!valid) return null + + let claims: { sub?: string; exp?: number } + try { + claims = JSON.parse(new TextDecoder().decode(base64urlToBytes(payload))) + } catch { + return null + } + + if (typeof claims.exp === 'number' && claims.exp < Math.floor(Date.now() / 1000)) { + return null + } + + return claims.sub ?? null +} diff --git a/apps/rooms/src/rooms-db.ts b/apps/rooms/src/rooms-db.ts new file mode 100644 index 0000000..0637205 --- /dev/null +++ b/apps/rooms/src/rooms-db.ts @@ -0,0 +1,72 @@ +/** + * Room storage on D1. Each room is a single JSON blob in the `data` column; + * queryable fields (RoomId, Name, CreatorAccountId, IsDorm) are SQLite + * generated (virtual) columns extracted from that JSON and indexed. This keeps + * the room shape flexible while still allowing fast lookups by id/name/creator. + * + * `SCHEMA_DDL` mirrors `migrations/0001_init.sql`; the room data is seeded from + * `static/ImportRooms.json` by `migrations/0002_import_rooms.sql`. Tests apply + * `SCHEMA_DDL` then seed the imported rooms directly. + */ + +/** Schema DDL (mirror of migrations/0001_init.sql, sans the seed INSERT). */ +export const SCHEMA_DDL: string[] = [ + `CREATE TABLE IF NOT EXISTS rooms ( + data TEXT NOT NULL, + room_id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.RoomId')) VIRTUAL, + name TEXT GENERATED ALWAYS AS (json_extract(data, '$.Name')) VIRTUAL, + name_lower TEXT GENERATED ALWAYS AS (lower(json_extract(data, '$.Name'))) VIRTUAL, + creator_account_id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.CreatorAccountId')) VIRTUAL, + is_dorm INTEGER GENERATED ALWAYS AS (json_extract(data, '$.IsDorm')) VIRTUAL + )`, + `CREATE UNIQUE INDEX IF NOT EXISTS idx_rooms_room_id ON rooms (room_id)`, + `CREATE INDEX IF NOT EXISTS idx_rooms_name_lower ON rooms (name_lower)`, + `CREATE INDEX IF NOT EXISTS idx_rooms_creator ON rooms (creator_account_id)`, +] + +/** A stored room — the parsed JSON blob (full client-facing room response). */ +export type Room = Record + +interface RoomRow { + data: string +} + +const parseOne = (row: RoomRow | null): Room | null => (row ? (JSON.parse(row.data) as Room) : null) +const parseAll = (rows: RoomRow[]): Room[] => rows.map((r) => JSON.parse(r.data) as Room) + +/** Look up a single room by its RoomId. */ +export async function getRoomById(db: D1Database, roomId: number): Promise { + return parseOne( + await db.prepare('SELECT data FROM rooms WHERE room_id = ?1').bind(roomId).first() + ) +} + +/** Look up a single room by name (case-insensitive exact match). */ +export async function getRoomByName(db: D1Database, name: string): Promise { + return parseOne( + await db + .prepare('SELECT data FROM rooms WHERE name_lower = ?1') + .bind(name.toLowerCase()) + .first() + ) +} + +/** Look up multiple rooms by RoomId. */ +export async function getRoomsByIds(db: D1Database, ids: number[]): Promise { + if (ids.length === 0) return [] + const placeholders = ids.map((_, i) => `?${i + 1}`).join(',') + const { results } = await db + .prepare(`SELECT data FROM rooms WHERE room_id IN (${placeholders})`) + .bind(...ids) + .all() + return parseAll(results) +} + +/** All rooms created by an account (e.g. their dorm). */ +export async function getRoomsByCreator(db: D1Database, accountId: number): Promise { + const { results } = await db + .prepare('SELECT data FROM rooms WHERE creator_account_id = ?1') + .bind(accountId) + .all() + return parseAll(results) +} diff --git a/apps/rooms/src/rooms.app.ts b/apps/rooms/src/rooms.app.ts index 984a0b3..67f213d 100644 --- a/apps/rooms/src/rooms.app.ts +++ b/apps/rooms/src/rooms.app.ts @@ -3,26 +3,37 @@ import { useWorkersLogger } from 'workers-tagged-logger' import { withNotFound, withOnError } from '@repo/hono-helpers' +import { validateAndGetAccountId } from './jwt' +import { getRoomById, getRoomByName, getRoomsByCreator, getRoomsByIds } from './rooms-db' + +import type { Context } from 'hono' import type { App } from './context' /** - * Ported from the C# `RoomsController`. The C# backs these with EF Core - * (`AppDbContext`); there's no DB binding yet, so room lookups synthesize the - * same shape `BuildRoomResponse` produces. Responses are PascalCase — the C# - * sets `PropertyNamingPolicy = null` and the anonymous response object uses - * PascalCase member names (see JSON/ownedrooms.json). + * Room server. Rooms are stored in D1 as JSON blobs with generated columns for + * querying (see rooms-db.ts); the dorm (RoomId 1) is seeded by the migration. + * Responses are the stored JSON verbatim (PascalCase, client-facing shape). * - * The class `[Route("rooms")]` prefix maps to this worker's subdomain, so the - * method routes are served bare (e.g. `/rooms/{id}`, not `/rooms/rooms/{id}`). + * The C# `[Route("rooms")]` prefix maps to this worker's subdomain, so method + * routes are served bare. The 2023 client also hits several of these without the + * `/roomserver` prefix, so both forms are registered. */ -/** - * Build the full room payload the C# `BuildRoomResponse` returns. With no DB, - * room 1 is the dorm (matching JSON/ownedrooms.json) and any other id gets a - * generic published room so the client can still resolve and load it. - */ -/** Unity scene id for the dorm (also the matchmake/heartbeat instance location). */ -const DORM_SCENE_ID = '76d98498-60a1-430c-ab76-b54a29b7a163' +/** Parse the first valid integer id from a comma-separated `id` query param. */ +function firstId(idParam: string): number | undefined { + return idParam + .split(',') + .map((s) => Number.parseInt(s.trim(), 10)) + .find((n) => !Number.isNaN(n)) +} + +/** Parse all valid integer ids from a comma-separated `id` query param. */ +function allIds(idParam: string): number[] { + return idParam + .split(',') + .map((s) => Number.parseInt(s.trim(), 10)) + .filter((n) => !Number.isNaN(n)) +} /** Room permissions + (empty) Photon token the client needs to spawn into a room. */ function photonAccessToken() { @@ -52,56 +63,16 @@ function photonAccessToken() { } } -function buildRoomResponse(roomId: number) { - const isDorm = roomId === 1 - return { - RoomId: roomId, - Name: isDorm ? 'DormRoom' : `Room${roomId}`, - Description: isDorm ? 'Your private room' : '', - CreatorAccountId: 1, - ImageName: 'DefaultRoomImage.jpg', - State: 0, - Accessibility: 0, - SupportsLevelVoting: false, - IsRRO: false, - IsDorm: isDorm, - CloningAllowed: false, - SupportsVRLow: true, - SupportsQuest2: true, - SupportsMobile: true, - SupportsScreens: true, - SupportsWalkVR: true, - SupportsTeleportVR: true, - SupportsJuniors: true, - MinLevel: 0, - WarningMask: 0, - CustomWarning: null, - DisableMicAutoMute: false, - DisableRoomComments: false, - EncryptVoiceChat: false, - CreatedAt: '2026-01-18T02:31:37.6171131', - Stats: { CheerCount: 0, FavoriteCount: 0, VisitorCount: 1, VisitCount: 1 }, - // The client needs a SubRoom (UnitySceneId + DataBlob) to load the scene. - // The dorm points at the dorm scene; an empty DataBlob loads the default - // build. Generic rooms have no known scene yet. - SubRooms: [ - { - SubRoomId: 1, - Name: '', - DataBlob: '', - IsSandbox: false, - MaxPlayers: 4, - Accessibility: 0, - UnitySceneId: isDorm ? DORM_SCENE_ID : '', - DataSavedAt: '2026-01-18T02:31:37.6171131', - }, - ], - Roles: [], - LoadScreens: [], - PromoImages: [], - PromoExternalContent: [], - Tags: [], +/** The account whose owned rooms to return: the Bearer token's `sub`, falling + * back to account 1 (the stub player) when there's no valid token. */ +async function ownerId(c: Context): Promise { + const authHeader = c.req.header('Authorization') ?? '' + if (authHeader.toLowerCase().startsWith('bearer ')) { + const sub = await validateAndGetAccountId(authHeader.slice('Bearer '.length)) + const id = sub ? Number.parseInt(sub, 10) : Number.NaN + if (!Number.isNaN(id)) return id } + return 1 } const app = new Hono() @@ -120,63 +91,55 @@ const app = new Hono() .get('/', (c) => c.json({ service: 'rooms', status: 'ok' })) - // Room lookup by `id` (comma-separated; first match wins) or `name`. The C# - // 400s when neither is supplied and returns `{}` when nothing matches. - .get('/rooms', (c) => { + // Room lookup by `id` (first match wins) or `name`. The C# 400s when neither + // is supplied and returns `{}` when nothing matches. + .get('/rooms', async (c) => { const idParam = c.req.query('id') const nameParam = c.req.query('name') if (!idParam && !nameParam) { return c.json("Either 'id' or 'name' query parameter is required", 400) } if (idParam) { - const firstId = idParam - .split(',') - .map((s) => Number.parseInt(s.trim(), 10)) - .find((n) => !Number.isNaN(n)) - if (firstId === undefined) return c.json({}) - return c.json(buildRoomResponse(firstId)) + const id = firstId(idParam) + const room = id === undefined ? null : await getRoomById(c.env.DB, id) + return c.json(room ?? {}) } - // Looked up by name — no DB to resolve it, so synthesize a room 1 (dorm). - // TODO: resolve the named room once a DB binding exists. - return c.json(buildRoomResponse(1)) + const room = await getRoomByName(c.env.DB, nameParam ?? '') + return c.json(room ?? {}) }) - // Bulk room lookup by `id` (synthesized per id) or `name`. The client calls - // this bare on the rooms host. We have no named-room data, so a name lookup - // returns [] — the client treats that as NoSuchRoom (a non-fatal warning), - // which is the honest answer since we can't actually host that room. - .get('/rooms/bulk', (c) => { + // Bulk room lookup by `id` or `name` — returns an array of matched rooms (the + // client calls this bare on the rooms host). Rooms not in D1 are simply absent + // from the result; the client treats an empty result as NoSuchRoom. + .get('/rooms/bulk', async (c) => { const idParam = c.req.query('id') const nameParam = c.req.query('name') if (!idParam && !nameParam) { return c.json("Either 'id' or 'name' query parameter is required", 400) } if (idParam) { - const ids = idParam - .split(',') - .map((s) => Number.parseInt(s.trim(), 10)) - .filter((n) => !Number.isNaN(n)) - return c.json(ids.map(buildRoomResponse)) + return c.json(await getRoomsByIds(c.env.DB, allIds(idParam))) } - return c.json([]) + const room = await getRoomByName(c.env.DB, nameParam ?? '') + return c.json(room ? [room] : []) }) - // Single room by id. The C# 404s when the row is missing; with no DB we - // synthesize the room so the client can load it (ignores the include/ - // unityAsset* query params, same as the C#). - .get('/rooms/:roomId{[0-9]+}', (c) => { - const roomId = Number.parseInt(c.req.param('roomId'), 10) - return c.json(buildRoomResponse(roomId)) - }) + // Rooms created/owned by the caller (their dorm). The client calls all three. + .get('/roomserver/rooms/createdby/me', async (c) => + c.json(await getRoomsByCreator(c.env.DB, await ownerId(c))) + ) + .get('/rooms/ownedby/me', async (c) => c.json(await getRoomsByCreator(c.env.DB, await ownerId(c)))) + .get('/rooms/createdby/me', async (c) => c.json(await getRoomsByCreator(c.env.DB, await ownerId(c)))) - // Rooms created by the caller. The C# serves JSON/ownedrooms.json (the dorm). - .get('/roomserver/rooms/createdby/me', (c) => c.json([buildRoomResponse(1)])) + // Single room by id. 404 when the room isn't in D1 (matches the C#). Ignores + // the include/unityAsset* query params, same as the C#. + .get('/rooms/:roomId{[0-9]+}', async (c) => { + const room = await getRoomById(c.env.DB, Number.parseInt(c.req.param('roomId'), 10)) + return room ? c.json(room) : c.notFound() + }) // Photon access token + room permissions the client needs to spawn into a - // room. Without it the player is stuck on a black screen. PhotonAccessToken is - // empty (the client uses its baked-in Photon credentials); roomInstanceId is - // our constant 1. The client calls it on the rooms host both bare and under - // `/roomserver`, so both are registered. + // room. The client calls it on the rooms host both bare and under `/roomserver`. .get('/photon_access_token', (c) => c.json(photonAccessToken())) .get('/roomserver/photon_access_token', (c) => c.json(photonAccessToken())) diff --git a/apps/rooms/src/test/integration/api.test.ts b/apps/rooms/src/test/integration/api.test.ts index a11bd22..f99e3d4 100644 --- a/apps/rooms/src/test/integration/api.test.ts +++ b/apps/rooms/src/test/integration/api.test.ts @@ -1,10 +1,50 @@ -import { SELF } from 'cloudflare:test' -import { describe, expect, it } from 'vitest' +import { env, SELF } from 'cloudflare:test' +import { beforeAll, describe, expect, it } from 'vitest' import '../../rooms.app' +import importRooms from '../../../static/ImportRooms.json' +import { SCHEMA_DDL } from '../../rooms-db' + +import type { Env } from '../../context' + +declare module 'cloudflare:test' { + interface ProvidedEnv extends Env {} +} + const ORIGIN = 'https://rooms.rec.djdevin.net' +// Mint a token the way the `auth` worker does, using the same dev secret. +const DEV_SECRET = 'dev-insecure-signing-key-change-me' +function b64url(input: ArrayBuffer | string): string { + const bytes = typeof input === 'string' ? new TextEncoder().encode(input) : new Uint8Array(input) + let binary = '' + for (const byte of bytes) binary += String.fromCharCode(byte) + return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') +} +async function bearer(sub: string): Promise> { + const now = Math.floor(Date.now() / 1000) + const signingInput = `${b64url(JSON.stringify({ alg: 'HS256', typ: 'JWT' }))}.${b64url( + JSON.stringify({ sub, exp: now + 3600 }) + )}` + const key = await crypto.subtle.importKey( + 'raw', + new TextEncoder().encode(DEV_SECRET), + { name: 'HMAC', hash: 'SHA-256' }, + false, + ['sign'] + ) + const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(signingInput)) + return { Authorization: `Bearer ${signingInput}.${b64url(sig)}` } +} + +// Apply the schema + seed the imported rooms into the test D1 (mirrors the migrations). +beforeAll(async () => { + for (const stmt of SCHEMA_DDL) await env.DB.prepare(stmt).run() + const insert = env.DB.prepare('INSERT OR IGNORE INTO rooms (data) VALUES (?1)') + await env.DB.batch(importRooms.map((r) => insert.bind(JSON.stringify(r)))) +}) + describe('rooms endpoints', () => { it('GET / reports service status', async () => { const res = await SELF.fetch(`${ORIGIN}/`) @@ -12,34 +52,33 @@ describe('rooms endpoints', () => { expect(await res.json()).toEqual({ service: 'rooms', status: 'ok' }) }) - it('GET /rooms/1 returns the dorm room (ignoring include/unityAsset params)', async () => { - const res = await SELF.fetch( - `${ORIGIN}/rooms/1?include=1325&unityAssetTarget=0&unityAssetVersion=1` - ) + it('GET /rooms/1 returns the seeded dorm with its SubRoom scene', async () => { + const res = await SELF.fetch(`${ORIGIN}/rooms/1?include=1325`) expect(res.status).toBe(200) const body = (await res.json()) as { RoomId: number Name: string IsDorm: boolean - Stats: object + SubRooms: Array<{ UnitySceneId: string }> } expect(body).toMatchObject({ RoomId: 1, Name: 'DormRoom', IsDorm: true }) - expect(body).toHaveProperty('Stats') - const subRooms = (body as unknown as { SubRooms: Array<{ UnitySceneId: string }> }).SubRooms - expect(subRooms[0].UnitySceneId).toBe('76d98498-60a1-430c-ab76-b54a29b7a163') + expect(body.SubRooms[0].UnitySceneId).toBe('76d98498-60a1-430c-ab76-b54a29b7a163') }) - it('GET /rooms/:id synthesizes a generic room for other ids', async () => { - const res = await SELF.fetch(`${ORIGIN}/rooms/42`) - expect(res.status).toBe(200) - const body = (await res.json()) as { RoomId: number; Name: string; IsDorm: boolean } - expect(body).toMatchObject({ RoomId: 42, Name: 'Room42', IsDorm: false }) + it('GET /rooms/:id 404s for a room not in D1', async () => { + const res = await SELF.fetch(`${ORIGIN}/rooms/99999`) + expect(res.status).toBe(404) }) - it('GET /rooms?id=1 returns the matching room', async () => { - const res = await SELF.fetch(`${ORIGIN}/rooms?id=1`) + it('GET /rooms?name= resolves a real room case-insensitively', async () => { + const res = await SELF.fetch(`${ORIGIN}/rooms?name=reccenter`) expect(res.status).toBe(200) - expect(await res.json()).toMatchObject({ RoomId: 1, Name: 'DormRoom' }) + expect(await res.json()).toMatchObject({ Name: 'RecCenter' }) + }) + + it('GET /rooms?name= returns {} when nothing matches', async () => { + const res = await SELF.fetch(`${ORIGIN}/rooms?name=NoSuchRoomHere`) + expect(await res.json()).toEqual({}) }) it('GET /rooms with no id or name returns 400', async () => { @@ -47,36 +86,36 @@ describe('rooms endpoints', () => { expect(res.status).toBe(400) }) - it('GET /roomserver/photon_access_token returns permissions + instance id', async () => { - const res = await SELF.fetch(`${ORIGIN}/roomserver/photon_access_token`) + it('GET /rooms/bulk?id= returns the matching rooms', async () => { + const res = await SELF.fetch(`${ORIGIN}/rooms/bulk?id=1,2`) expect(res.status).toBe(200) - const body = (await res.json()) as { Permissions: unknown[]; RoomInstanceId: number } - expect(body.Permissions.length).toBeGreaterThan(0) - expect(body.RoomInstanceId).toBe(1) + const body = (await res.json()) as Array<{ RoomId: number; Name: string }> + expect(body.map((r) => r.Name).sort()).toEqual(['DormRoom', 'RecCenter']) }) - it('GET /rooms/bulk?id= returns an array; ?name= returns []', async () => { - const byId = await SELF.fetch(`${ORIGIN}/rooms/bulk?id=1,2`) - expect(byId.status).toBe(200) - expect(((await byId.json()) as unknown[]).length).toBe(2) - const byName = await SELF.fetch(`${ORIGIN}/rooms/bulk?name=RecCenter`) - expect(byName.status).toBe(200) - expect(await byName.json()).toEqual([]) + it('GET /rooms/bulk?name=RecCenter returns [RecCenter]', async () => { + const res = await SELF.fetch(`${ORIGIN}/rooms/bulk?name=RecCenter`) + const body = (await res.json()) as Array<{ Name: string }> + expect(body.map((r) => r.Name)).toEqual(['RecCenter']) }) - it('GET /photon_access_token (bare) also returns permissions', async () => { - const res = await SELF.fetch(`${ORIGIN}/photon_access_token`) - expect(res.status).toBe(200) - const body = (await res.json()) as { Permissions: unknown[]; RoomInstanceId: number } - expect(body.Permissions.length).toBeGreaterThan(0) - expect(body.RoomInstanceId).toBe(1) + it('GET /rooms/ownedby/me returns the caller created rooms (auth-scoped)', async () => { + // No token → stub account 1, which owns all the seeded rooms. + const mine = (await (await SELF.fetch(`${ORIGIN}/rooms/ownedby/me`)).json()) as unknown[] + expect(mine.length).toBe(importRooms.length) + // A different account owns none of them. + const other = (await ( + await SELF.fetch(`${ORIGIN}/rooms/ownedby/me`, { headers: await bearer('999') }) + ).json()) as unknown[] + expect(other).toEqual([]) }) - it('GET /roomserver/rooms/createdby/me returns the owned rooms array', async () => { - const res = await SELF.fetch(`${ORIGIN}/roomserver/rooms/createdby/me`) - expect(res.status).toBe(200) - const body = (await res.json()) as Array<{ RoomId: number }> - expect(Array.isArray(body)).toBe(true) - expect(body[0]).toMatchObject({ RoomId: 1, Name: 'DormRoom' }) + it('GET /photon_access_token (bare + /roomserver) returns permissions', async () => { + for (const path of ['/photon_access_token', '/roomserver/photon_access_token']) { + const res = await SELF.fetch(`${ORIGIN}${path}`) + expect(res.status).toBe(200) + const body = (await res.json()) as { Permissions: unknown[]; RoomInstanceId: number } + expect(body.Permissions.length).toBeGreaterThan(0) + } }) }) diff --git a/apps/rooms/static/ImportRooms.json b/apps/rooms/static/ImportRooms.json new file mode 100644 index 0000000..a2f5299 --- /dev/null +++ b/apps/rooms/static/ImportRooms.json @@ -0,0 +1,4803 @@ +[ + { + "RoomId": 1, + "Name": "DormRoom", + "Description": "Your private room", + "ImageName": "DormRoom.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.317921Z", + "PublishedAt": "2026-03-11T06:48:51.317921Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": true, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 4, + "CloningAllowed": false, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "1", + "SubRooms": [ + { + "SubRoomId": 1, + "RoomId": 1, + "CreatorAccountId": null, + "UnitySceneId": "76d98498-60a1-430c-ab76-b54a29b7a163", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 4, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 2, + "Name": "RecCenter", + "Description": "A social hub to meet and mingle with friends new and old.", + "ImageName": "RecCenter.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.318650Z", + "PublishedAt": "2026-03-11T06:48:51.318650Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 12, + "CloningAllowed": false, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "2", + "SubRooms": [ + { + "SubRoomId": 2, + "RoomId": 2, + "CreatorAccountId": null, + "UnitySceneId": "cbad71af-0831-44d8-b8ef-69edafa841f6", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 12, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 3, + "Name": "3DCharades", + "Description": "Take turns drawing, acting, and guessing funny phrases with your friends!", + "ImageName": "3DCharades.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": false, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.319060Z", + "PublishedAt": "2026-03-11T06:48:51.319060Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 8, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": true, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "3", + "SubRooms": [ + { + "SubRoomId": 3, + "RoomId": 3, + "CreatorAccountId": null, + "UnitySceneId": "a673712c-877f-4749-b69a-4a4c6310d545", + "Name": "InkSpaceHome", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 4, + "Name": "Legacy3DCharades", + "Description": "Take turns drawing, acting, and guessing funny phrases with your friends!", + "ImageName": "Legacy3DCharades.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": false, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": false, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.319471Z", + "PublishedAt": "2026-03-11T06:48:51.319471Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 8, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": true, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "4", + "SubRooms": [ + { + "SubRoomId": 4, + "RoomId": 4, + "CreatorAccountId": null, + "UnitySceneId": "4078dfed-24bb-4db7-863f-578ba48d726b", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 5, + "Name": "TheInkSpace", + "Description": "A cozy event venue with a stage. The entire room is made of props, so customize to your heart\u0027s content!", + "ImageName": "TheInkSpace.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": false, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.319872Z", + "PublishedAt": "2026-03-11T06:48:51.319872Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 8, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": true, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "5", + "SubRooms": [ + { + "SubRoomId": 5, + "RoomId": 5, + "CreatorAccountId": null, + "UnitySceneId": "1fa06e3c-c307-4c11-a91b-1fabcddb8a96", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 8, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 6, + "Name": "DiscGolfLake", + "Description": "A leisurely stroll through the grass. Throw your disc into the goal. Sounds easy, right?", + "ImageName": "DiscGolfLake.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.320270Z", + "PublishedAt": "2026-03-11T06:48:51.320270Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 4, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "6", + "SubRooms": [ + { + "SubRoomId": 6, + "RoomId": 6, + "CreatorAccountId": null, + "UnitySceneId": "f6f7256c-e438-4299-b99e-d20bef8cf7e0", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 4, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 7, + "Name": "DiscGolfPropulsion", + "Description": "Throw your disc through hazards and around wind machines on this challenging course!", + "ImageName": "DiscGolfPropulsion.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.320653Z", + "PublishedAt": "2026-03-11T06:48:51.320653Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 4, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "7", + "SubRooms": [ + { + "SubRoomId": 7, + "RoomId": 7, + "CreatorAccountId": null, + "UnitySceneId": "d9378c9f-80bc-46fb-ad1e-1bed8a674f55", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 4, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 8, + "Name": "Dodgeball", + "Description": "Throw dodgeballs to knock out your friends in this gym classic!", + "ImageName": "Dodgeball.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.321125Z", + "PublishedAt": "2026-03-11T06:48:51.321125Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 6, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "8", + "SubRooms": [ + { + "SubRoomId": 8, + "RoomId": 8, + "CreatorAccountId": null, + "UnitySceneId": "3d474b26-26f7-45e9-9a36-9b02847d5e6f", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 6, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 9, + "Name": "Paddleball", + "Description": "A simple rally game between two players in a plexiglass tube with a zero-g ball.", + "ImageName": "Paddleball.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": false, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.321686Z", + "PublishedAt": "2026-03-11T06:48:51.321686Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 2, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "9", + "SubRooms": [ + { + "SubRoomId": 9, + "RoomId": 9, + "CreatorAccountId": null, + "UnitySceneId": "d89f74fa-d51e-477a-a425-025a891dd499", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 2, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 10, + "Name": "Paintball", + "Description": "Red and Blue teams splat each other in capture the flag and team battle.", + "ImageName": "Paintball.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": true, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.322204Z", + "PublishedAt": "2026-03-11T06:48:51.322204Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 8, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "10", + "SubRooms": [ + { + "SubRoomId": 10, + "RoomId": 10, + "CreatorAccountId": null, + "UnitySceneId": "e122fe98-e7db-49e8-a1b1-105424b6e1f0", + "Name": "River", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 11, + "RoomId": 10, + "CreatorAccountId": null, + "UnitySceneId": "a785267d-c579-42ea-be43-fec1992d1ca7", + "Name": "Homestead", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 12, + "RoomId": 10, + "CreatorAccountId": null, + "UnitySceneId": "ff4c6427-7079-4f59-b22a-69b089420827", + "Name": "Quarry", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 13, + "RoomId": 10, + "CreatorAccountId": null, + "UnitySceneId": "380d18b5-de9c-49f3-80f7-f4a95c1de161", + "Name": "Clearcut", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 14, + "RoomId": 10, + "CreatorAccountId": null, + "UnitySceneId": "58763055-2dfb-4814-80b8-16fac5c85709", + "Name": "Spillway", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 15, + "RoomId": 10, + "CreatorAccountId": null, + "UnitySceneId": "65ddbb48-5a01-4e3e-972d-e5c7419e2bc3", + "Name": "Drive-in", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "pvp", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 11, + "Name": "PaintballVR", + "Description": "Red and Blue teams splat each other in capture the flag and team battle.", + "ImageName": "PaintballVR.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": true, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": false, + "SupportsWalkVR": false, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.322714Z", + "PublishedAt": "2026-03-11T06:48:51.322714Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 8, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "11", + "SubRooms": [ + { + "SubRoomId": 16, + "RoomId": 11, + "CreatorAccountId": null, + "UnitySceneId": "e122fe98-e7db-49e8-a1b1-105424b6e1f0", + "Name": "River", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 17, + "RoomId": 11, + "CreatorAccountId": null, + "UnitySceneId": "a785267d-c579-42ea-be43-fec1992d1ca7", + "Name": "Homestead", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 18, + "RoomId": 11, + "CreatorAccountId": null, + "UnitySceneId": "ff4c6427-7079-4f59-b22a-69b089420827", + "Name": "Quarry", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 19, + "RoomId": 11, + "CreatorAccountId": null, + "UnitySceneId": "380d18b5-de9c-49f3-80f7-f4a95c1de161", + "Name": "Clearcut", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 20, + "RoomId": 11, + "CreatorAccountId": null, + "UnitySceneId": "58763055-2dfb-4814-80b8-16fac5c85709", + "Name": "Spillway", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 21, + "RoomId": 11, + "CreatorAccountId": null, + "UnitySceneId": "65ddbb48-5a01-4e3e-972d-e5c7419e2bc3", + "Name": "Drive-in", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 12, + "Name": "GoldenTrophy", + "Description": "The goblin king stole Coach\u0027s Golden Trophy. Team up and embark on an epic quest to recover it!", + "ImageName": "GoldenTrophy.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.323222Z", + "PublishedAt": "2026-03-11T06:48:51.323222Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 4, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "12", + "SubRooms": [ + { + "SubRoomId": 22, + "RoomId": 12, + "CreatorAccountId": null, + "UnitySceneId": "91e16e35-f48f-4700-ab8a-a1b79e50e51b", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 4, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "quest", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 13, + "Name": "Orientation", + "Description": "An introductory tour of Rec Room!", + "ImageName": "Orientation.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.323621Z", + "PublishedAt": "2026-03-11T06:48:51.323621Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 1, + "CloningAllowed": false, + "DisableMicAutoMute": false, + "DisableRoomComments": true, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "13", + "SubRooms": [ + { + "SubRoomId": 23, + "RoomId": 13, + "CreatorAccountId": null, + "UnitySceneId": "c79709d8-a31b-48aa-9eb8-cc31ba9505e8", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 1, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 14, + "Name": "TheRiseofJumbotron", + "Description": "Robot invaders threaten the galaxy! Team up with your friends and bring the laser heat!", + "ImageName": "TheRiseofJumbotron.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.324014Z", + "PublishedAt": "2026-03-11T06:48:51.324014Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 4, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "14", + "SubRooms": [ + { + "SubRoomId": 24, + "RoomId": 14, + "CreatorAccountId": null, + "UnitySceneId": "acc06e66-c2d0-4361-b0cd-46246a4c455c", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 4, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "quest", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 15, + "Name": "CrimsonCauldron", + "Description": "Can your band of adventurers brave the enchanted wilds, and lift the curse of the crimson cauldron?", + "ImageName": "CrimsonCauldron.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.324419Z", + "PublishedAt": "2026-03-11T06:48:51.324419Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 4, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "15", + "SubRooms": [ + { + "SubRoomId": 25, + "RoomId": 15, + "CreatorAccountId": null, + "UnitySceneId": "949fa41f-4347-45c0-b7ac-489129174045", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 4, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "quest", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 16, + "Name": "IsleOfLostSkulls", + "Description": "Can your pirate crew get to the Isle, defeat its fearsome guardian, and escape with the gold?", + "ImageName": "IsleOfLostSkulls.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.324810Z", + "PublishedAt": "2026-03-11T06:48:51.324810Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 3, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "16", + "SubRooms": [ + { + "SubRoomId": 26, + "RoomId": 16, + "CreatorAccountId": null, + "UnitySceneId": "7e01cfe0-820a-406f-b1b3-0a5bf575235c", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 3, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "quest", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 17, + "Name": "Soccer", + "Description": "Teams of three run around slamming themselves into an over-sized soccer ball. Goal!", + "ImageName": "Soccer.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.325197Z", + "PublishedAt": "2026-03-11T06:48:51.325197Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 6, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "17", + "SubRooms": [ + { + "SubRoomId": 27, + "RoomId": 17, + "CreatorAccountId": null, + "UnitySceneId": "6d5eea4b-f069-4ed0-9916-0e2f07df0d03", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 6, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 18, + "Name": "LaserTag", + "Description": "Teams battle each other and waves of robots.", + "ImageName": "LaserTag.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": true, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.325598Z", + "PublishedAt": "2026-03-11T06:48:51.325598Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 8, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "18", + "SubRooms": [ + { + "SubRoomId": 28, + "RoomId": 18, + "CreatorAccountId": null, + "UnitySceneId": "239e676c-f12f-489f-bf3a-d4c383d692c3", + "Name": "Hangar", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 29, + "RoomId": 18, + "CreatorAccountId": null, + "UnitySceneId": "9d6456ce-6264-48b4-808d-2d96b3d91038", + "Name": "CyberJunkCity", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "pvp", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 19, + "Name": "LaserTagCyberJunk", + "Description": "Teams battle each other and waves of robots in a totally cyber neon future city.", + "ImageName": "LaserTagCyberJunk.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 0, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.326002Z", + "PublishedAt": "2026-03-11T06:48:51.326002Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 8, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "19", + "SubRooms": [ + { + "SubRoomId": 30, + "RoomId": 19, + "CreatorAccountId": null, + "UnitySceneId": "9d6456ce-6264-48b4-808d-2d96b3d91038", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 0, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 20, + "Name": "RecRoyaleSquads", + "Description": "Squads of three battle it out on Frontier Island. Last squad standing wins!", + "ImageName": "RecRoyale.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.326394Z", + "PublishedAt": "2026-03-11T06:48:51.326394Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 18, + "CloningAllowed": false, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "20", + "SubRooms": [ + { + "SubRoomId": 31, + "RoomId": 20, + "CreatorAccountId": null, + "UnitySceneId": "253fa009-6e65-4c90-91a1-7137a56a267f", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 18, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 21, + "Name": "RecRoyaleSolos", + "Description": "Battle it out on Frontier Island. Last person standing wins!", + "ImageName": "RecRoyale.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.326850Z", + "PublishedAt": "2026-03-11T06:48:51.326850Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 16, + "CloningAllowed": false, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "21", + "SubRooms": [ + { + "SubRoomId": 32, + "RoomId": 21, + "CreatorAccountId": null, + "UnitySceneId": "b010171f-4875-4e89-baba-61e878cd41e1", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 16, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 22, + "Name": "Lounge", + "Description": "A low-key lounge to chill with your friends. Great for private parties!", + "ImageName": "Lounge.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.327299Z", + "PublishedAt": "2026-03-11T06:48:51.327299Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "22", + "SubRooms": [ + { + "SubRoomId": 33, + "RoomId": 22, + "CreatorAccountId": null, + "UnitySceneId": "a067557f-ca32-43e6-b6e5-daaec60b4f5a", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 23, + "Name": "PerformanceHall", + "Description": "A theater for plays, music, comedy and other performances.", + "ImageName": "PerformanceHall.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.327737Z", + "PublishedAt": "2026-03-11T06:48:51.327737Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 40, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "23", + "SubRooms": [ + { + "SubRoomId": 34, + "RoomId": 23, + "CreatorAccountId": null, + "UnitySceneId": "9932f88f-3929-43a0-a012-a40b5128e346", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 40, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 24, + "Name": "MakerRoom", + "Description": "This room is a blank canvas. Make it into whatever you like!", + "ImageName": "MakerRoom.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.328171Z", + "PublishedAt": "2026-03-11T06:48:51.328171Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "24", + "SubRooms": [ + { + "SubRoomId": 35, + "RoomId": 24, + "CreatorAccountId": null, + "UnitySceneId": "a75f7547-79eb-47c6-8986-6767abcb4f92", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 12, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 25, + "Name": "Park", + "Description": "A sprawling park with amphitheater, play fields, and a cave.", + "ImageName": "Park.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.328556Z", + "PublishedAt": "2026-03-11T06:48:51.328556Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "25", + "SubRooms": [ + { + "SubRoomId": 36, + "RoomId": 25, + "CreatorAccountId": null, + "UnitySceneId": "0a864c86-5a71-4e18-8041-8124e4dc9d98", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 26, + "Name": "River", + "Description": "The original outdoor paintball course. Simple, balanced, classic.", + "ImageName": "Paintball.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.328941Z", + "PublishedAt": "2026-03-11T06:48:51.328941Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "26", + "SubRooms": [ + { + "SubRoomId": 37, + "RoomId": 26, + "CreatorAccountId": null, + "UnitySceneId": "e122fe98-e7db-49e8-a1b1-105424b6e1f0", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 27, + "Name": "Homestead", + "Description": "A day on the farm. Great for asymmetrical battles!", + "ImageName": "Homestead.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.329347Z", + "PublishedAt": "2026-03-11T06:48:51.329347Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "27", + "SubRooms": [ + { + "SubRoomId": 38, + "RoomId": 27, + "CreatorAccountId": null, + "UnitySceneId": "a785267d-c579-42ea-be43-fec1992d1ca7", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 28, + "Name": "Quarry", + "Description": "The sun sets on this construction site in the desert. Great for sniping battles!", + "ImageName": "Quarry.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.329761Z", + "PublishedAt": "2026-03-11T06:48:51.329761Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "28", + "SubRooms": [ + { + "SubRoomId": 39, + "RoomId": 28, + "CreatorAccountId": null, + "UnitySceneId": "ff4c6427-7079-4f59-b22a-69b089420827", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 29, + "Name": "Clearcut", + "Description": "The sun rises on this logging camp in the forest. Great for mid-range splatting!", + "ImageName": "Clearcut.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.330167Z", + "PublishedAt": "2026-03-11T06:48:51.330167Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "29", + "SubRooms": [ + { + "SubRoomId": 40, + "RoomId": 29, + "CreatorAccountId": null, + "UnitySceneId": "380d18b5-de9c-49f3-80f7-f4a95c1de161", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 30, + "Name": "Spillway", + "Description": "Night shift at the hydroelectric plant. Great for stealthy splatting!", + "ImageName": "Spillway.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.330562Z", + "PublishedAt": "2026-03-11T06:48:51.330562Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "30", + "SubRooms": [ + { + "SubRoomId": 41, + "RoomId": 30, + "CreatorAccountId": null, + "UnitySceneId": "58763055-2dfb-4814-80b8-16fac5c85709", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 31, + "Name": "Lake", + "Description": "A leisurely trail to the lake and beyond.", + "ImageName": "DiscGolfLake.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.330960Z", + "PublishedAt": "2026-03-11T06:48:51.330960Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "31", + "SubRooms": [ + { + "SubRoomId": 42, + "RoomId": 31, + "CreatorAccountId": null, + "UnitySceneId": "f6f7256c-e438-4299-b99e-d20bef8cf7e0", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 32, + "Name": "PropulsionTestRange", + "Description": "The science department left some of their equipment laying around. What will you do with it?", + "ImageName": "DiscGolfPropulsion.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.331353Z", + "PublishedAt": "2026-03-11T06:48:51.331353Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "32", + "SubRooms": [ + { + "SubRoomId": 43, + "RoomId": 32, + "CreatorAccountId": null, + "UnitySceneId": "d9378c9f-80bc-46fb-ad1e-1bed8a674f55", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 33, + "Name": "Gym", + "Description": "A school gymnasium for smaller sporting events.", + "ImageName": "Dodgeball.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.331779Z", + "PublishedAt": "2026-03-11T06:48:51.331779Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "33", + "SubRooms": [ + { + "SubRoomId": 44, + "RoomId": 33, + "CreatorAccountId": null, + "UnitySceneId": "3d474b26-26f7-45e9-9a36-9b02847d5e6f", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 34, + "Name": "Stadium", + "Description": "A professional stadium for larger sporting events.", + "ImageName": "Soccer.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.332483Z", + "PublishedAt": "2026-03-11T06:48:51.332483Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "34", + "SubRooms": [ + { + "SubRoomId": 45, + "RoomId": 34, + "CreatorAccountId": null, + "UnitySceneId": "6d5eea4b-f069-4ed0-9916-0e2f07df0d03", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 35, + "Name": "Hangar", + "Description": "A late-eighties-style laser tag battle arcade.", + "ImageName": "LaserTag.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.333009Z", + "PublishedAt": "2026-03-11T06:48:51.333009Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "35", + "SubRooms": [ + { + "SubRoomId": 46, + "RoomId": 35, + "CreatorAccountId": null, + "UnitySceneId": "239e676c-f12f-489f-bf3a-d4c383d692c3", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 36, + "Name": "CyberJunkCity", + "Description": "A late-2080s-style cyberpunk dystopian laser tag battle arcade", + "ImageName": "CyberJunkCity.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.333466Z", + "PublishedAt": "2026-03-11T06:48:51.333466Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "36", + "SubRooms": [ + { + "SubRoomId": 47, + "RoomId": 36, + "CreatorAccountId": null, + "UnitySceneId": "9d6456ce-6264-48b4-808d-2d96b3d91038", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 37, + "Name": "DodgeballVR", + "Description": "Throw dodgeballs to knock out your friends in this gym classic!", + "ImageName": "Dodgeball.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": false, + "SupportsWalkVR": false, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.333880Z", + "PublishedAt": "2026-03-11T06:48:51.333880Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 6, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "37", + "SubRooms": [ + { + "SubRoomId": 48, + "RoomId": 37, + "CreatorAccountId": null, + "UnitySceneId": "3d474b26-26f7-45e9-9a36-9b02847d5e6f", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 6, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 38, + "Name": "Crescendo", + "Description": "Gather your vampire hunting crew, conquer a legendary castle, and restore peace to Rec Room!", + "ImageName": "Crescendo.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.334308Z", + "PublishedAt": "2026-03-11T06:48:51.334308Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 3, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "38", + "SubRooms": [ + { + "SubRoomId": 49, + "RoomId": 38, + "CreatorAccountId": null, + "UnitySceneId": "49cb8993-a956-43e2-86f4-1318f279b22a", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 3, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "quest", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 39, + "Name": "Bowling", + "Description": "Hang out with friends, bowl a few games, eat snacks!", + "ImageName": "Bowling.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.334714Z", + "PublishedAt": "2026-03-11T06:48:51.334714Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 8, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "39", + "SubRooms": [ + { + "SubRoomId": 50, + "RoomId": 39, + "CreatorAccountId": null, + "UnitySceneId": "ae929543-9a07-41d5-8ee9-dbbee8c36800", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 8, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 40, + "Name": "BowlingAlley", + "Description": "Lone Shoe Lanes is the hottest hangout in town. ", + "ImageName": "Bowling.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.335102Z", + "PublishedAt": "2026-03-11T06:48:51.335102Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 8, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "40", + "SubRooms": [ + { + "SubRoomId": 51, + "RoomId": 40, + "CreatorAccountId": null, + "UnitySceneId": "ae929543-9a07-41d5-8ee9-dbbee8c36800", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 8, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 41, + "Name": "StuntRunner", + "Description": "Sprint, climb, and wall jump in this high-speed obstacle course where every second counts!", + "ImageName": "StuntRunner.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 1, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": false, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.335494Z", + "PublishedAt": "2026-03-11T06:48:51.335494Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 12, + "CloningAllowed": false, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "41", + "SubRooms": [ + { + "SubRoomId": 52, + "RoomId": 41, + "CreatorAccountId": null, + "UnitySceneId": "b7281665-a715-4051-826b-8e08e69c6172", + "Name": "StuntRunner", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 12, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + }, + { + "SubRoomId": 53, + "RoomId": 41, + "CreatorAccountId": null, + "UnitySceneId": "3a636bd2-f896-424c-9225-c184522c0d87", + "Name": "TheMainEvent", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 4, + "Accessibility": 1, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 42, + "Name": "StuntRunnerBaseRoom", + "Description": "A soundstage warehouse used to film Stunt Runner and other RecTV shows.", + "ImageName": "StuntRunner.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.335904Z", + "PublishedAt": "2026-03-11T06:48:51.335904Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "42", + "SubRooms": [ + { + "SubRoomId": 54, + "RoomId": 42, + "CreatorAccountId": null, + "UnitySceneId": "882e9b96-7115-4b03-86f6-c0c9d8e22e00", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 43, + "Name": "Registration", + "Description": "Registration", + "ImageName": "Registration.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.336373Z", + "PublishedAt": "2026-03-11T06:48:51.336373Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 1, + "CloningAllowed": false, + "DisableMicAutoMute": true, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "43", + "SubRooms": [ + { + "SubRoomId": 55, + "RoomId": 43, + "CreatorAccountId": null, + "UnitySceneId": "cf61556d-68fd-4288-9ae5-7a512621e569", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": false, + "MaxPlayers": 1, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 44, + "Name": "ARRoom", + "Description": "Your real room!", + "ImageName": "ARRoom.jpg", + "WarningMask": 32, + "CustomWarning": "Use Augmented Reality to play Rec Room in your real-world space! Please be aware of your surroundings. Note: this room cannot be saved.", + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.336778Z", + "PublishedAt": "2026-03-11T06:48:51.336778Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 4, + "CloningAllowed": false, + "DisableMicAutoMute": true, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "44", + "SubRooms": [ + { + "SubRoomId": 56, + "RoomId": 44, + "CreatorAccountId": null, + "UnitySceneId": "bf268f5f-b55b-41af-8628-32fa4b5d70b6", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 4, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + }, + { + "RoomId": 51, + "Name": "DriveIn", + "Description": "A drive-in theater, diner, and truck stop make this a multi-area paint arena", + "ImageName": "DriveIn.jpg", + "WarningMask": 0, + "CustomWarning": null, + "CreatorAccountId": 1, + "State": 0, + "Accessibility": 2, + "PublishState": 0, + "SupportsLevelVoting": false, + "IsRRO": true, + "IsRecRoomApproved": false, + "ExcludeFromLists": false, + "ExcludeFromSearch": false, + "SupportsScreens": true, + "SupportsWalkVR": true, + "SupportsTeleportVR": true, + "SupportsVRLow": true, + "SupportsQuest2": true, + "SupportsMobile": true, + "SupportsJuniors": true, + "MinLevel": 0, + "AgeRating": 2, + "CreatedAt": "2026-03-11T06:48:51.342473Z", + "PublishedAt": "2026-03-11T06:48:51.342473Z", + "BecameRRStudioRoomAt": null, + "Stats": { + "CheerCount": 0, + "FavoriteCount": 0, + "VisitorCount": 0, + "VisitCount": 0 + }, + "RankingContext": null, + "IsDorm": false, + "IsPlacePlay": false, + "MaxPlayerCalculationMode": 0, + "MaxPlayers": 20, + "CloningAllowed": true, + "DisableMicAutoMute": false, + "DisableRoomComments": false, + "EncryptVoiceChat": false, + "ToxmodEnabled": true, + "LoadScreenLocked": false, + "UgcVersion": 1, + "PersistenceVersion": 0, + "UgcSubVersion": null, + "MinUgcSubVersion": null, + "AutoLocalizeRoom": false, + "LocalizationContext": { + "TargetLocale": null, + "Scope": null, + "LocalizedFields": [ + "Description", + "CustomWarning" + ] + }, + "IsDeveloperOwned": true, + "RankedEntityId": "51", + "SubRooms": [ + { + "SubRoomId": 63, + "RoomId": 51, + "CreatorAccountId": null, + "UnitySceneId": "65ddbb48-5a01-4e3e-972d-e5c7419e2bc3", + "Name": "Home", + "LastModeratedSaveModerationState": 0, + "IsSandbox": true, + "MaxPlayers": 20, + "Accessibility": 2, + "ShouldAutoStageSaves": true, + "StagedSubRoomDataSaveId": null + } + ], + "Roles": [ + { + "AccountId": 1, + "Role": 255, + "LastChangedByAccountId": null, + "InvitedRole": 0 + }, + { + "AccountId": 2, + "Role": 30, + "LastChangedByAccountId": null, + "InvitedRole": 0 + } + ], + "IsJuniorCreated": false, + "Tags": [ + { + "Tag": "rro", + "Type": 2 + }, + { + "Tag": "base", + "Type": 0 + } + ], + "PromoImages": [], + "PromoExternalContent": [], + "LoadScreens": [], + "RestrictedCircuitsAllowListNames": [] + } +] \ No newline at end of file diff --git a/apps/rooms/wrangler.jsonc b/apps/rooms/wrangler.jsonc index ed7b413..a0728e1 100644 --- a/apps/rooms/wrangler.jsonc +++ b/apps/rooms/wrangler.jsonc @@ -10,6 +10,16 @@ "custom_domain": true } ], + // Room storage. Create with `wrangler d1 create rec-rooms`, set the id, then + // `wrangler d1 migrations apply rec-rooms --remote`. + "d1_databases": [ + { + "binding": "DB", + "database_name": "rec-rooms", + "database_id": "d44083e1-5bfe-4467-aa9a-f13c5c2496d5", + "migrations_dir": "migrations" + } + ], "upload_source_maps": true, "observability": { "logs": {