- Added missing room images
- Removed internal rooms and disallow cloning some rooms
- License fixes
- Switched target to 20200306
- Socket header fixes
- Sockets are closed upon shutdown
* Sockets persist after being destroyed, fix
- Config changes for 20200306
- Settings initialized
- Name generation words cannot be longer than 9 characters
- Dorm generation changes and fixes
- Added some settings keys
- Matchmaking additions
* Instances are not yet updated to be or not to be in-progress
- Instance fixes and logging
- Image operation fixes
- DisplayName route start
- Challenge route start
- Default Amplitude key (i can see althe Amplitude requests are ignored
- Rate limiting ease
- GameConfigs properly queried and sent
- Many 'bulk' endpoints were added in or around 20200306
- Objective routes started
- DormRoom redirection in v2/name
- Client doesn't care if it gets 200 when setting prefs
- Balance/storefronts started
- Matchmaking goto/room timer and fixes
- Selfhosted Photon addition on the client sends matchmaking /photonregionpings, ignore since Photon is only one server in one region
132 lines
6.9 KiB
TypeScript
132 lines
6.9 KiB
TypeScript
/* Galvanic Corrosion - Rec Room custom server for communities.
|
|
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
|
|
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 <https://www.gnu.org/licenses/>. */
|
|
|
|
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;
|
|
}
|
|
|
|
} |