forked from zombieb/galvanic-corrosion-rewrite
rooooms are mostly dooone, misc routes, instance management stubs, and matchmaking base
This commit is contained in:
@@ -148,8 +148,7 @@ const server = Deno.serve({
|
|||||||
`${typeof res.status == 'number' ? res.status : "SENT STACK TRACE"}: ${req.method} ${getFullPathFromUrl(new URL(req.url))}`,
|
`${typeof res.status == 'number' ? res.status : "SENT STACK TRACE"}: ${req.method} ${getFullPathFromUrl(new URL(req.url))}`,
|
||||||
formatHeader(req.headers, 'Content-Type'),
|
formatHeader(req.headers, 'Content-Type'),
|
||||||
formatHeader(req.headers, 'Connection'),
|
formatHeader(req.headers, 'Connection'),
|
||||||
formatHeader(req.headers, 'User-Agent'),
|
formatHeader(req.headers, 'User-Agent')
|
||||||
formatHeader(req.headers, 'Accept-Encoding')
|
|
||||||
]);
|
]);
|
||||||
if (!logBlacklist.includes(url.pathname)) {
|
if (!logBlacklist.includes(url.pathname)) {
|
||||||
if (res.status === 404) log.e(netlog);
|
if (res.status === 404) log.e(netlog);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Server from "../../../server/server.ts";
|
||||||
import { createHonoRoute } from "../../../util/import.ts";
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
|
||||||
export const route = createHonoRoute("/config");
|
export const route = createHonoRoute("/config");
|
||||||
@@ -5,3 +6,7 @@ export const route = createHonoRoute("/config");
|
|||||||
route.app.get('/v1/amplitude', c => {
|
route.app.get('/v1/amplitude', c => {
|
||||||
return c.json({AmplitudeKey: ""});
|
return c.json({AmplitudeKey: ""});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
route.app.get('/v2', c => {
|
||||||
|
return c.json(Server.getPublicConfig());
|
||||||
|
});
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import { authenticate } from "../../../util/api.ts";
|
||||||
import { createHonoRoute } from "../../../util/import.ts";
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
|
||||||
export const route = createHonoRoute("/messages");
|
export const route = createHonoRoute("/messages");
|
||||||
|
|
||||||
route.app.get('/v2/get', async c => {
|
route.app.get('/v2/get', authenticate, async c => {
|
||||||
return c.json(await c.get('profile').Messages.getMessages());
|
return c.json(await c.get('profile').Messages.getMessages());
|
||||||
});
|
});
|
||||||
@@ -9,21 +9,26 @@ export const route = createHonoRoute("/playerReputation");
|
|||||||
const getRepIdParamSchema = z.object({
|
const getRepIdParamSchema = z.object({
|
||||||
id: z.coerce.number().min(0).max(2_147_483_647)
|
id: z.coerce.number().min(0).max(2_147_483_647)
|
||||||
})
|
})
|
||||||
route.app.get('/v1/:id', typedZValidator('param', getRepIdParamSchema), authenticate, async c => {
|
route.app.get('/v1/:id', authenticate, typedZValidator('param', getRepIdParamSchema), authenticate, async c => {
|
||||||
const { id } = c.req.valid('param');
|
const { id } = c.req.valid('param');
|
||||||
const prof = await Server.Profiles.get(id);
|
const prof = await Server.Profiles.get(id);
|
||||||
|
|
||||||
if (!prof) return c.status(404);
|
if (!prof) return c.status(404);
|
||||||
else return c.json({
|
else return c.json(await prof.Reputation.export());
|
||||||
AccountId: id,
|
});
|
||||||
Noteriety: 0.0,
|
|
||||||
CheerGeneral: 0,
|
const getRepBulkBodySchema = z.object({
|
||||||
CheerHelpful: 0,
|
Ids: z.array(z.coerce.number())
|
||||||
CheerGreatHost: 0,
|
});
|
||||||
CheerSportsman: 0,
|
route.app.post('/v1/bulk', authenticate, typedZValidator('form', getRepBulkBodySchema), async c => {
|
||||||
CheerCreative: 0,
|
const ids = c.req.valid('form').Ids;
|
||||||
CheerCredit: 0,
|
|
||||||
SubscriberCount: 0,
|
if (typeof ids == 'object') {
|
||||||
SubscribedCount: 0,
|
const profs = await Server.Profiles.getMany(...ids);
|
||||||
});
|
return c.json(await Promise.all(profs.map(prof => prof.Reputation.export())));
|
||||||
|
} else {
|
||||||
|
const prof = await Server.Profiles.get(ids);
|
||||||
|
if (!prof) return c.json([]);
|
||||||
|
return c.json([await prof.Reputation.export()]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
@@ -2,6 +2,7 @@ import z from "zod";
|
|||||||
import { createHonoRoute } from "../../../util/import.ts";
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
import { authenticate } from "../../../util/api.ts";
|
import { authenticate } from "../../../util/api.ts";
|
||||||
import { typedZValidator } from "../../../util/validators.ts";
|
import { typedZValidator } from "../../../util/validators.ts";
|
||||||
|
import Server from "../../../server/server.ts";
|
||||||
|
|
||||||
export const route = createHonoRoute("/players");
|
export const route = createHonoRoute("/players");
|
||||||
|
|
||||||
@@ -15,3 +16,19 @@ const getProgParamSchema = z.object({
|
|||||||
route.app.get('/v1/progression/:id', authenticate, typedZValidator('param', getProgParamSchema), async c => {
|
route.app.get('/v1/progression/:id', authenticate, typedZValidator('param', getProgParamSchema), async c => {
|
||||||
return c.json(await c.get('profile').Reputation.export());
|
return c.json(await c.get('profile').Reputation.export());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getProgBulkBodySchema = z.object({
|
||||||
|
Ids: z.union([z.array(z.coerce.number()), z.coerce.number()])
|
||||||
|
});
|
||||||
|
route.app.post('/v1/progression/bulk', authenticate, typedZValidator('form', getProgBulkBodySchema), async c => {
|
||||||
|
const ids = c.req.valid('form').Ids;
|
||||||
|
|
||||||
|
if (typeof ids == 'object') {
|
||||||
|
const profs = await Server.Profiles.getMany(...ids);
|
||||||
|
return c.json(await Promise.all(profs.map(prof => prof.Progression.get())));
|
||||||
|
} else {
|
||||||
|
const prof = await Server.Profiles.get(ids);
|
||||||
|
if (!prof) return c.json([]);
|
||||||
|
return c.json([await prof.Progression.get()]);
|
||||||
|
}
|
||||||
|
});
|
||||||
7
src/routes/api/routes/quickPlay.ts
Normal file
7
src/routes/api/routes/quickPlay.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
|
||||||
|
export const route = createHonoRoute("/quickPlay");
|
||||||
|
|
||||||
|
route.app.get('/v1/getandclear', c => {
|
||||||
|
return c.json({});
|
||||||
|
});
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { HTTPStatus } from "@oneday/http-status";
|
||||||
|
import { statusResponse } from "../../../util/api.ts";
|
||||||
import { createHonoRoute } from "../../../util/import.ts";
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
|
||||||
export const route = createHonoRoute('/relationships');
|
export const route = createHonoRoute('/relationships');
|
||||||
@@ -5,3 +7,8 @@ export const route = createHonoRoute('/relationships');
|
|||||||
route.app.get('/v2/get', c => {
|
route.app.get('/v2/get', c => {
|
||||||
return c.json([]);
|
return c.json([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// deno-lint-ignore require-await
|
||||||
|
route.app.post('/v1/bulkignoreplatformusers', async c => {
|
||||||
|
return statusResponse(c, HTTPStatus.OK);
|
||||||
|
});
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { authenticate } from "../../../util/api.ts";
|
import { authenticate, statusResponse } from "../../../util/api.ts";
|
||||||
import { createHonoRoute } from "../../../util/import.ts";
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
import { typedZValidator } from "../../../util/validators.ts";
|
import { typedZValidator } from "../../../util/validators.ts";
|
||||||
import { HonoEnv } from "../../../util/types.ts";
|
import { HonoEnv } from "../../../util/types.ts";
|
||||||
import { Context } from "@hono/hono";
|
import { Context } from "@hono/hono";
|
||||||
|
import { HTTPStatus } from "@oneday/http-status";
|
||||||
|
|
||||||
export const route = createHonoRoute('/settings');
|
export const route = createHonoRoute('/settings');
|
||||||
|
|
||||||
@@ -25,5 +26,5 @@ route.app.post('/v2/set', typedZValidator('json', settingsSetSchema), async c =>
|
|||||||
const { Key, Value } = c.req.valid('json');
|
const { Key, Value } = c.req.valid('json');
|
||||||
|
|
||||||
await c.get('profile').Settings.setSetting(Key, Value);
|
await c.get('profile').Settings.setSetting(Key, Value);
|
||||||
return c.status(200);
|
return statusResponse(c, HTTPStatus.OK);
|
||||||
});
|
});
|
||||||
@@ -1,7 +1,50 @@
|
|||||||
import { createHonoRoute, routeImporter } from "../../util/import.ts";
|
import { createHonoRoute, routeImporter } from "../../util/import.ts";
|
||||||
|
import { Context, Next } from "@hono/hono";
|
||||||
|
import z from "zod";
|
||||||
|
import { HonoEnv } from "../../util/types.ts";
|
||||||
|
import { statusResponse } from "../../util/api.ts";
|
||||||
|
import { HTTPStatus } from "@oneday/http-status";
|
||||||
|
import Logging from "@proxnet/undead-logging";
|
||||||
|
|
||||||
|
const log = new Logging("MatchRoute");
|
||||||
|
|
||||||
export const route = createHonoRoute('/match');
|
export const route = createHonoRoute('/match');
|
||||||
|
|
||||||
|
const loginLockBodySchema = z.object({
|
||||||
|
LoginLock: z.uuidv4()
|
||||||
|
});
|
||||||
|
export const loginLockMiddleware = async (c: Context<HonoEnv>, nxt: Next) => {
|
||||||
|
function unauthorized() {
|
||||||
|
return statusResponse(c, HTTPStatus.Unauthorized);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.req.header("Content-Type") !== "application/x-www-form-urlencoded") return unauthorized();
|
||||||
|
try {
|
||||||
|
const form = await c.req.formData();
|
||||||
|
|
||||||
|
const body = await loginLockBodySchema.safeParseAsync(Object.fromEntries(form.entries()));
|
||||||
|
if (body.success) {
|
||||||
|
if (typeof c.get('profile') == 'undefined') {
|
||||||
|
log.w(`Profile was not set, cannot validate LoginLock. Was the request authorized?`);
|
||||||
|
return statusResponse(c, HTTPStatus.InternalServerError);
|
||||||
|
}
|
||||||
|
|
||||||
|
const profile = c.get('profile');
|
||||||
|
|
||||||
|
const loginLock = await profile.Matchmaking.getLoginLock();
|
||||||
|
if (!loginLock) await profile.Matchmaking.setLoginLock(body.data.LoginLock);
|
||||||
|
else if (body.data.LoginLock !== loginLock) {
|
||||||
|
log.w(`LoginLock did not match. The token for this profile could be compromised or the client is an unknown state.`);
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await nxt();
|
||||||
|
} else return unauthorized();
|
||||||
|
} catch {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await routeImporter(route.app, 'src/routes/match/', [
|
await routeImporter(route.app, 'src/routes/match/', [
|
||||||
'routes'
|
'routes'
|
||||||
]);
|
]);
|
||||||
13
src/routes/match/routes/goto.ts
Normal file
13
src/routes/match/routes/goto.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { HTTPStatus } from "@oneday/http-status";
|
||||||
|
import { statusResponse } from "../../../util/api.ts";
|
||||||
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
|
||||||
|
export const route = createHonoRoute("/goto");
|
||||||
|
|
||||||
|
route.app.post('/room/:roomName', c => {
|
||||||
|
return statusResponse(c, HTTPStatus.NotImplemented);
|
||||||
|
});
|
||||||
|
|
||||||
|
route.app.post('/room/:roomName/:subRoomName', c => {
|
||||||
|
return statusResponse(c, HTTPStatus.NotImplemented);
|
||||||
|
});
|
||||||
@@ -1,10 +1,20 @@
|
|||||||
import { authenticate } from "../../../util/api.ts";
|
import { authenticate, statusResponse } from "../../../util/api.ts";
|
||||||
import { createHonoRoute } from "../../../util/import.ts";
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
import { loginLockMiddleware } from "../root.ts";
|
||||||
|
import { HTTPStatus } from "@oneday/http-status";
|
||||||
|
|
||||||
export const route = createHonoRoute("/player");
|
export const route = createHonoRoute("/player");
|
||||||
|
|
||||||
route.app.use(authenticate);
|
route.app.use(authenticate);
|
||||||
|
|
||||||
route.app.post('/login', _c => {
|
route.app.post('/login', authenticate, loginLockMiddleware, async c => {
|
||||||
return new Response("OK", { status: 200 });
|
|
||||||
|
});
|
||||||
|
|
||||||
|
route.app.post('/player/vrmovementmode', authenticate, loginLockMiddleware, async c => {
|
||||||
|
return statusResponse(c, HTTPStatus.OK); // stub
|
||||||
|
});
|
||||||
|
|
||||||
|
route.app.post('/player/statusvisibility', authenticate, loginLockMiddleware, async c => {
|
||||||
|
return statusResponse(c, HTTPStatus.OK); // stub
|
||||||
});
|
});
|
||||||
@@ -5,19 +5,19 @@ export class EventManager<Events extends { [K in keyof Events]: unknown }> {
|
|||||||
[K in keyof Events]?: Set<Callback<Events[K]>>
|
[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])
|
if (!this.#listeners[eventName])
|
||||||
this.#listeners[eventName] = new Set();
|
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 {
|
off<K extends keyof Events>(eventName: K, cb: Callback<Events[K]>): void {
|
||||||
this.#listeners[eventName]?.delete(callback);
|
this.#listeners[eventName]?.delete(cb);
|
||||||
if (this.#listeners[eventName]?.size === 0)
|
if (this.#listeners[eventName]?.size === 0)
|
||||||
delete this.#listeners[eventName];
|
delete this.#listeners[eventName];
|
||||||
}
|
}
|
||||||
|
|
||||||
emit<K extends keyof Events>(eventName: K, payload: Events[K]): void {
|
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 { CloudRegionCode } from "../../util/photon.ts";
|
||||||
import type Profile from "../profiles/profile.ts";
|
import type Profile from "../profiles/profile.ts";
|
||||||
import { RoomInstance, RoomLocation } from "./base.ts";
|
import { RoomInstance, RoomLocation } from "./types.ts";
|
||||||
|
|
||||||
export interface InstanceCreationOptions {
|
export interface InstanceCreationOptions {
|
||||||
roomId: number,
|
roomId: number,
|
||||||
|
|||||||
@@ -1,63 +1,47 @@
|
|||||||
|
import Logging from "@proxnet/undead-logging";
|
||||||
import { ServerContentBase } from "../ContentBase.ts";
|
import { ServerContentBase } from "../ContentBase.ts";
|
||||||
|
import { type Instance } from "./Instance.ts";
|
||||||
|
|
||||||
export enum RoomLocation {
|
const log = new Logging("Instances");
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InstanceManager extends ServerContentBase {
|
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 z from "zod";
|
||||||
import Command from "../commands/command.ts";
|
import Command from "../commands/command.ts";
|
||||||
import { ServerContentBase } from "../ContentBase.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 { sign } from "@hono/hono/jwt";
|
||||||
import { CachedLogin, DbCachedLogin, PlatformMask, PlatformType, TokenFormat, TokenType } from "./types.ts";
|
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);
|
return await this.getCachedLogins(type, platformId, false);
|
||||||
},
|
},
|
||||||
zod: z.tuple([
|
zod: z.tuple([
|
||||||
z.string().transform(transformStringToEnum<PlatformType>(PlatformType)),
|
z.coerce.number().transform(transformCheckEnum<PlatformType>(PlatformType)),
|
||||||
z.string().min(4)
|
z.string().min(4)
|
||||||
]),
|
]),
|
||||||
help: 'List all cachedlogins for platformId: <type: PlatformType, platformId: string>'
|
help: 'List all cachedlogins for platformId: <type: PlatformType, platformId: string>'
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export interface TokenFormat extends TokenFormatBase {
|
|||||||
|
|
||||||
export enum ProfileRole {
|
export enum ProfileRole {
|
||||||
Developer = 'developer',
|
Developer = 'developer',
|
||||||
Moderator = 'moderator',
|
|
||||||
Web = 'webClient',
|
Web = 'webClient',
|
||||||
Game = 'gameClient'
|
Game = 'gameClient'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ServerContentBase } from "../ContentBase.ts";
|
|||||||
import { DeviceClass } from "../platforms/types.ts";
|
import { DeviceClass } from "../platforms/types.ts";
|
||||||
import Profile from "../profiles/profile.ts";
|
import Profile from "../profiles/profile.ts";
|
||||||
import { type ServerBase } from "../server.ts";
|
import { type ServerBase } from "../server.ts";
|
||||||
import { RoomInstance } from "../instances/base.ts";
|
import { RoomInstance } from "../instances/types.ts";
|
||||||
|
|
||||||
export enum VRMovementMode {
|
export enum VRMovementMode {
|
||||||
TELEPORT,
|
TELEPORT,
|
||||||
|
|||||||
@@ -15,11 +15,8 @@ export class ProfileMatchmakingManager extends ProfileContentManager {
|
|||||||
async setLoginLock(lock: string) {
|
async setLoginLock(lock: string) {
|
||||||
await this.kv.getKv().set(this.#deviceClassKey, lock);
|
await this.kv.getKv().set(this.#deviceClassKey, lock);
|
||||||
}
|
}
|
||||||
async getLoginLock(): Promise<string> {
|
async getLoginLock(): Promise<string | null> {
|
||||||
return (await this.kv.getKv().get<string>(this.#loginLockKey)).value || "";
|
return (await this.kv.getKv().get<string>(this.#loginLockKey)).value;
|
||||||
}
|
|
||||||
async hasLoginLock() {
|
|
||||||
return (await this.kv.getKv().get<string>(this.#loginLockKey)).value ? true : false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#lastSeen = this.profile.constructProfilePropertyKey('lastseen');
|
#lastSeen = this.profile.constructProfilePropertyKey('lastseen');
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ interface MessageBase {
|
|||||||
|
|
||||||
export class ProfileMessageManager extends ProfileContentManager {
|
export class ProfileMessageManager extends ProfileContentManager {
|
||||||
|
|
||||||
|
// temp
|
||||||
// deno-lint-ignore require-await
|
// deno-lint-ignore require-await
|
||||||
async getMessages() {
|
async getMessages() {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -2,4 +2,13 @@ import ProfileContentManager from "./base.ts";
|
|||||||
|
|
||||||
export class ProfileProgressionManager extends ProfileContentManager {
|
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:
|
} // 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 });
|
if (!s) settings.push({ Key, Value });
|
||||||
else s.Value = Value;
|
else s.Value = Value;
|
||||||
await this.#updateSettings(settings);
|
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 KV from "../../persistence/kv.ts";
|
||||||
|
import { type ServerBase } from "../../server.ts";
|
||||||
import type Profile from "../profile.ts";
|
import type Profile from "../profile.ts";
|
||||||
|
|
||||||
class ProfileContentManager {
|
class ProfileContentManager {
|
||||||
|
|
||||||
protected profile: Profile;
|
protected profile: Profile;
|
||||||
protected kv: KV;
|
protected kv: KV;
|
||||||
constructor(profile: Profile, kv: KV) {
|
protected server: ServerBase
|
||||||
|
constructor(server: ServerBase, profile: Profile, kv: KV) {
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
this.kv = kv;
|
this.kv = kv;
|
||||||
|
this.server = server;
|
||||||
profile.managers.push(this);
|
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");
|
#log = new Logging("ProfileManager");
|
||||||
|
|
||||||
|
#logSettingChanges = false;
|
||||||
|
|
||||||
async #getUnusedId() {
|
async #getUnusedId() {
|
||||||
let id = Math.round(Math.random() * 2_147_483_647);
|
let id = Math.round(Math.random() * 2_147_483_647);
|
||||||
if (await this.get(id)) id = await this.#getUnusedId();
|
if (await this.get(id)) id = await this.#getUnusedId();
|
||||||
@@ -78,7 +80,7 @@ class ProfileManagerBase extends ServerContentBase {
|
|||||||
async getAll() {
|
async getAll() {
|
||||||
const keys = this.kv.getKv().list({ prefix: [ ProfileManagerBase.profilesKey ] });
|
const keys = this.kv.getKv().list({ prefix: [ ProfileManagerBase.profilesKey ] });
|
||||||
const awaitedKeys = await Array.fromAsync(keys);
|
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) {
|
async getByUsername(username: string) {
|
||||||
@@ -88,9 +90,24 @@ class ProfileManagerBase extends ServerContentBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override start() {
|
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({
|
this.server.Commands.addRootCommand(new Command({
|
||||||
key: ['account', 'profile', 'acc', 'prof'],
|
key: ['account', 'profile', 'acc', 'prof', "profiles"],
|
||||||
subcommands: [
|
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({
|
new Command({
|
||||||
key: ['get', 'g', 'fetch', 'f'],
|
key: ['get', 'g', 'fetch', 'f'],
|
||||||
exec: async (id: number) => {
|
exec: async (id: number) => {
|
||||||
@@ -104,7 +121,7 @@ class ProfileManagerBase extends ServerContentBase {
|
|||||||
help: 'Fetch a profile: <id: number>'
|
help: 'Fetch a profile: <id: number>'
|
||||||
}),
|
}),
|
||||||
new Command({
|
new Command({
|
||||||
key: ['getall', 'listall', 'fetchall', 'all', 'a'],
|
key: ['getall', 'listall', 'fetchall', 'all', 'a', "list"],
|
||||||
exec: async () => {
|
exec: async () => {
|
||||||
const ids = await this.getAll();
|
const ids = await this.getAll();
|
||||||
return ids;
|
return ids;
|
||||||
@@ -132,10 +149,20 @@ class ProfileManagerBase extends ServerContentBase {
|
|||||||
else return await profile.setRole(role);
|
else return await profile.setRole(role);
|
||||||
},
|
},
|
||||||
zod: z.tuple([
|
zod: z.tuple([
|
||||||
z.string().transform(Number),
|
z.coerce.number(),
|
||||||
z.string()
|
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 ProfileContentManager from "./content/base.ts";
|
||||||
import { ProfileMatchmakingManager } from "./content/Matchmaking.ts";
|
import { ProfileMatchmakingManager } from "./content/Matchmaking.ts";
|
||||||
import { ProfileMessageManager } from "./content/Messages.ts";
|
import { ProfileMessageManager } from "./content/Messages.ts";
|
||||||
|
import { ProfileProgressionManager } from "./content/Progression.ts";
|
||||||
import { ProfileReputationManager } from "./content/Reputation.ts";
|
import { ProfileReputationManager } from "./content/Reputation.ts";
|
||||||
|
import { ProfileRoomsManager } from "./content/Rooms.ts";
|
||||||
import { ProfileSettingsManager } from "./content/Settings.ts";
|
import { ProfileSettingsManager } from "./content/Settings.ts";
|
||||||
import { ProfileSubscriptionsManager } from "./content/Subscriptions.ts";
|
import { ProfileSubscriptionsManager } from "./content/Subscriptions.ts";
|
||||||
import ProfileManagerBase from "./manager.ts";
|
import ProfileManagerBase from "./manager.ts";
|
||||||
@@ -32,6 +34,8 @@ class Profile {
|
|||||||
Reputation: ProfileReputationManager;
|
Reputation: ProfileReputationManager;
|
||||||
Subscriptions: ProfileSubscriptionsManager;
|
Subscriptions: ProfileSubscriptionsManager;
|
||||||
Messages: ProfileMessageManager;
|
Messages: ProfileMessageManager;
|
||||||
|
Rooms: ProfileRoomsManager;
|
||||||
|
Progression: ProfileProgressionManager;
|
||||||
|
|
||||||
constructor(acc: SelfAccount, kv: KV, server: ServerBase) {
|
constructor(acc: SelfAccount, kv: KV, server: ServerBase) {
|
||||||
this.#id = acc.accountId;
|
this.#id = acc.accountId;
|
||||||
@@ -39,12 +43,14 @@ class Profile {
|
|||||||
this.#kv = kv;
|
this.#kv = kv;
|
||||||
this.#server = server;
|
this.#server = server;
|
||||||
|
|
||||||
this.Settings = new ProfileSettingsManager(this, this.#kv);
|
this.Settings = new ProfileSettingsManager(server, this, this.#kv);
|
||||||
this.Avatar = new ProfileAvatarManager(this, this.#kv);
|
this.Avatar = new ProfileAvatarManager(server, this, this.#kv);
|
||||||
this.Matchmaking = new ProfileMatchmakingManager(this, this.#kv);
|
this.Matchmaking = new ProfileMatchmakingManager(server, this, this.#kv);
|
||||||
this.Reputation = new ProfileReputationManager(this, this.#kv);
|
this.Reputation = new ProfileReputationManager(server, this, this.#kv);
|
||||||
this.Subscriptions = new ProfileSubscriptionsManager(this, this.#kv);
|
this.Subscriptions = new ProfileSubscriptionsManager(server, this, this.#kv);
|
||||||
this.Messages = new ProfileMessageManager(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() {
|
async #saveSelfAcc() {
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import { FactoryMode, HardwareSupports, RoomDataTypes, WriteMode } from "./inter
|
|||||||
import { AGRoom, AGRoomLocation, AGRoomRuntimeConfig } from "./internal/ClientRoomTypes.ts";
|
import { AGRoom, AGRoomLocation, AGRoomRuntimeConfig } from "./internal/ClientRoomTypes.ts";
|
||||||
import Command from "../commands/command.ts";
|
import Command from "../commands/command.ts";
|
||||||
import z from "zod";
|
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 {
|
export class ServerRoomsBase extends ServerContentBase {
|
||||||
|
|
||||||
#subroomKv = new KV('subrooms', true);
|
#subroomKv = new KV('subrooms', true);
|
||||||
@@ -54,12 +55,29 @@ export class ServerRoomsBase extends ServerContentBase {
|
|||||||
}),
|
}),
|
||||||
new Command({
|
new Command({
|
||||||
key: ["getplayerdorm", "playerdorm", "pd", "dorm"],
|
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) => {
|
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"
|
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]"
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -178,21 +196,24 @@ export class ServerRoomsBase extends ServerContentBase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await roomFactory.addHardwareSupport(...HardwareSupports);
|
await roomFactory.addHardwareSupport(...HardwareSupports);
|
||||||
roomFactory.setTags(new Set(["dormroom"]));
|
|
||||||
|
|
||||||
const subroomFactory = await roomFactory.newSubroom();
|
const subroomFactory = await roomFactory.newSubroom();
|
||||||
subroomFactory.CanMatchmakeInto = true;
|
subroomFactory.setSubroomProperties({
|
||||||
subroomFactory.IsSandbox = true;
|
CanMatchmakeInto: true,
|
||||||
subroomFactory.MaxPlayers = 4;
|
IsSandbox: true,
|
||||||
subroomFactory.Name = "Home";
|
MaxPlayers: 4,
|
||||||
subroomFactory.RoomId = roomFactory.getRoomId();
|
Name: "Home",
|
||||||
subroomFactory.RoomSceneLocationId = RoomLocation.DormRoom;
|
RoomId: roomFactory.getRoomId(),
|
||||||
|
RoomSceneLocationId: RoomLocation.DormRoom
|
||||||
|
});
|
||||||
subroomFactory.addSave("");
|
subroomFactory.addSave("");
|
||||||
roomFactory.addSubroom(subroomFactory.RoomSceneId);
|
roomFactory.addSubroom(subroomFactory.RoomSceneId);
|
||||||
|
|
||||||
await subroomFactory.write();
|
await subroomFactory.write();
|
||||||
await roomFactory.write();
|
await roomFactory.write();
|
||||||
|
|
||||||
|
await profile.Rooms.addRoom(roomFactory.getRoomId());
|
||||||
|
|
||||||
return roomFactory;
|
return roomFactory;
|
||||||
}
|
}
|
||||||
else return await new RoomFactory(this.server, this.kv).init({ mode: FactoryMode.Fetch, id: id.value });
|
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
|
Room: DatabaseRoomContent
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseSubroom {
|
export interface SubroomProps {
|
||||||
RoomId: number,
|
RoomId: number,
|
||||||
RoomSceneLocationId: string,
|
RoomSceneLocationId: string,
|
||||||
Name: string,
|
Name: string,
|
||||||
IsSandbox: boolean,
|
IsSandbox: boolean,
|
||||||
MaxPlayers: number,
|
MaxPlayers: number,
|
||||||
CanMatchmakeInto: boolean,
|
CanMatchmakeInto: boolean,
|
||||||
LatestSaveId: number | null,
|
}
|
||||||
|
export interface DatabaseSubroom extends SubroomProps {
|
||||||
|
LatestSaveId: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RoomSaveMap = Map<number, RoomSave>
|
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, 'meta'], this.#obj),
|
||||||
this.#kv.getKv().set([RoomFactory.roomsKey, this.#roomId, 'hardware'], this.#hardwareSupport),
|
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, '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);
|
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; }
|
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 }
|
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 { ServerUpdateEvent } from "../serverevents.ts";
|
||||||
import { AvatarContentBase } from "./avatars/base.ts";
|
import { AvatarContentBase } from "./avatars/base.ts";
|
||||||
import { EventManager } from "./baseevent.ts";
|
import { EventManager } from "./baseevent.ts";
|
||||||
@@ -5,9 +6,11 @@ import { CommandsBase } from "./commands/commands.ts";
|
|||||||
import { ServerContentManager } from "./content/base.ts";
|
import { ServerContentManager } from "./content/base.ts";
|
||||||
import GameConfigsBase from "./gameconfigs/base.ts";
|
import GameConfigsBase from "./gameconfigs/base.ts";
|
||||||
import { InstanceManager } from "./instances/base.ts";
|
import { InstanceManager } from "./instances/base.ts";
|
||||||
|
import { Objective, ObjectiveType } from "./objectives/base.ts";
|
||||||
import { PlatformsManager } from "./platforms/base.ts";
|
import { PlatformsManager } from "./platforms/base.ts";
|
||||||
import { type PresenceUpdateEvent } from "./presence/events/PresenceUpdateEvent.ts";
|
import { type PresenceUpdateEvent } from "./presence/events/PresenceUpdateEvent.ts";
|
||||||
import { type ProfileUpdateEvent } from "./profiles/events/ProfileUpdate.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 { type RoleUpdateEvent } from "./profiles/events/RoleUpdate.ts";
|
||||||
import ProfileManagerBase from "./profiles/manager.ts";
|
import ProfileManagerBase from "./profiles/manager.ts";
|
||||||
import { ServerRoomsBase } from "./rooms/base.ts";
|
import { ServerRoomsBase } from "./rooms/base.ts";
|
||||||
@@ -16,6 +19,7 @@ import { RoomUpdatedEvent, SubroomUpdatedEvent } from "./rooms/internal/RoomEven
|
|||||||
interface ServerEvents {
|
interface ServerEvents {
|
||||||
'profile.roleupdate': RoleUpdateEvent,
|
'profile.roleupdate': RoleUpdateEvent,
|
||||||
'profile.update': ProfileUpdateEvent,
|
'profile.update': ProfileUpdateEvent,
|
||||||
|
'profile.setting.update': ProfileUpdatedSettingEvent,
|
||||||
'presence.update': PresenceUpdateEvent,
|
'presence.update': PresenceUpdateEvent,
|
||||||
'server.start': ServerUpdateEvent,
|
'server.start': ServerUpdateEvent,
|
||||||
'server.destroy': ServerUpdateEvent,
|
'server.destroy': ServerUpdateEvent,
|
||||||
@@ -23,6 +27,31 @@ interface ServerEvents {
|
|||||||
'room.subroom.updated': SubroomUpdatedEvent
|
'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> {
|
class ServerBase extends EventManager<ServerEvents> {
|
||||||
Profiles = new ProfileManagerBase(this, 'profiles', true);
|
Profiles = new ProfileManagerBase(this, 'profiles', true);
|
||||||
GameConfigs = new GameConfigsBase(this, 'gameconfigs', true);
|
GameConfigs = new GameConfigsBase(this, 'gameconfigs', true);
|
||||||
@@ -36,6 +65,78 @@ class ServerBase extends EventManager<ServerEvents> {
|
|||||||
generateMask(...num: number[]) {
|
generateMask(...num: number[]) {
|
||||||
return num.reduce((sum, val) => sum + val, 0);
|
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();
|
const Server = new ServerBase();
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ export const typedZValidator = <
|
|||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use transformCheckEnum after #.coerce.number() (optionally)
|
||||||
|
*/
|
||||||
export const transformStringToEnum = <T>(anEnum: { [s: string]: string | number }, str?: boolean) => {
|
export const transformStringToEnum = <T>(anEnum: { [s: string]: string | number }, str?: boolean) => {
|
||||||
return (arg: string, ctx: z.RefinementCtx<string | number>) => {
|
return (arg: string, ctx: z.RefinementCtx<string | number>) => {
|
||||||
if (!str) {
|
if (!str) {
|
||||||
@@ -37,3 +40,12 @@ export const transformStringToEnum = <T>(anEnum: { [s: string]: string | number
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const transformCheckEnum = <T>(anEnum: { [s: string]: string | number }) => {
|
||||||
|
return (arg: number | string, ctx: z.RefinementCtx<number | string>) => {
|
||||||
|
if (typeof anEnum[arg] == 'undefined') {
|
||||||
|
ctx.addIssue("Not an enum member");
|
||||||
|
return null;
|
||||||
|
} else return anEnum[arg] as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user