forked from zombieb/galvanic-corrosion-rewrite
rooooms are mostly dooone, misc routes, instance management stubs, and matchmaking base
This commit is contained in:
@@ -5,19 +5,19 @@ export class EventManager<Events extends { [K in keyof Events]: unknown }> {
|
||||
[K in keyof Events]?: Set<Callback<Events[K]>>
|
||||
} = {};
|
||||
|
||||
on<K extends keyof Events>(eventName: K, callback: Callback<Events[K]>): void {
|
||||
on<K extends keyof Events>(eventName: K, cb: Callback<Events[K]>): void {
|
||||
if (!this.#listeners[eventName])
|
||||
this.#listeners[eventName] = new Set();
|
||||
this.#listeners[eventName]!.add(callback);
|
||||
this.#listeners[eventName]!.add(cb);
|
||||
}
|
||||
|
||||
off<K extends keyof Events>(eventName: K, callback: Callback<Events[K]>): void {
|
||||
this.#listeners[eventName]?.delete(callback);
|
||||
off<K extends keyof Events>(eventName: K, cb: Callback<Events[K]>): void {
|
||||
this.#listeners[eventName]?.delete(cb);
|
||||
if (this.#listeners[eventName]?.size === 0)
|
||||
delete this.#listeners[eventName];
|
||||
}
|
||||
|
||||
emit<K extends keyof Events>(eventName: K, payload: Events[K]): void {
|
||||
this.#listeners[eventName]?.forEach((callback) => callback(payload));
|
||||
this.#listeners[eventName]?.forEach(cb => cb(payload));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CloudRegionCode } from "../../util/photon.ts";
|
||||
import type Profile from "../profiles/profile.ts";
|
||||
import { RoomInstance, RoomLocation } from "./base.ts";
|
||||
import { RoomInstance, RoomLocation } from "./types.ts";
|
||||
|
||||
export interface InstanceCreationOptions {
|
||||
roomId: number,
|
||||
|
||||
@@ -1,63 +1,47 @@
|
||||
import Logging from "@proxnet/undead-logging";
|
||||
import { ServerContentBase } from "../ContentBase.ts";
|
||||
import { type Instance } from "./Instance.ts";
|
||||
|
||||
export enum RoomLocation {
|
||||
Calibration = "f5fbd9c9-e853-4036-9d48-5f68e861af04",
|
||||
DormRoom = "76d98498-60a1-430c-ab76-b54a29b7a163",
|
||||
RecCenter = "cbad71af-0831-44d8-b8ef-69edafa841f6",
|
||||
Charades = "4078dfed-24bb-4db7-863f-578ba48d726b",
|
||||
TheInkSpace = "1fa06e3c-c307-4c11-a91b-1fabcddb8a96",
|
||||
Paddleball = "d89f74fa-d51e-477a-a425-025a891dd499",
|
||||
GoldenTrophy = "91e16e35-f48f-4700-ab8a-a1b79e50e51b",
|
||||
Orientation = "c79709d8-a31b-48aa-9eb8-cc31ba9505e8",
|
||||
TheRiseofJumbotron = "acc06e66-c2d0-4361-b0cd-46246a4c455c",
|
||||
CrimsonCauldron = "949fa41f-4347-45c0-b7ac-489129174045",
|
||||
IsleOfLostSkulls = "7e01cfe0-820a-406f-b1b3-0a5bf575235c",
|
||||
RecRoyaleSquads = "253fa009-6e65-4c90-91a1-7137a56a267f",
|
||||
RecRoyaleSolos = "b010171f-4875-4e89-baba-61e878cd41e1",
|
||||
Lounge = "a067557f-ca32-43e6-b6e5-daaec60b4f5a",
|
||||
PerformanceHall = "9932f88f-3929-43a0-a012-a40b5128e346",
|
||||
MakerRoom = "a75f7547-79eb-47c6-8986-6767abcb4f92",
|
||||
Park = "0a864c86-5a71-4e18-8041-8124e4dc9d98",
|
||||
Lake = "f6f7256c-e438-4299-b99e-d20bef8cf7e0",
|
||||
PropulsionTestRange = "d9378c9f-80bc-46fb-ad1e-1bed8a674f55",
|
||||
Gym = "3d474b26-26f7-45e9-9a36-9b02847d5e6f",
|
||||
Stadium = "6d5eea4b-f069-4ed0-9916-0e2f07df0d03",
|
||||
Hangar = "239e676c-f12f-489f-bf3a-d4c383d692c3",
|
||||
CyberJunkCity = "9d6456ce-6264-48b4-808d-2d96b3d91038",
|
||||
Crescendo = "49cb8993-a956-43e2-86f4-1318f279b22a",
|
||||
BowlingAlley = "ae929543-9a07-41d5-8ee9-dbbee8c36800",
|
||||
AnimationRecordingStudio = "a95c349c-0f96-4c2d-a4c8-4969ffa8ea44",
|
||||
StuntRunner = "b7281665-a715-4051-826b-8e08e69c6172",
|
||||
TheMainEvent = "3a636bd2-f896-424c-9225-c184522c0d87",
|
||||
StuntRunnerBaseRoom = "882e9b96-7115-4b03-86f6-c0c9d8e22e00",
|
||||
Registration = "cf61556d-68fd-4288-9ae5-7a512621e569",
|
||||
ARRoom = "bf268f5f-b55b-41af-8628-32fa4b5d70b6",
|
||||
PaintballRiver = "e122fe98-e7db-49e8-a1b1-105424b6e1f0",
|
||||
PaintballHomestead = "a785267d-c579-42ea-be43-fec1992d1ca7",
|
||||
PaintballQuarry = "ff4c6427-7079-4f59-b22a-69b089420827",
|
||||
PaintballClearcut = "380d18b5-de9c-49f3-80f7-f4a95c1de161",
|
||||
PaintballSpillway = "58763055-2dfb-4814-80b8-16fac5c85709",
|
||||
PaintballDriveIn = "65ddbb48-5a01-4e3e-972d-e5c7419e2bc3",
|
||||
}
|
||||
|
||||
export interface RoomInstance {
|
||||
roomInstanceId: number,
|
||||
roomId: number,
|
||||
subRoomId: number,
|
||||
location: RoomLocation,
|
||||
name: string,
|
||||
maxCapacity: number,
|
||||
isFull: boolean,
|
||||
isPrivate: boolean,
|
||||
isInProgress: boolean,
|
||||
photonRegionId: string,
|
||||
photonRoomId: string,
|
||||
dataBlob?: string,
|
||||
eventId?: number
|
||||
}
|
||||
const log = new Logging("Instances");
|
||||
|
||||
export class InstanceManager extends ServerContentBase {
|
||||
|
||||
#instances: Set<Instance> = new Set();
|
||||
|
||||
clearEmptyInstances() {
|
||||
log.i(`Starting instance purge\n Before: ${
|
||||
this.#instances.size
|
||||
} instances, ${
|
||||
this.#instances.values().reduce((prev, current) => prev + current.getPlayers().length, 0)
|
||||
} players`);
|
||||
|
||||
return new Promise(() => {
|
||||
for (const inst of this.#instances) {
|
||||
if (inst.getPlayers().length === 0) this.deleteInstance(inst);
|
||||
}
|
||||
|
||||
log.i(`Instance purge complete\n After: ${
|
||||
this.#instances.size
|
||||
} instances, ${
|
||||
this.#instances.values().reduce((prev, current) => prev + current.getPlayers().length, 0)
|
||||
} players`);
|
||||
});
|
||||
}
|
||||
|
||||
getAllInstances() {
|
||||
|
||||
}
|
||||
|
||||
registerInstance(inst: Instance) {
|
||||
|
||||
}
|
||||
|
||||
getInstance(id: number) {
|
||||
|
||||
}
|
||||
|
||||
deleteInstance(inst: Instance) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
55
src/server/instances/types.ts
Normal file
55
src/server/instances/types.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
export enum RoomLocation {
|
||||
Calibration = "f5fbd9c9-e853-4036-9d48-5f68e861af04",
|
||||
DormRoom = "76d98498-60a1-430c-ab76-b54a29b7a163",
|
||||
RecCenter = "cbad71af-0831-44d8-b8ef-69edafa841f6",
|
||||
Charades = "4078dfed-24bb-4db7-863f-578ba48d726b",
|
||||
TheInkSpace = "1fa06e3c-c307-4c11-a91b-1fabcddb8a96",
|
||||
Paddleball = "d89f74fa-d51e-477a-a425-025a891dd499",
|
||||
GoldenTrophy = "91e16e35-f48f-4700-ab8a-a1b79e50e51b",
|
||||
Orientation = "c79709d8-a31b-48aa-9eb8-cc31ba9505e8",
|
||||
TheRiseofJumbotron = "acc06e66-c2d0-4361-b0cd-46246a4c455c",
|
||||
CrimsonCauldron = "949fa41f-4347-45c0-b7ac-489129174045",
|
||||
IsleOfLostSkulls = "7e01cfe0-820a-406f-b1b3-0a5bf575235c",
|
||||
RecRoyaleSquads = "253fa009-6e65-4c90-91a1-7137a56a267f",
|
||||
RecRoyaleSolos = "b010171f-4875-4e89-baba-61e878cd41e1",
|
||||
Lounge = "a067557f-ca32-43e6-b6e5-daaec60b4f5a",
|
||||
PerformanceHall = "9932f88f-3929-43a0-a012-a40b5128e346",
|
||||
MakerRoom = "a75f7547-79eb-47c6-8986-6767abcb4f92",
|
||||
Park = "0a864c86-5a71-4e18-8041-8124e4dc9d98",
|
||||
Lake = "f6f7256c-e438-4299-b99e-d20bef8cf7e0",
|
||||
PropulsionTestRange = "d9378c9f-80bc-46fb-ad1e-1bed8a674f55",
|
||||
Gym = "3d474b26-26f7-45e9-9a36-9b02847d5e6f",
|
||||
Stadium = "6d5eea4b-f069-4ed0-9916-0e2f07df0d03",
|
||||
Hangar = "239e676c-f12f-489f-bf3a-d4c383d692c3",
|
||||
CyberJunkCity = "9d6456ce-6264-48b4-808d-2d96b3d91038",
|
||||
Crescendo = "49cb8993-a956-43e2-86f4-1318f279b22a",
|
||||
BowlingAlley = "ae929543-9a07-41d5-8ee9-dbbee8c36800",
|
||||
AnimationRecordingStudio = "a95c349c-0f96-4c2d-a4c8-4969ffa8ea44",
|
||||
StuntRunner = "b7281665-a715-4051-826b-8e08e69c6172",
|
||||
TheMainEvent = "3a636bd2-f896-424c-9225-c184522c0d87",
|
||||
StuntRunnerBaseRoom = "882e9b96-7115-4b03-86f6-c0c9d8e22e00",
|
||||
Registration = "cf61556d-68fd-4288-9ae5-7a512621e569",
|
||||
ARRoom = "bf268f5f-b55b-41af-8628-32fa4b5d70b6",
|
||||
PaintballRiver = "e122fe98-e7db-49e8-a1b1-105424b6e1f0",
|
||||
PaintballHomestead = "a785267d-c579-42ea-be43-fec1992d1ca7",
|
||||
PaintballQuarry = "ff4c6427-7079-4f59-b22a-69b089420827",
|
||||
PaintballClearcut = "380d18b5-de9c-49f3-80f7-f4a95c1de161",
|
||||
PaintballSpillway = "58763055-2dfb-4814-80b8-16fac5c85709",
|
||||
PaintballDriveIn = "65ddbb48-5a01-4e3e-972d-e5c7419e2bc3"
|
||||
}
|
||||
|
||||
export interface RoomInstance {
|
||||
roomInstanceId: number;
|
||||
roomId: number;
|
||||
subRoomId: number;
|
||||
location: RoomLocation;
|
||||
name: string;
|
||||
maxCapacity: number;
|
||||
isFull: boolean;
|
||||
isPrivate: boolean;
|
||||
isInProgress: boolean;
|
||||
photonRegionId: string;
|
||||
photonRoomId: string;
|
||||
dataBlob?: string;
|
||||
eventId?: number;
|
||||
}
|
||||
7
src/server/matchmaking/base.ts
Normal file
7
src/server/matchmaking/base.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { ServerContentBase } from "../ContentBase.ts";
|
||||
|
||||
export class ServerMatchmakingBase extends ServerContentBase {
|
||||
|
||||
|
||||
|
||||
}
|
||||
36
src/server/matchmaking/types.ts
Normal file
36
src/server/matchmaking/types.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
enum MatchmakingErrorCode {
|
||||
Success,
|
||||
NoSuchGame,
|
||||
PlayerNotOnline,
|
||||
InsufficientSpace,
|
||||
EventNotStarted,
|
||||
EventAlreadyFinished,
|
||||
EventCreatorNotReady,
|
||||
BlockedFromRoom,
|
||||
ProfileLocked,
|
||||
NoBirthday,
|
||||
MarkedForDelete,
|
||||
JuniorNotAllowed,
|
||||
Banned,
|
||||
AlreadyInBestInstance,
|
||||
InsufficientRelationship,
|
||||
UpdateRequired = 16,
|
||||
AlreadyInTargetInstance,
|
||||
RegistrationRequired,
|
||||
UGCNotAllowed,
|
||||
NoSuchRoom,
|
||||
RoomCreatorNotReady,
|
||||
RoomIsNotActive,
|
||||
RoomBlockedByCreator,
|
||||
RoomBlockingCreator,
|
||||
RoomIsPrivate,
|
||||
RoomInstanceIsPrivate,
|
||||
DeviceClassNotSupported = 30,
|
||||
DeviceClassNotSupportedByRoomOwner,
|
||||
VRMovementModeNotSupportedByRoomOwner,
|
||||
EventIsPrivate = 35,
|
||||
RoomInviteExpired = 40,
|
||||
NoAvailableRegion = 45,
|
||||
NotorietyTooPoor = 50,
|
||||
BannedFromRoom = 55
|
||||
}
|
||||
101
src/server/objectives/base.ts
Normal file
101
src/server/objectives/base.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { ServerContentBase } from "../ContentBase.ts";
|
||||
|
||||
export enum ObjectiveType {
|
||||
Default = -1,
|
||||
FirstSessionOfDay = 1,
|
||||
AddAFriend,
|
||||
PartyUp,
|
||||
AllOtherChallenges,
|
||||
LevelUp,
|
||||
CheerAPlayer,
|
||||
PointedAtPlayer,
|
||||
CheerARoom,
|
||||
SubscribeToPlayer,
|
||||
DailyObjective1,
|
||||
DailyObjective2,
|
||||
DailyObjective3,
|
||||
AllDailyObjectives,
|
||||
CompleteAnyDaily,
|
||||
CompleteAnyWeekly,
|
||||
OOBE_GoToLockerRoom = 20,
|
||||
OOBE_GoToActivity,
|
||||
OOBE_FinishActivity,
|
||||
NUX_PunchcardObjective = 25,
|
||||
NUX_AllPunchcardObjectives,
|
||||
GoToRecCenter = 30,
|
||||
FinishActivity,
|
||||
VisitACustomRoom,
|
||||
CreateACustomRoom,
|
||||
ScoreBasketInRecCenter = 35,
|
||||
UploadPhotoToRecNet,
|
||||
UpdatePlayerBio,
|
||||
SaveOutfitSlot,
|
||||
PurchaseClothingItem,
|
||||
PurchaseNonClothingItem,
|
||||
CharadesGames = 100,
|
||||
CharadesWinsPerformer,
|
||||
CharadesWinsGuesser,
|
||||
DiscGolfWins = 200,
|
||||
DiscGolfGames,
|
||||
DiscGolfHolesUnderPar,
|
||||
DodgeballWins = 300,
|
||||
DodgeballGames,
|
||||
DodgeballHits,
|
||||
PaddleballGames = 400,
|
||||
PaddleballWins,
|
||||
PaddleballScores,
|
||||
PaintballAnyModeGames = 500,
|
||||
PaintballAnyModeWins,
|
||||
PaintballAnyModeHits,
|
||||
PaintballCTFWins = 600,
|
||||
PaintballCTFGames,
|
||||
PaintballCTFHits,
|
||||
PaintballFlagCaptures,
|
||||
PaintballTeamBattleWins = 700,
|
||||
PaintballTeamBattleGames,
|
||||
PaintballTeamBattleHits,
|
||||
PaintballFreeForAllWins = 710,
|
||||
PaintballFreeForAllGames,
|
||||
PaintballFreeForAllHits,
|
||||
SoccerWins = 800,
|
||||
SoccerGames,
|
||||
SoccerGoals,
|
||||
QuestGames = 1000,
|
||||
QuestWins,
|
||||
QuestPlayerRevives,
|
||||
QuestEnemyKills,
|
||||
QuestGames_Goblin1 = 1010,
|
||||
QuestWins_Goblin1,
|
||||
QuestPlayerRevives_Goblin1,
|
||||
QuestEnemyKills_Goblin1,
|
||||
QuestGames_Goblin2 = 1020,
|
||||
QuestWins_Goblin2,
|
||||
QuestPlayerRevives_Goblin2,
|
||||
QuestEnemyKills_Goblin2,
|
||||
QuestGames_Scifi1 = 1030,
|
||||
QuestWins_Scifi1,
|
||||
QuestPlayerRevives_Scifi1,
|
||||
QuestEnemyKills_Scifi1,
|
||||
QuestGames_Pirate1 = 1040,
|
||||
QuestWins_Pirate1,
|
||||
QuestPlayerRevives_Pirate1,
|
||||
QuestEnemyKills_Pirate1,
|
||||
ArenaGames = 2000,
|
||||
ArenaWins,
|
||||
ArenaPlayerRevives,
|
||||
ArenaHeroTags,
|
||||
ArenaBotTags,
|
||||
RecRoyaleGames = 3000,
|
||||
RecRoyaleWins,
|
||||
RecRoyaleTags,
|
||||
}
|
||||
|
||||
export type Objective = {
|
||||
type: ObjectiveType;
|
||||
score: number;
|
||||
}
|
||||
export class ServerObjectivesManager extends ServerContentBase {
|
||||
|
||||
// asdf
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import z from "zod";
|
||||
import Command from "../commands/command.ts";
|
||||
import { ServerContentBase } from "../ContentBase.ts";
|
||||
import { transformStringToEnum } from "../../util/validators.ts";
|
||||
import { transformCheckEnum, transformStringToEnum } from "../../util/validators.ts";
|
||||
import { sign } from "@hono/hono/jwt";
|
||||
import { CachedLogin, DbCachedLogin, PlatformMask, PlatformType, TokenFormat, TokenType } from "./types.ts";
|
||||
|
||||
@@ -127,7 +127,7 @@ export class PlatformsManager extends ServerContentBase {
|
||||
return await this.getCachedLogins(type, platformId, false);
|
||||
},
|
||||
zod: z.tuple([
|
||||
z.string().transform(transformStringToEnum<PlatformType>(PlatformType)),
|
||||
z.coerce.number().transform(transformCheckEnum<PlatformType>(PlatformType)),
|
||||
z.string().min(4)
|
||||
]),
|
||||
help: 'List all cachedlogins for platformId: <type: PlatformType, platformId: string>'
|
||||
|
||||
@@ -13,7 +13,6 @@ export interface TokenFormat extends TokenFormatBase {
|
||||
|
||||
export enum ProfileRole {
|
||||
Developer = 'developer',
|
||||
Moderator = 'moderator',
|
||||
Web = 'webClient',
|
||||
Game = 'gameClient'
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ServerContentBase } from "../ContentBase.ts";
|
||||
import { DeviceClass } from "../platforms/types.ts";
|
||||
import Profile from "../profiles/profile.ts";
|
||||
import { type ServerBase } from "../server.ts";
|
||||
import { RoomInstance } from "../instances/base.ts";
|
||||
import { RoomInstance } from "../instances/types.ts";
|
||||
|
||||
export enum VRMovementMode {
|
||||
TELEPORT,
|
||||
|
||||
@@ -15,11 +15,8 @@ export class ProfileMatchmakingManager extends ProfileContentManager {
|
||||
async setLoginLock(lock: string) {
|
||||
await this.kv.getKv().set(this.#deviceClassKey, lock);
|
||||
}
|
||||
async getLoginLock(): Promise<string> {
|
||||
return (await this.kv.getKv().get<string>(this.#loginLockKey)).value || "";
|
||||
}
|
||||
async hasLoginLock() {
|
||||
return (await this.kv.getKv().get<string>(this.#loginLockKey)).value ? true : false;
|
||||
async getLoginLock(): Promise<string | null> {
|
||||
return (await this.kv.getKv().get<string>(this.#loginLockKey)).value;
|
||||
}
|
||||
|
||||
#lastSeen = this.profile.constructProfilePropertyKey('lastseen');
|
||||
|
||||
@@ -37,6 +37,7 @@ interface MessageBase {
|
||||
|
||||
export class ProfileMessageManager extends ProfileContentManager {
|
||||
|
||||
// temp
|
||||
// deno-lint-ignore require-await
|
||||
async getMessages() {
|
||||
return [];
|
||||
|
||||
@@ -2,4 +2,13 @@ import ProfileContentManager from "./base.ts";
|
||||
|
||||
export class ProfileProgressionManager extends ProfileContentManager {
|
||||
|
||||
// deno-lint-ignore require-await
|
||||
async get() {
|
||||
return {
|
||||
PlayerId: this.profile.getId(),
|
||||
Level: 1,
|
||||
XP: 0
|
||||
}
|
||||
}
|
||||
|
||||
} // do this soon:tm:
|
||||
23
src/server/profiles/content/Rooms.ts
Normal file
23
src/server/profiles/content/Rooms.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import ProfileContentManager from "./base.ts";
|
||||
|
||||
export class ProfileRoomsManager extends ProfileContentManager {
|
||||
|
||||
#roomsKey = this.profile.constructProfilePropertyKey('rooms');
|
||||
#rooms: Set<number> = new Set();
|
||||
|
||||
async #write() {
|
||||
await this.kv.getKv().set(this.#roomsKey, this.#rooms);
|
||||
}
|
||||
async addRoom(id: number) {
|
||||
this.#rooms.add(id);
|
||||
await this.#write();
|
||||
}
|
||||
async delRoom(id: number) {
|
||||
this.#rooms.delete(id);
|
||||
await this.#write();
|
||||
}
|
||||
getRooms() {
|
||||
return this.#rooms;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,6 +40,7 @@ export class ProfileSettingsManager extends ProfileContentManager {
|
||||
if (!s) settings.push({ Key, Value });
|
||||
else s.Value = Value;
|
||||
await this.#updateSettings(settings);
|
||||
this.server.emit('profile.setting.update', { profile: this.profile, key: Key, value: Value });
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
import type KV from "../../persistence/kv.ts";
|
||||
import { type ServerBase } from "../../server.ts";
|
||||
import type Profile from "../profile.ts";
|
||||
|
||||
class ProfileContentManager {
|
||||
|
||||
protected profile: Profile;
|
||||
protected kv: KV;
|
||||
constructor(profile: Profile, kv: KV) {
|
||||
protected server: ServerBase
|
||||
constructor(server: ServerBase, profile: Profile, kv: KV) {
|
||||
this.profile = profile;
|
||||
this.kv = kv;
|
||||
this.server = server;
|
||||
profile.managers.push(this);
|
||||
}
|
||||
|
||||
|
||||
7
src/server/profiles/events/ProfileUpdatedSetting.ts
Normal file
7
src/server/profiles/events/ProfileUpdatedSetting.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type Profile from "../profile.ts";
|
||||
|
||||
export interface ProfileUpdatedSettingEvent {
|
||||
profile: Profile,
|
||||
key: string,
|
||||
value: string
|
||||
}
|
||||
@@ -16,6 +16,8 @@ class ProfileManagerBase extends ServerContentBase {
|
||||
|
||||
#log = new Logging("ProfileManager");
|
||||
|
||||
#logSettingChanges = false;
|
||||
|
||||
async #getUnusedId() {
|
||||
let id = Math.round(Math.random() * 2_147_483_647);
|
||||
if (await this.get(id)) id = await this.#getUnusedId();
|
||||
@@ -78,7 +80,7 @@ class ProfileManagerBase extends ServerContentBase {
|
||||
async getAll() {
|
||||
const keys = this.kv.getKv().list({ prefix: [ ProfileManagerBase.profilesKey ] });
|
||||
const awaitedKeys = await Array.fromAsync(keys);
|
||||
return awaitedKeys.map(entry => entry.key).map(val => val[1]).filter(val => typeof val == 'number');
|
||||
return awaitedKeys.map(entry => entry.key).filter(key => key.length == 2).map(val => val[1]).filter(val => typeof val == 'number');
|
||||
}
|
||||
|
||||
async getByUsername(username: string) {
|
||||
@@ -88,9 +90,24 @@ class ProfileManagerBase extends ServerContentBase {
|
||||
}
|
||||
|
||||
override start() {
|
||||
this.server.on('profile.setting.update', ev => {
|
||||
if (this.#logSettingChanges) this.#log.i(`Profile ${ev.profile} "${ev.profile.getUsername()}" settings update\n Key: "${ev.key}"\n Value: "${ev.value}"`);
|
||||
});
|
||||
|
||||
this.server.Commands.addRootCommand(new Command({
|
||||
key: ['account', 'profile', 'acc', 'prof'],
|
||||
key: ['account', 'profile', 'acc', 'prof', "profiles"],
|
||||
subcommands: [
|
||||
new Command({
|
||||
key: ['settingevent', 'se', 'logsettings'],
|
||||
exec: (val: boolean) => {
|
||||
this.#logSettingChanges = val;
|
||||
return val;
|
||||
},
|
||||
zod: z.tuple([
|
||||
z.stringbool()
|
||||
]),
|
||||
help: 'Log changes to profile settings',
|
||||
}),
|
||||
new Command({
|
||||
key: ['get', 'g', 'fetch', 'f'],
|
||||
exec: async (id: number) => {
|
||||
@@ -104,7 +121,7 @@ class ProfileManagerBase extends ServerContentBase {
|
||||
help: 'Fetch a profile: <id: number>'
|
||||
}),
|
||||
new Command({
|
||||
key: ['getall', 'listall', 'fetchall', 'all', 'a'],
|
||||
key: ['getall', 'listall', 'fetchall', 'all', 'a', "list"],
|
||||
exec: async () => {
|
||||
const ids = await this.getAll();
|
||||
return ids;
|
||||
@@ -132,10 +149,20 @@ class ProfileManagerBase extends ServerContentBase {
|
||||
else return await profile.setRole(role);
|
||||
},
|
||||
zod: z.tuple([
|
||||
z.string().transform(Number),
|
||||
z.coerce.number(),
|
||||
z.string()
|
||||
]),
|
||||
help: 'Set the profile role: <id: number, role: "developer" | "moderator" | "screenshare" | "user">'
|
||||
help: 'Set the profile role: <id: number, role: "gameClient" | "webClient" | "developer">'
|
||||
}),
|
||||
new Command({
|
||||
key: ['settings', 'setting'],
|
||||
exec: async (id: number) => {
|
||||
const profile = await this.get(id);
|
||||
if (profile) return await profile.Settings.getAllSettings();
|
||||
else return null;
|
||||
},
|
||||
zod: z.tuple([z.coerce.number()]),
|
||||
help: "Get player settings"
|
||||
})
|
||||
]
|
||||
}));
|
||||
|
||||
@@ -7,7 +7,9 @@ import { ProfileAvatarManager } from "./content/Avatar.ts";
|
||||
import ProfileContentManager from "./content/base.ts";
|
||||
import { ProfileMatchmakingManager } from "./content/Matchmaking.ts";
|
||||
import { ProfileMessageManager } from "./content/Messages.ts";
|
||||
import { ProfileProgressionManager } from "./content/Progression.ts";
|
||||
import { ProfileReputationManager } from "./content/Reputation.ts";
|
||||
import { ProfileRoomsManager } from "./content/Rooms.ts";
|
||||
import { ProfileSettingsManager } from "./content/Settings.ts";
|
||||
import { ProfileSubscriptionsManager } from "./content/Subscriptions.ts";
|
||||
import ProfileManagerBase from "./manager.ts";
|
||||
@@ -32,6 +34,8 @@ class Profile {
|
||||
Reputation: ProfileReputationManager;
|
||||
Subscriptions: ProfileSubscriptionsManager;
|
||||
Messages: ProfileMessageManager;
|
||||
Rooms: ProfileRoomsManager;
|
||||
Progression: ProfileProgressionManager;
|
||||
|
||||
constructor(acc: SelfAccount, kv: KV, server: ServerBase) {
|
||||
this.#id = acc.accountId;
|
||||
@@ -39,12 +43,14 @@ class Profile {
|
||||
this.#kv = kv;
|
||||
this.#server = server;
|
||||
|
||||
this.Settings = new ProfileSettingsManager(this, this.#kv);
|
||||
this.Avatar = new ProfileAvatarManager(this, this.#kv);
|
||||
this.Matchmaking = new ProfileMatchmakingManager(this, this.#kv);
|
||||
this.Reputation = new ProfileReputationManager(this, this.#kv);
|
||||
this.Subscriptions = new ProfileSubscriptionsManager(this, this.#kv);
|
||||
this.Messages = new ProfileMessageManager(this, this.#kv);
|
||||
this.Settings = new ProfileSettingsManager(server, this, this.#kv);
|
||||
this.Avatar = new ProfileAvatarManager(server, this, this.#kv);
|
||||
this.Matchmaking = new ProfileMatchmakingManager(server, this, this.#kv);
|
||||
this.Reputation = new ProfileReputationManager(server, this, this.#kv);
|
||||
this.Subscriptions = new ProfileSubscriptionsManager(server, this, this.#kv);
|
||||
this.Messages = new ProfileMessageManager(server, this, this.#kv);
|
||||
this.Rooms = new ProfileRoomsManager(server, this, this.#kv);
|
||||
this.Progression = new ProfileProgressionManager(server, this, this.#kv);
|
||||
}
|
||||
|
||||
async #saveSelfAcc() {
|
||||
|
||||
@@ -7,8 +7,9 @@ import { FactoryMode, HardwareSupports, RoomDataTypes, WriteMode } from "./inter
|
||||
import { AGRoom, AGRoomLocation, AGRoomRuntimeConfig } from "./internal/ClientRoomTypes.ts";
|
||||
import Command from "../commands/command.ts";
|
||||
import z from "zod";
|
||||
import { RoomLocation } from "../instances/base.ts";
|
||||
import { RoomLocation } from "../instances/types.ts";
|
||||
|
||||
const roomIdSchema = z.coerce.number().min(1).max(Math.pow(2, 31));
|
||||
export class ServerRoomsBase extends ServerContentBase {
|
||||
|
||||
#subroomKv = new KV('subrooms', true);
|
||||
@@ -54,12 +55,29 @@ export class ServerRoomsBase extends ServerContentBase {
|
||||
}),
|
||||
new Command({
|
||||
key: ["getplayerdorm", "playerdorm", "pd", "dorm"],
|
||||
zod: z.tuple([z.coerce.number().min(1).max(Math.pow(2, 31))]),
|
||||
zod: z.tuple([roomIdSchema]),
|
||||
exec: async (playerId: number) => {
|
||||
const factory = await this.getPlayerDorm(this.server.Profiles.get(playerId))
|
||||
const profile = await this.server.Profiles.get(playerId);
|
||||
if (!profile) return new Error("Profile does not exist");
|
||||
const factory = await this.getPlayerDorm(profile);
|
||||
if (factory) return await factory.export();
|
||||
else return null;
|
||||
},
|
||||
help: "Get the domroom information for a certain profile/player"
|
||||
})
|
||||
}),
|
||||
new Command({
|
||||
key: ['get', 'g'],
|
||||
zod: z.tuple([roomIdSchema, z.boolean().optional()]),
|
||||
exec: async (roomId: number, details?: boolean) => {
|
||||
const factory = await this.get(roomId);
|
||||
if (factory) {
|
||||
if (details) return await factory.export();
|
||||
else return (await factory.export()).Room;
|
||||
}
|
||||
else return null;
|
||||
},
|
||||
help: "Get room [details]"
|
||||
}),
|
||||
],
|
||||
}))
|
||||
}
|
||||
@@ -89,9 +107,9 @@ export class ServerRoomsBase extends ServerContentBase {
|
||||
}
|
||||
|
||||
const supportsVRLow = room.Scenes.map(scene => locations.find(loc => loc.ReplicationId == scene.RoomSceneLocationId))
|
||||
.filter(val => typeof val !== 'undefined').some(loc => loc.SupportsVRLow) ?? false;
|
||||
.filter(val => typeof val !== 'undefined').some(loc => loc.SupportsVRLow) ?? false;
|
||||
const supportsMobile = room.Scenes.map(scene => locations.find(loc => loc.ReplicationId == scene.RoomSceneLocationId))
|
||||
.filter(val => typeof val !== 'undefined').some(loc => loc.SupportsMobile) ?? false;
|
||||
.filter(val => typeof val !== 'undefined').some(loc => loc.SupportsMobile) ?? false;
|
||||
|
||||
roomFactory.Name = room.Name;
|
||||
roomFactory.Description = room.Description;
|
||||
@@ -150,10 +168,10 @@ export class ServerRoomsBase extends ServerContentBase {
|
||||
}
|
||||
|
||||
async getIdFromName(name: string) {
|
||||
const id = await this.kv.getKv().get<number>([ServerRoomsBase.roomNamesKey, name]);
|
||||
if (id.value == null) return null;
|
||||
return id.value;
|
||||
}
|
||||
const id = await this.kv.getKv().get<number>([ServerRoomsBase.roomNamesKey, name]);
|
||||
if (id.value == null) return null;
|
||||
return id.value;
|
||||
}
|
||||
|
||||
async getPlayerDorm(profile: Profile) {
|
||||
const id = await this.kv.getKv().get<number>([ServerRoomsBase.playerDormsKey, profile.getId()]);
|
||||
@@ -178,21 +196,24 @@ export class ServerRoomsBase extends ServerContentBase {
|
||||
});
|
||||
|
||||
await roomFactory.addHardwareSupport(...HardwareSupports);
|
||||
roomFactory.setTags(new Set(["dormroom"]));
|
||||
|
||||
const subroomFactory = await roomFactory.newSubroom();
|
||||
subroomFactory.CanMatchmakeInto = true;
|
||||
subroomFactory.IsSandbox = true;
|
||||
subroomFactory.MaxPlayers = 4;
|
||||
subroomFactory.Name = "Home";
|
||||
subroomFactory.RoomId = roomFactory.getRoomId();
|
||||
subroomFactory.RoomSceneLocationId = RoomLocation.DormRoom;
|
||||
subroomFactory.setSubroomProperties({
|
||||
CanMatchmakeInto: true,
|
||||
IsSandbox: true,
|
||||
MaxPlayers: 4,
|
||||
Name: "Home",
|
||||
RoomId: roomFactory.getRoomId(),
|
||||
RoomSceneLocationId: RoomLocation.DormRoom
|
||||
});
|
||||
subroomFactory.addSave("");
|
||||
roomFactory.addSubroom(subroomFactory.RoomSceneId);
|
||||
|
||||
await subroomFactory.write();
|
||||
await roomFactory.write();
|
||||
|
||||
await profile.Rooms.addRoom(roomFactory.getRoomId());
|
||||
|
||||
return roomFactory;
|
||||
}
|
||||
else return await new RoomFactory(this.server, this.kv).init({ mode: FactoryMode.Fetch, id: id.value });
|
||||
|
||||
@@ -149,14 +149,16 @@ export interface DatabaseRoom {
|
||||
Room: DatabaseRoomContent
|
||||
}
|
||||
|
||||
export interface DatabaseSubroom {
|
||||
export interface SubroomProps {
|
||||
RoomId: number,
|
||||
RoomSceneLocationId: string,
|
||||
Name: string,
|
||||
IsSandbox: boolean,
|
||||
MaxPlayers: number,
|
||||
CanMatchmakeInto: boolean,
|
||||
LatestSaveId: number | null,
|
||||
}
|
||||
export interface DatabaseSubroom extends SubroomProps {
|
||||
LatestSaveId: number | null
|
||||
}
|
||||
|
||||
export type RoomSaveMap = Map<number, RoomSave>
|
||||
|
||||
@@ -133,6 +133,7 @@ export class RoomFactory {
|
||||
this.#kv.getKv().set([RoomFactory.roomsKey, this.#roomId, 'meta'], this.#obj),
|
||||
this.#kv.getKv().set([RoomFactory.roomsKey, this.#roomId, 'hardware'], this.#hardwareSupport),
|
||||
this.#kv.getKv().set([RoomFactory.roomsKey, this.#roomId, 'tags'], this.#tags),
|
||||
this.#kv.getKv().set([RoomFactory.roomsKey, this.#roomId, "subrooms"], this.#subrooms)
|
||||
]);
|
||||
|
||||
if (!this.IsDormRoom) this.#kv.getKv().set([ServerRoomsBase.roomNamesKey, this.Name], this.#roomId);
|
||||
|
||||
@@ -83,6 +83,10 @@ export class SubroomFactory {
|
||||
}
|
||||
}
|
||||
|
||||
setSubroomProperties(props: RoomDataTypes.SubroomProps) {
|
||||
Object.assign(this, props);
|
||||
}
|
||||
|
||||
get RoomId() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.RoomId; }
|
||||
set RoomId(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else this.#obj.RoomId = data }
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getNetConfig } from "../net.ts";
|
||||
import { ServerUpdateEvent } from "../serverevents.ts";
|
||||
import { AvatarContentBase } from "./avatars/base.ts";
|
||||
import { EventManager } from "./baseevent.ts";
|
||||
@@ -5,9 +6,11 @@ import { CommandsBase } from "./commands/commands.ts";
|
||||
import { ServerContentManager } from "./content/base.ts";
|
||||
import GameConfigsBase from "./gameconfigs/base.ts";
|
||||
import { InstanceManager } from "./instances/base.ts";
|
||||
import { Objective, ObjectiveType } from "./objectives/base.ts";
|
||||
import { PlatformsManager } from "./platforms/base.ts";
|
||||
import { type PresenceUpdateEvent } from "./presence/events/PresenceUpdateEvent.ts";
|
||||
import { type ProfileUpdateEvent } from "./profiles/events/ProfileUpdate.ts";
|
||||
import { ProfileUpdatedSettingEvent } from "./profiles/events/ProfileUpdatedSetting.ts";
|
||||
import { type RoleUpdateEvent } from "./profiles/events/RoleUpdate.ts";
|
||||
import ProfileManagerBase from "./profiles/manager.ts";
|
||||
import { ServerRoomsBase } from "./rooms/base.ts";
|
||||
@@ -16,6 +19,7 @@ import { RoomUpdatedEvent, SubroomUpdatedEvent } from "./rooms/internal/RoomEven
|
||||
interface ServerEvents {
|
||||
'profile.roleupdate': RoleUpdateEvent,
|
||||
'profile.update': ProfileUpdateEvent,
|
||||
'profile.setting.update': ProfileUpdatedSettingEvent,
|
||||
'presence.update': PresenceUpdateEvent,
|
||||
'server.start': ServerUpdateEvent,
|
||||
'server.destroy': ServerUpdateEvent,
|
||||
@@ -23,6 +27,31 @@ interface ServerEvents {
|
||||
'room.subroom.updated': SubroomUpdatedEvent
|
||||
}
|
||||
|
||||
export interface LevelProgressionItem {
|
||||
Level: number;
|
||||
RequiredXp: number;
|
||||
};
|
||||
interface AutoMicMutingConfig {
|
||||
MicSpamVolumeThreshold: number;
|
||||
MicVolumeSampleInterval: number;
|
||||
MicVolumeSampleRollingWindowLength: number;
|
||||
MicSpamSamplePercentageForWarning: number;
|
||||
MicSpamSamplePercentageForWarningToEnd: number;
|
||||
MicSpamSamplePercentageForForceMute: number;
|
||||
MicSpamSamplePercentageForForceMuteToEnd: number;
|
||||
MicSpamWarningStateVolumeMultiplier: number;
|
||||
};
|
||||
|
||||
export type PublicConfig = {
|
||||
ShareBaseUrl: string;
|
||||
ServerMaintenance: {
|
||||
StartsInMinutes: number;
|
||||
};
|
||||
LevelProgressionMaps: LevelProgressionItem[];
|
||||
DailyObjectives: Objective[][];
|
||||
AutoMicMutingConfig: AutoMicMutingConfig;
|
||||
};
|
||||
|
||||
class ServerBase extends EventManager<ServerEvents> {
|
||||
Profiles = new ProfileManagerBase(this, 'profiles', true);
|
||||
GameConfigs = new GameConfigsBase(this, 'gameconfigs', true);
|
||||
@@ -36,6 +65,78 @@ class ServerBase extends EventManager<ServerEvents> {
|
||||
generateMask(...num: number[]) {
|
||||
return num.reduce((sum, val) => sum + val, 0);
|
||||
}
|
||||
|
||||
getPublicConfig() {
|
||||
const netConfig = getNetConfig();
|
||||
|
||||
function generateLevelProgressionMap() {
|
||||
const m: LevelProgressionItem[] = [];
|
||||
for (let i = 0; i < 31; i++) {
|
||||
m.push({
|
||||
Level: i,
|
||||
RequiredXp: Math.round(i * 1 * 20),
|
||||
});
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
const conf: PublicConfig = {
|
||||
ServerMaintenance: {
|
||||
StartsInMinutes: 0,
|
||||
},
|
||||
LevelProgressionMaps: generateLevelProgressionMap(),
|
||||
DailyObjectives: [
|
||||
[
|
||||
{ type: ObjectiveType.Default, score: 0 },
|
||||
{ type: ObjectiveType.GoToRecCenter, score: 0 },
|
||||
{ type: ObjectiveType.Default, score: 0 }
|
||||
],
|
||||
[
|
||||
{ type: ObjectiveType.Default, score: 0 },
|
||||
{ type: ObjectiveType.LevelUp, score: 0 },
|
||||
{ type: ObjectiveType.Default, score: 0 }
|
||||
],
|
||||
[
|
||||
{ type: ObjectiveType.Default, score: 0 },
|
||||
{ type: ObjectiveType.SaveOutfitSlot, score: 0 },
|
||||
{ type: ObjectiveType.Default, score: 0 }
|
||||
],
|
||||
[
|
||||
{ type: ObjectiveType.Default, score: 0 },
|
||||
{ type: ObjectiveType.ScoreBasketInRecCenter, score: 0 },
|
||||
{ type: ObjectiveType.Default, score: 0 }
|
||||
],
|
||||
[
|
||||
{ type: ObjectiveType.Default, score: 0 },
|
||||
{ type: ObjectiveType.VisitACustomRoom, score: 0 },
|
||||
{ type: ObjectiveType.Default, score: 0 }
|
||||
],
|
||||
[
|
||||
{ type: ObjectiveType.Default, score: 0 },
|
||||
{ type: ObjectiveType.SubscribeToPlayer, score: 0 },
|
||||
{ type: ObjectiveType.Default, score: 0 }
|
||||
],
|
||||
[
|
||||
{ type: ObjectiveType.Default, score: 0 },
|
||||
{ type: ObjectiveType.AddAFriend, score: 0 },
|
||||
{ type: ObjectiveType.Default, score: 0 }
|
||||
]
|
||||
],
|
||||
AutoMicMutingConfig: {
|
||||
MicSpamVolumeThreshold: 1.125,
|
||||
MicVolumeSampleInterval: 0.25,
|
||||
MicVolumeSampleRollingWindowLength: 7.0,
|
||||
MicSpamSamplePercentageForWarning: 0.8,
|
||||
MicSpamSamplePercentageForWarningToEnd: 0.2,
|
||||
MicSpamSamplePercentageForForceMute: 0.8,
|
||||
MicSpamSamplePercentageForForceMuteToEnd: 0.2,
|
||||
MicSpamWarningStateVolumeMultiplier: 0.25
|
||||
},
|
||||
ShareBaseUrl: `${netConfig.securePublicHost ? 'https' : 'http'}://${netConfig.publicHost}/{0}` // {0} is replaced by the game
|
||||
};
|
||||
|
||||
return conf;
|
||||
}
|
||||
}
|
||||
|
||||
const Server = new ServerBase();
|
||||
|
||||
Reference in New Issue
Block a user