This commit is contained in:
2025-08-30 16:01:43 -04:00
parent 391bf3d1f8
commit 2fbd09e43f
17 changed files with 382 additions and 36 deletions

View File

@@ -9,8 +9,12 @@ export class ServerContentBase {
this.server = server;
this.kv = new KV(id, kv);
server.on('server.start', this.start);
server.on('server.destroy', this.destroy);
server.on('server.start', _ev => {
this.start();
});
server.on('server.destroy', _ev => {
this.destroy();
});
}
async kvInit() {

View File

@@ -2,6 +2,14 @@ import { CloudRegionCode } from "../../util/photon.ts";
import type Profile from "../profiles/profile.ts";
import { RoomInstance, RoomLocation } from "./base.ts";
export interface InstanceCreationOptions {
roomId: number,
subRoomId: number,
name: string,
maxCapacity: number,
private?: boolean
}
export class Instance {
#createdAt = new Date();
@@ -9,40 +17,42 @@ export class Instance {
#players: Set<Profile> = new Set();
#instanceId: number;
//#roomId: number;
//#subRoomId: number;
#roomId: number;
#subRoomId: number;
#location: RoomLocation;
//#name: string;
//#maxCapacity: number;
#name: string;
#maxCapacity: number;
#isFull: boolean = false;
//#isPrivate: boolean;
#isPrivate: boolean;
#isInProgress: boolean = false;
#photonRegionId: string = CloudRegionCode.us;
//#photonRoomId: string;
#photonRoomId: string;
#dataBlob?: string;
#eventId?: string
#eventId?: number
constructor(options: {
id: number,
location: RoomLocation,
}
) {
this.#instanceId = options.id;
this.#location = options.location;
constructor(id: number, location: RoomLocation, options: InstanceCreationOptions) {
this.#instanceId = id;
this.#location = location;
this.#roomId = options.roomId;
this.#subRoomId = options.subRoomId;
this.#isPrivate = typeof options.private == 'boolean' ? options.private : false;
this.#name = options.name;
this.#maxCapacity = options.maxCapacity;
this.#photonRoomId = `GCR-${this.#instanceId}`;
}
getPlayers() {
return this.#players.values().toArray();
}
playerIsHere(profile: Profile) {
return this.getPlayers().find(prof => prof.same(profile)) ? true : false;
return Boolean(this.getPlayers().find(prof => prof.same(profile)));
}
removePlayer(profile: Profile) {
this.#players.delete(profile);
}
addPlayer(profile: Profile) {
this.#players.add(profile);
}
getCreatedAt() {
@@ -50,10 +60,22 @@ export class Instance {
}
export() {
/*const inst: RoomInstance = {
const inst: RoomInstance = {
roomInstanceId: this.#instanceId,
}*/
roomId: this.#roomId,
subRoomId: this.#subRoomId,
location: this.#location,
name: this.#name,
maxCapacity: this.#maxCapacity,
isFull: this.#isFull,
isPrivate: this.#isPrivate,
isInProgress: this.#isInProgress,
photonRegionId: this.#photonRegionId,
photonRoomId: this.#photonRoomId,
dataBlob: this.#dataBlob,
eventId: this.#eventId
};
return inst;
}
}

View File

@@ -58,6 +58,6 @@ export interface RoomInstance {
export class InstanceManager extends ServerContentBase {
}

View File

@@ -0,0 +1,5 @@
import ProfileContentManager from "./base.ts";
export class ProfileProgressionManager extends ProfileContentManager {
} // do this soon:tm:

View File

@@ -1,5 +1,33 @@
import ProfileContentManager from "./base.ts";
interface ProfileReputation {
AccountId: number,
Noteriety: number,
CheerGeneral: number,
CheerHelpful: number,
CheerGreatHost: number,
CheerSportsman: number,
CheerCreative: number,
CheerCredit: number,
SubscriberCount: number,
SubscribedCount: number,
}
export class ProfileReputationManager extends ProfileContentManager {
async export(): Promise<ProfileReputation> {
return {
AccountId: this.profile.getId(),
Noteriety: 0.0,
CheerGeneral: 0.0,
CheerHelpful: 0.0,
CheerGreatHost: 0.0,
CheerSportsman: 0.0,
CheerCreative: 0.0,
CheerCredit: 0.0,
SubscribedCount: 0,
SubscriberCount: 0
}
}
}

View File

@@ -34,7 +34,7 @@ export class ProfileSettingsManager extends ProfileContentManager {
await this.kv.getKv().set(this.#key, settings);
}
async setSetting(Key: ProfileSetting, Value: string) {
async setSetting(Key: string, Value: string) {
const settings = await this.getAllSettings();
const s = settings.find(setting => setting.Key === Key);
if (!s) settings.push({ Key, Value });

View File

@@ -93,7 +93,7 @@ class ProfileManagerBase extends ServerContentBase {
exec: async (id: number) => {
const prof = await this.get(id);
if (!prof) return prof;
else return await prof.export();
else return prof.export();
},
zod: z.tuple([
z.string().transform(Number)

26
src/server/rooms/base.ts Normal file
View File

@@ -0,0 +1,26 @@
import { ServerContentBase } from "../ContentBase.ts";
export class ServerRoomsBase extends ServerContentBase {
#roomsKey = "rooms";
#roomNamesKey = "room_names"
getKv() {
return this.kv;
}
async getIdFromName(name: string) {
const id = await this.kv.getKv().get<number>([this.#roomsKey, name]);
if (id.value == null) return null;
return id.value;
}
async getFromName(name: string) {
}
async get(id: number) {
}
}

View File

@@ -0,0 +1,178 @@
import { RoomLocation } from "../../instances/base.ts";
import { RoomFactory } from "./RoomFactory.ts";
import { SubroomFactory } from "./SubroomFactory.ts";
export enum WriteMode {
Overwrite = "overwrite",
WriteIfFree = "if_free"
}
export enum FactoryMode {
Fetch = 'fetch',
Write = 'write'
}
export type HardwareSupport = "screens" | "walk_vr" | "teleport_vr" | "low_vr" | "mobile";
export const HardwareSupportStrings = ["screens", "walk_vr", "teleport_vr", "low_vr", "mobile"];
export enum RoomState {
Active,
PendingJunior = 11,
Moderation_PendingReview = 100,
Moderation_Closed,
MarkedForDelete = 1000
}
export enum RoomAccessibility {
Private,
Public,
Unlisted
}
export interface BuiltinScene {
Name: string,
RoomSceneLocationId: RoomLocation,
IsSandbox: boolean,
CanMatchmakeInto: boolean,
SupportsJoinInProgress: boolean,
UseLevelBasedMatchmaking: boolean,
UseAgeBasedMatchmaking: boolean,
UseRecRoyaleMatchmaking: boolean,
MaxPlayers: number
}
export interface BuiltinRoom {
Name: string,
Description: string,
Accessibility: RoomAccessibility,
SupportsLevelVoting: boolean,
CloningAllowed: boolean,
SupportsScreens: boolean,
SupportsWalkVR: boolean,
SupportsTeleportVR: boolean,
Scenes: BuiltinScene[]
}
export interface RoomScene {
RoomSceneId: number,
RoomId: number,
RoomSceneLocationId: string,
Name: string,
IsSandbox: boolean,
DataBlobName: string,
MaxPlayers: number,
CanMatchmakeInto?: boolean,
DataModifiedAt: string
}
export interface Room {
RoomId: number,
Name: string,
Description: string,
CreatorPlayerId: number,
ImageName: string,
State: RoomState,
Accessibility: RoomAccessibility,
SupportsLevelVoting: boolean,
IsAGRoom: boolean,
IsDormRoom?: boolean,
CloningAllowed: boolean,
SupportsScreens: boolean,
SupportsWalkVR: boolean,
SupportsTeleportVR: boolean,
AllowsJuniors: boolean,
RoomWarningMask: number,
CustomRoomWarning: string,
DisableMicAutoMute?: boolean | null
}
export enum RoomWarningMask {
None,
Scary,
Mature = 2,
FlashingLights = 4,
IntenseMotion = 8,
Violence = 16,
Custom = 32
}
export enum TagType {
General,
Auto,
AGOnly,
Banned
}
export interface TagDTO {
Tag: string,
Type: TagType
}
export interface RoomDetails {
Room: Room,
Scenes: RoomScene[],
CoOwners: number[],
InvitedCoOwners: number[],
Moderators?: number[],
InvitedModerators?: number[],
Hosts: number[],
InvitedHosts: number[],
CheerCount: number,
FavoriteCount: number,
VisitCount: number,
Tags: TagDTO[]
}
export enum CreateModifyRoomStatus {
Success,
Unknown,
PermissionDenied,
RoomNotActive,
RoomDoesNotExist,
RoomHasNoDataBlob,
DuplicateName = 10,
ReservedName,
InappropriateName,
InappropriateDescription,
TooManyRooms = 20,
InvalidMaxPlayers = 30,
DataHistoryDoesNotExist = 40,
DataHistoryAlreadyActive,
InvalidTags = 50,
NoStartingRoomScene = 55,
RoomUnderModerationReview = 100,
PlayerHasRoomUnderModerationReview,
AccessibilityUnderModerationLock,
JuniorStatusFail = 200,
InappropriateCustomWarning = 300,
PartialBanSuccessBanPower = 400,
TargetHasBanPower,
PlayerIsRoomBanned = 410
}
export interface DatabaseRoom {
Visits: number,
Hardware: Set<HardwareSupport>,
Subrooms: number[],
Tags: Set<string>,
CoOwners: Set<number>,
InvitedCoOwners: Set<number>,
Hosts: Set<number>,
InvitedHosts: Set<number>,
Favorites: Set<number>,
Cheers: Set<number>
}
export interface DatabaseSubroom {
}
export interface RoomUpdatedEvent {
room: RoomFactory
}
export interface SubroomUpdatedEvent {
subroom: SubroomFactory
}
export * as RoomDataTypes from "./DataTypes.ts";

View File

@@ -0,0 +1,53 @@
import type KV from "../../persistence/kv.ts";
import { type ServerBase } from "../../server.ts";
import { DatabaseRoom, RoomDataTypes } from "./DataTypes.ts";
export interface RoomFactoryOptions {
id?: number,
name?: string,
}
export class RoomFactory {
#server: ServerBase;
#kv: KV;
#roomId: number | undefined;
#name: string | undefined;
#description: string | undefined;
#creatorPlayerId: number | undefined;
#imageName: string | undefined;
#state: RoomDataTypes.RoomScene | undefined;
#accessibility: RoomDataTypes.RoomAccessibility | undefined;
#supportsLevelVoting: boolean | undefined;
#isAGRoom: boolean | undefined;
#isDormRoom: boolean | undefined;
#cloningAllowed: boolean | undefined;
#supportsScreens: boolean | undefined;
#supportsWalkVR: boolean | undefined;
#supportsTeleportVR: boolean | undefined;
#allowsJuniors: boolean | undefined;
#roomWarningMask: number | undefined;
#customRoomWarning: string | undefined;
#disableMicAutoMute: boolean | undefined;
#dbMeta: DatabaseRoom | null = null;
#visits: number | undefined;
#hardware
constructor(server: ServerBase, kv: KV) {
this.#server = server;
this.#kv = kv;
}
async init(options: RoomFactoryOptions) {
if (typeof options.id == 'undefined' && typeof options.name == 'undefined')
throw new Error("Must specify a room ID or a room name");
}
}

View File

@@ -1,3 +1,4 @@
import { ServerUpdateEvent } from "../serverevents.ts";
import { AvatarContentBase } from "./avatars/base.ts";
import { EventManager } from "./baseevent.ts";
import { CommandsBase } from "./commands/commands.ts";
@@ -9,13 +10,17 @@ import { type PresenceUpdateEvent } from "./presence/events/PresenceUpdateEvent.
import { type ProfileUpdateEvent } from "./profiles/events/ProfileUpdate.ts";
import { type RoleUpdateEvent } from "./profiles/events/RoleUpdate.ts";
import ProfileManagerBase from "./profiles/manager.ts";
import { ServerRoomsBase } from "./rooms/base.ts";
import { RoomUpdatedEvent, SubroomUpdatedEvent } from "./rooms/internal/DataTypes.ts";
interface ServerEvents {
'profile.roleupdate': RoleUpdateEvent,
'profile.update': ProfileUpdateEvent,
'presence.update': PresenceUpdateEvent,
'server.start': undefined,
'server.destroy': undefined,
'server.start': ServerUpdateEvent,
'server.destroy': ServerUpdateEvent,
'room.updated': RoomUpdatedEvent,
'room.subroom.updated': SubroomUpdatedEvent
}
class ServerBase extends EventManager<ServerEvents> {
@@ -26,6 +31,7 @@ class ServerBase extends EventManager<ServerEvents> {
Avatars = new AvatarContentBase(this, 'avatars');
Instances = new InstanceManager(this, 'instances');
Content = new ServerContentManager(this, "content");
Rooms = new ServerRoomsBase(this, 'rooms', true);
}
const Server = new ServerBase();