From 2fbd09e43ff61b9de943bb5f4e301a80cd230f64 Mon Sep 17 00:00:00 2001 From: zombieb Date: Sat, 30 Aug 2025 16:01:43 -0400 Subject: [PATCH] wer --- src/main.ts | 14 +- src/net.ts | 4 +- src/routes/api/routes/messages.ts | 6 +- src/routes/api/routes/players.ts | 10 ++ src/routes/api/routes/settings.ts | 5 +- src/server/ContentBase.ts | 8 +- src/server/instances/Instance.ts | 60 ++++--- src/server/instances/base.ts | 2 +- src/server/profiles/content/Progression.ts | 5 + src/server/profiles/content/Reputation.ts | 28 ++++ src/server/profiles/content/Settings.ts | 2 +- src/server/profiles/manager.ts | 2 +- src/server/rooms/base.ts | 26 +++ src/server/rooms/internal/DataTypes.ts | 178 +++++++++++++++++++++ src/server/rooms/internal/RoomFactory.ts | 53 ++++++ src/server/server.ts | 10 +- src/serverevents.ts | 5 + 17 files changed, 382 insertions(+), 36 deletions(-) create mode 100644 src/server/profiles/content/Progression.ts create mode 100644 src/server/rooms/base.ts create mode 100644 src/server/rooms/internal/DataTypes.ts create mode 100644 src/server/rooms/internal/RoomFactory.ts create mode 100644 src/serverevents.ts diff --git a/src/main.ts b/src/main.ts index df0037c..c15d453 100644 --- a/src/main.ts +++ b/src/main.ts @@ -12,6 +12,7 @@ import { PushNotificationId } from "./server/socket/signalr/types.ts"; import { genericResponse } from "./util/api.ts"; import { getNetConfig } from "./net.ts"; import { TokenFormat, TokenType } from "./server/platforms/types.ts"; +import { Context } from "node:vm"; LoggingConfiguration.resetTimeFormat = TimeFormat.Unix; LoggingConfiguration.resetLogTiming = LogTiming.Microtask; @@ -48,7 +49,7 @@ await routeImporter(AppRoot.app, 'src/', [ ]); // deno-lint-ignore require-await -AppRoot.app.use('*', async c => { +AppRoot.app.use('*', async (c: Context) => { return c.json(genericResponse(false, "Resource Not Found"), 404); }); @@ -60,7 +61,11 @@ const onListen = async () => { await Deno.mkdir('persist'); await Promise.all(Object.values(Server).map(base => ((base as ServerContentBase).kvInit ? (base as ServerContentBase).kvInit() : undefined))); - Server.emit('server.start', undefined); + if (typeof Server == 'undefined') { + log.e(`SERVER LOCKUP: Server is undefined!`); + return; + } + Server.emit('server.start', { server: Server }); } const netConfig = getNetConfig(); @@ -92,7 +97,7 @@ const server = Deno.serve({ const splitHeader = authHeader.split(' ')[1]; if (!splitHeader) return unauthRes; - const secret = Deno.env.get('secret'); + const secret = Deno.env.get('SECRET'); if (!secret) { log.w(`No secret set!`); return unauthRes; @@ -140,6 +145,7 @@ const server = Deno.serve({ formatHeader(req.headers, 'Content-Type'), formatHeader(req.headers, 'Connection'), formatHeader(req.headers, 'User-Agent'), + formatHeader(req.headers, 'Accept-Encoding') ]); if (!logBlacklist.includes(url.pathname)) { if (res.status === 404) log.e(netlog); @@ -163,7 +169,7 @@ Deno.addSignalListener('SIGINT', () => { for (const socket of consoleSockets) socket.destroy(); for (const socket of gameSockets) socket.sendNotification(PushNotificationId.ModerationQuitGame); - Server.emit('server.destroy', undefined); + Server.emit('server.destroy', { server: Server }); }); Server.Commands.addRootCommand(new Command({ diff --git a/src/net.ts b/src/net.ts index 38b54eb..d80157b 100644 --- a/src/net.ts +++ b/src/net.ts @@ -1,8 +1,8 @@ export function getNetConfig() { return { - host: "10.0.1.39", + host: "127.0.0.1", port: 13370, - publicHost: "10.0.1.39:13370", + publicHost: "127.0.0.1:13370", securePublicHost: false } } \ No newline at end of file diff --git a/src/routes/api/routes/messages.ts b/src/routes/api/routes/messages.ts index 22f03f1..e931c17 100644 --- a/src/routes/api/routes/messages.ts +++ b/src/routes/api/routes/messages.ts @@ -1,3 +1,7 @@ import { createHonoRoute } from "../../../util/import.ts"; -export const route = createHonoRoute("/messages"); \ No newline at end of file +export const route = createHonoRoute("/messages"); + +route.app.get('/v2/get', c => { + return c.json([]); +}); \ No newline at end of file diff --git a/src/routes/api/routes/players.ts b/src/routes/api/routes/players.ts index d4a7f2a..71d7db8 100644 --- a/src/routes/api/routes/players.ts +++ b/src/routes/api/routes/players.ts @@ -1,7 +1,17 @@ +import z from "zod"; import { createHonoRoute } from "../../../util/import.ts"; +import { authenticate } from "../../../util/api.ts"; +import { typedZValidator } from "../../../util/validators.ts"; export const route = createHonoRoute("/players"); route.app.get('/v2/progression/bulk', c => { return c.json([]); // todo: progression +}); + +const getProgParamSchema = z.object({ + id: z.coerce.number() +}); +route.app.get('/v1/progression/:id', authenticate, typedZValidator('param', getProgParamSchema), async c => { + return c.json(await c.get('profile').Reputation.export()); }); \ No newline at end of file diff --git a/src/routes/api/routes/settings.ts b/src/routes/api/routes/settings.ts index 504d6c7..fe93467 100644 --- a/src/routes/api/routes/settings.ts +++ b/src/routes/api/routes/settings.ts @@ -1,8 +1,7 @@ import z from "zod"; import { authenticate, genericResponse } from "../../../util/api.ts"; import { createHonoRoute } from "../../../util/import.ts"; -import { transformStringToEnum, typedZValidator } from "../../../util/validators.ts"; -import { ProfileSetting } from "../../../server/profiles/content/Settings.ts"; +import { typedZValidator } from "../../../util/validators.ts"; import { HonoEnv } from "../../../util/types.ts"; import { Context } from "@hono/hono"; @@ -19,7 +18,7 @@ route.app.get('/v2/', getSettingsMiddleware); route.app.get('/v2', getSettingsMiddleware); const settingsSetSchema = z.object({ - Key: z.string().transform(transformStringToEnum(ProfileSetting, true)), + Key: z.string(), Value: z.string() }); route.app.post('/v2/set', typedZValidator('json', settingsSetSchema), async c => { diff --git a/src/server/ContentBase.ts b/src/server/ContentBase.ts index 0bf4ba5..fdb19ff 100644 --- a/src/server/ContentBase.ts +++ b/src/server/ContentBase.ts @@ -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() { diff --git a/src/server/instances/Instance.ts b/src/server/instances/Instance.ts index eb756e9..8de42c8 100644 --- a/src/server/instances/Instance.ts +++ b/src/server/instances/Instance.ts @@ -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 = 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; } } \ No newline at end of file diff --git a/src/server/instances/base.ts b/src/server/instances/base.ts index 7122851..0f71a95 100644 --- a/src/server/instances/base.ts +++ b/src/server/instances/base.ts @@ -58,6 +58,6 @@ export interface RoomInstance { export class InstanceManager extends ServerContentBase { - + } \ No newline at end of file diff --git a/src/server/profiles/content/Progression.ts b/src/server/profiles/content/Progression.ts new file mode 100644 index 0000000..f38f1b0 --- /dev/null +++ b/src/server/profiles/content/Progression.ts @@ -0,0 +1,5 @@ +import ProfileContentManager from "./base.ts"; + +export class ProfileProgressionManager extends ProfileContentManager { + +} // do this soon:tm: \ No newline at end of file diff --git a/src/server/profiles/content/Reputation.ts b/src/server/profiles/content/Reputation.ts index 35a325a..9869ed7 100644 --- a/src/server/profiles/content/Reputation.ts +++ b/src/server/profiles/content/Reputation.ts @@ -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 { + 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 + } + } + } \ No newline at end of file diff --git a/src/server/profiles/content/Settings.ts b/src/server/profiles/content/Settings.ts index 502c868..8fc7696 100644 --- a/src/server/profiles/content/Settings.ts +++ b/src/server/profiles/content/Settings.ts @@ -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 }); diff --git a/src/server/profiles/manager.ts b/src/server/profiles/manager.ts index d6cb2eb..ef57fec 100644 --- a/src/server/profiles/manager.ts +++ b/src/server/profiles/manager.ts @@ -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) diff --git a/src/server/rooms/base.ts b/src/server/rooms/base.ts new file mode 100644 index 0000000..efb9e35 --- /dev/null +++ b/src/server/rooms/base.ts @@ -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([this.#roomsKey, name]); + if (id.value == null) return null; + return id.value; + } + + async getFromName(name: string) { + + } + + async get(id: number) { + + } + +} \ No newline at end of file diff --git a/src/server/rooms/internal/DataTypes.ts b/src/server/rooms/internal/DataTypes.ts new file mode 100644 index 0000000..284fcc1 --- /dev/null +++ b/src/server/rooms/internal/DataTypes.ts @@ -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, + Subrooms: number[], + Tags: Set, + CoOwners: Set, + InvitedCoOwners: Set, + Hosts: Set, + InvitedHosts: Set, + Favorites: Set, + Cheers: Set +} + +export interface DatabaseSubroom { + +} + +export interface RoomUpdatedEvent { + room: RoomFactory +} + +export interface SubroomUpdatedEvent { + subroom: SubroomFactory +} + +export * as RoomDataTypes from "./DataTypes.ts"; \ No newline at end of file diff --git a/src/server/rooms/internal/RoomFactory.ts b/src/server/rooms/internal/RoomFactory.ts new file mode 100644 index 0000000..11e00db --- /dev/null +++ b/src/server/rooms/internal/RoomFactory.ts @@ -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"); + + + } + +} \ No newline at end of file diff --git a/src/server/server.ts b/src/server/server.ts index 9e1ecf3..3ec2822 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -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 { @@ -26,6 +31,7 @@ class ServerBase extends EventManager { 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(); diff --git a/src/serverevents.ts b/src/serverevents.ts new file mode 100644 index 0000000..c6eecc9 --- /dev/null +++ b/src/serverevents.ts @@ -0,0 +1,5 @@ +import { ServerBase } from "./server/server.ts"; + +export interface ServerUpdateEvent { + server: ServerBase +} \ No newline at end of file