/* Galvanic Corrosion - Rec Room custom server for communities. Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import { Redis } from "../../db.ts"; import Rooms from "./rooms.ts"; import { IntegratedRoomScene, RoomAccessibility, RoomDetails, RoomState } from "./roomtypes.ts"; interface RoomFetchOptions { roomName?: string, roomId?: number } export function parseBooleanDefault(obj: string, def: boolean | undefined = false) { try { return JSON.parse(obj) as boolean; } catch { return def; } } export class RoomFetch { roomId: number | null = null; roomName: string | null = null; constructor(options: RoomFetchOptions) { this.roomId = options.roomId ?? null; this.roomName = options.roomName ?? null; } async fetch() { if (!this.roomId && this.roomName) { const givenId = await Rooms.getIdFromName(this.roomName); if (!givenId) return null; else this.roomId = givenId; } else if (!this.roomName && this.roomId) { const givenName = await Rooms.getNameFromId(this.roomId); if (!givenName) return null; else this.roomName = givenName; } else if (!this.roomId && !this.roomName) return null; const roomRootKey = Redis.buildKey( Redis.KeyGroups.Rooms.Root, this.roomId!.toString(), // code above takes care of null possibility ); const roomMetaKey = Redis.buildKey( roomRootKey, Rooms.roomRootKeys.Meta ); const [ hash, cheerCount, favoriteCount, visitCount ] = await Promise.all([ Redis.Database.hgetall(roomMetaKey), Redis.Database.get(Redis.buildKey(roomRootKey, Rooms.roomRootKeys.CheerCount)), Redis.Database.get(Redis.buildKey(roomRootKey, Rooms.roomRootKeys.FavoriteCount)), Redis.Database.get(Redis.buildKey(roomRootKey, Rooms.roomRootKeys.VisitCount)), ]); const room: RoomDetails = { Room: { RoomId: hash[Rooms.roomMetaKeys.RoomId] ? parseInt(hash[Rooms.roomMetaKeys.RoomId]) : 0, Name: hash[Rooms.roomMetaKeys.Name] ?? "DATABASEERROR", Description: hash[Rooms.roomMetaKeys.Description] ?? "DATABASEERROR", CreatorPlayerId: hash[Rooms.roomMetaKeys.CreatorPlayerId] ? parseInt(hash[Rooms.roomMetaKeys.CreatorPlayerId]) : 1, ImageName: hash[Rooms.roomMetaKeys.ImageName] ?? "DefaultProfileImage.png", State: hash[Rooms.roomMetaKeys.State] ? parseInt(hash[Rooms.roomMetaKeys.State]) : RoomState.Active, Accessibility: hash[Rooms.roomMetaKeys.Accessibility] ? parseInt(hash[Rooms.roomMetaKeys.Accessibility]) : RoomAccessibility.Unlisted, SupportsLevelVoting: hash[Rooms.roomMetaKeys.SupportsLevelVoting] ? parseBooleanDefault(hash[Rooms.roomMetaKeys.SupportsLevelVoting], true) : true, IsAGRoom: hash[Rooms.roomMetaKeys.IsAGRoom] ? parseBooleanDefault(hash[Rooms.roomMetaKeys.IsAGRoom], false) : false, IsDormRoom: hash[Rooms.roomMetaKeys.IsDormRoom] ? parseBooleanDefault(hash[Rooms.roomMetaKeys.IsDormRoom], false) : false, CloningAllowed: hash[Rooms.roomMetaKeys.CloningAllowed] ? parseBooleanDefault(hash[Rooms.roomMetaKeys.CloningAllowed], false) : false, SupportsScreens: hash[Rooms.roomMetaKeys.SupportsScreens] ? parseBooleanDefault(hash[Rooms.roomMetaKeys.SupportsScreens], true) : true, SupportsWalkVR: hash[Rooms.roomMetaKeys.SupportsWalkVR] ? parseBooleanDefault(hash[Rooms.roomMetaKeys.SupportsWalkVR], true) : true, SupportsTeleportVR: hash[Rooms.roomMetaKeys.SupportsTeleportVR] ? parseBooleanDefault(hash[Rooms.roomMetaKeys.SupportsTeleportVR], true) : true, AllowsJuniors: hash[Rooms.roomMetaKeys.AllowsJuniors] ? parseBooleanDefault(hash[Rooms.roomMetaKeys.AllowsJuniors], true) : true, RoomWarningMask: hash[Rooms.roomMetaKeys.RoomWarningMask] ? parseInt(hash[Rooms.roomMetaKeys.RoomWarningMask]) : 0, CustomRoomWarning: hash[Rooms.roomMetaKeys.CustomRoomWarning] ?? "", DisableMicAutoMute: hash[Rooms.roomMetaKeys.DisableMicAutoMute] ? parseBooleanDefault(hash[Rooms.roomMetaKeys.DisableMicAutoMute], false) : undefined, }, Scenes: [], // temporary CoOwners: [], // temporary InvitedCoOwners: [], // temporary Hosts: [], // temporary InvitedHosts: [], // temporary CheerCount: cheerCount ? parseInt(cheerCount) : 0, FavoriteCount: favoriteCount ? parseInt(favoriteCount) : 0, VisitCount: visitCount ? parseInt(visitCount) : 0, Tags: [] // temporary } const subrooms = await Rooms.getSubroomIdsFromRoom(this.roomId!); for (const subroom of subrooms) { const subroomMetaKey = Redis.buildKey( Redis.KeyGroups.Rooms.Root, this.roomId!.toString(), Rooms.roomRootKeys.Subrooms, subroom.toString(), Rooms.subroomRootKeys.Meta ); const subroomDetails = await Redis.Database.hgetall(subroomMetaKey); room.Scenes.push({ RoomSceneId: parseInt(subroom), RoomId: this.roomId ?? 0, RoomSceneLocationId: subroomDetails[Rooms.subroomMetaKeys.RoomSceneLocationId] ?? IntegratedRoomScene.MakerRoom, Name: subroomDetails[Rooms.subroomMetaKeys.Name] ?? "DATABASE ERROR", IsSandbox: subroomDetails[Rooms.subroomMetaKeys.IsSandbox] ? parseBooleanDefault(subroomDetails[Rooms.subroomMetaKeys.IsSandbox], false) : false, CanMatchmakeInto: subroomDetails[Rooms.subroomMetaKeys.IsSandbox] ? parseBooleanDefault(subroomDetails[Rooms.subroomMetaKeys.IsSandbox], true) : undefined, DataBlobName: subroomDetails[Rooms.subroomMetaKeys.DataBlobName] ?? "", MaxPlayers: subroomDetails[Rooms.subroomMetaKeys.MaxPlayers] ? parseInt(subroomDetails[Rooms.subroomMetaKeys.MaxPlayers]) : 1, DataModifiedAt: subroomDetails[Rooms.subroomMetaKeys.DataModifiedAt] ?? new Date().toISOString() }); } return room; } }