b
This commit is contained in:
@@ -36,7 +36,8 @@ await routeImporter(AppRoot.app, 'src/', [
|
|||||||
'routes/root',
|
'routes/root',
|
||||||
'routes/api',
|
'routes/api',
|
||||||
'routes/auth',
|
'routes/auth',
|
||||||
'routes/accounts'
|
'routes/accounts',
|
||||||
|
'routes/match'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// deno-lint-ignore require-await
|
// deno-lint-ignore require-await
|
||||||
@@ -71,8 +72,8 @@ const server = Deno.serve({ hostname: "10.0.1.39", port: 13370, onListen: addr =
|
|||||||
availableTransports: [{transport:"WebSockets",transferFormats:["Text"]}]
|
availableTransports: [{transport:"WebSockets",transferFormats:["Text"]}]
|
||||||
}), { headers: { 'Content-Type': 'application/json' }});
|
}), { headers: { 'Content-Type': 'application/json' }});
|
||||||
}
|
}
|
||||||
if (req.headers.get('Connection')?.includes('Upgrade') && req.headers.get('Upgrade') === 'websocket') {
|
if (req.headers.get('Connection')?.includes('Upgrade') && req.headers.get('Upgrade')?.includes('websocket')) {
|
||||||
const isSignalR = req.headers.has('id');
|
const isSignalR = url.searchParams.has('id');
|
||||||
if (isSignalR) {
|
if (isSignalR) {
|
||||||
try {
|
try {
|
||||||
const authHeader = req.headers.get('Authorization');
|
const authHeader = req.headers.get('Authorization');
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ import { authenticate } from "../../../util/api.ts";
|
|||||||
import Server from "../../../server/server.ts";
|
import Server from "../../../server/server.ts";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { typedZValidator } from "../../../util/validators.ts";
|
import { typedZValidator } from "../../../util/validators.ts";
|
||||||
import Logging from "@proxnet/undead-logging";
|
|
||||||
|
|
||||||
const log = new Logging("AccountDebug");
|
|
||||||
|
|
||||||
export const route = createHonoRoute('/account');
|
export const route = createHonoRoute('/account');
|
||||||
|
|
||||||
|
|||||||
18
src/routes/api/routes/PlayerReporting.ts
Normal file
18
src/routes/api/routes/PlayerReporting.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { authenticate } from "../../../util/api.ts";
|
||||||
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
|
||||||
|
export const route = createHonoRoute('/PlayerReporting');
|
||||||
|
|
||||||
|
route.app.use(authenticate);
|
||||||
|
|
||||||
|
route.app.get('/v1/moderationBlockDetails', c => {
|
||||||
|
return c.json({
|
||||||
|
ReportCategory: 0,
|
||||||
|
Duration: 0,
|
||||||
|
GameSessionId: 0,
|
||||||
|
IsHostKick: false,
|
||||||
|
VoteKickReason: "",
|
||||||
|
Message: "",
|
||||||
|
IsBan: false
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,7 +1,27 @@
|
|||||||
|
import { profileAvatarSchema } from "../../../server/profiles/content/Avatar.ts";
|
||||||
|
import Server from "../../../server/server.ts";
|
||||||
|
import { authenticate } from "../../../util/api.ts";
|
||||||
import { createHonoRoute } from "../../../util/import.ts";
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
import { typedZValidator } from "../../../util/validators.ts";
|
||||||
|
|
||||||
export const route = createHonoRoute("/avatar");
|
export const route = createHonoRoute("/avatar");
|
||||||
|
|
||||||
route.app.get('/v1/defaultunlocked', c => {
|
route.app.get('/v1/defaultunlocked', c => {
|
||||||
return c.json([]);
|
return c.json(Server.Avatars.getAllPossibleCombinations());
|
||||||
|
});
|
||||||
|
|
||||||
|
route.app.get('/v4/items', c => {
|
||||||
|
return c.json(Server.Avatars.getAllPossibleCombinations());
|
||||||
|
});
|
||||||
|
|
||||||
|
route.app.use(authenticate);
|
||||||
|
|
||||||
|
route.app.get('/v2', async c => {
|
||||||
|
const outfit = await c.get('profile').Avatar.getAvatar();
|
||||||
|
return c.json(outfit);
|
||||||
|
});
|
||||||
|
route.app.post('/v2/set', typedZValidator('json', profileAvatarSchema), async c => {
|
||||||
|
const outfit = c.req.valid('json');
|
||||||
|
await c.get('profile').Avatar.setAvatar(outfit);
|
||||||
|
return c.status(200);
|
||||||
});
|
});
|
||||||
7
src/routes/api/routes/players.ts
Normal file
7
src/routes/api/routes/players.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
|
||||||
|
export const route = createHonoRoute("/players");
|
||||||
|
|
||||||
|
route.app.get('/v2/progression/bulk', c => {
|
||||||
|
return c.json([]); // todo: progression
|
||||||
|
});
|
||||||
7
src/routes/api/routes/relationships.ts
Normal file
7
src/routes/api/routes/relationships.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
|
||||||
|
export const route = createHonoRoute('/relationships');
|
||||||
|
|
||||||
|
route.app.get('/v2/get', c => {
|
||||||
|
return c.json([]);
|
||||||
|
});
|
||||||
@@ -3,17 +3,20 @@ import { authenticate, genericResponse } from "../../../util/api.ts";
|
|||||||
import { createHonoRoute } from "../../../util/import.ts";
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
import { transformStringToEnum, typedZValidator } from "../../../util/validators.ts";
|
import { transformStringToEnum, typedZValidator } from "../../../util/validators.ts";
|
||||||
import { ProfileSetting } from "../../../server/profiles/content/Settings.ts";
|
import { ProfileSetting } from "../../../server/profiles/content/Settings.ts";
|
||||||
import { trimTrailingSlash } from "@hono/hono/trailing-slash";
|
import { HonoEnv } from "../../../util/types.ts";
|
||||||
|
import { Context } from "@hono/hono";
|
||||||
|
|
||||||
export const route = createHonoRoute('/settings');
|
export const route = createHonoRoute('/settings');
|
||||||
|
|
||||||
route.app.use(authenticate, trimTrailingSlash());
|
route.app.use(authenticate);
|
||||||
|
|
||||||
route.app.get('/v2', async c => {
|
const getSettingsMiddleware = async (c: Context<HonoEnv>) => {
|
||||||
const profile = c.get('profile');
|
const profile = c.get('profile');
|
||||||
const settings = await profile.Settings.getAllSettings();
|
const settings = await profile.Settings.getAllSettings();
|
||||||
c.json(settings);
|
return c.json(settings);
|
||||||
});
|
};
|
||||||
|
route.app.get('/v2/', getSettingsMiddleware);
|
||||||
|
route.app.get('/v2', getSettingsMiddleware);
|
||||||
|
|
||||||
const settingsSetSchema = z.object({
|
const settingsSetSchema = z.object({
|
||||||
Key: z.string().transform(transformStringToEnum<ProfileSetting>(ProfileSetting, true)),
|
Key: z.string().transform(transformStringToEnum<ProfileSetting>(ProfileSetting, true)),
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import { createHonoRoute } from "../../../util/import.ts";
|
|||||||
import { transformStringToEnum, typedZValidator } from "../../../util/validators.ts";
|
import { transformStringToEnum, typedZValidator } from "../../../util/validators.ts";
|
||||||
import { PlatformType } from "../../../server/platforms/base.ts";
|
import { PlatformType } from "../../../server/platforms/base.ts";
|
||||||
import Server from "../../../server/server.ts";
|
import Server from "../../../server/server.ts";
|
||||||
|
import { authenticate } from "../../../util/api.ts";
|
||||||
|
import Logging from "@proxnet/undead-logging";
|
||||||
|
|
||||||
|
const log = new Logging("CachedLoginDebug");
|
||||||
|
|
||||||
export const route = createHonoRoute("/cachedlogin");
|
export const route = createHonoRoute("/cachedlogin");
|
||||||
|
|
||||||
@@ -16,3 +20,14 @@ route.app.get('/forplatformid/:platformType/:platformId', typedZValidator('param
|
|||||||
|
|
||||||
return c.json((await Server.Platforms.getCachedLogins(platformType || PlatformType.Steam, platformId, true)) || []);
|
return c.json((await Server.Platforms.getCachedLogins(platformType || PlatformType.Steam, platformId, true)) || []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
route.app.use(authenticate);
|
||||||
|
|
||||||
|
const forPlatformIdsReqSchema = z.object({
|
||||||
|
id: z.string()
|
||||||
|
});
|
||||||
|
route.app.post('/forplatformids', typedZValidator('form', forPlatformIdsReqSchema), async c => {
|
||||||
|
const { id } = c.req.valid('form');
|
||||||
|
const ids = await Server.Platforms.getCachedLogins(PlatformType.Steam, id, true);
|
||||||
|
return c.json(ids || []);
|
||||||
|
});
|
||||||
@@ -117,6 +117,7 @@ route.app.post('/token', typedZValidator('form', tokenGrantSchema), async c => {
|
|||||||
if (logins && logins.find(login => login.accountId === form.account_id)) {
|
if (logins && logins.find(login => login.accountId === form.account_id)) {
|
||||||
const profile = await Server.Profiles.get(form.account_id);
|
const profile = await Server.Profiles.get(form.account_id);
|
||||||
if (!profile) return error(TokenRequestError.InvalidRequest, "No such profile");
|
if (!profile) return error(TokenRequestError.InvalidRequest, "No such profile");
|
||||||
|
await Server.Platforms.updateLastLoginTime(form.platform, form.platform_id, form.account_id);
|
||||||
const token = await Server.Platforms.getToken(profile.getId(), await profile.getRole() || 'user');
|
const token = await Server.Platforms.getToken(profile.getId(), await profile.getRole() || 'user');
|
||||||
|
|
||||||
return c.json({
|
return c.json({
|
||||||
|
|||||||
7
src/routes/match/root.ts
Normal file
7
src/routes/match/root.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createHonoRoute, routeImporter } from "../../util/import.ts";
|
||||||
|
|
||||||
|
export const route = createHonoRoute('/match');
|
||||||
|
|
||||||
|
await routeImporter(route.app, 'src/routes/match/', [
|
||||||
|
'routes'
|
||||||
|
]);
|
||||||
10
src/routes/match/routes/player.ts
Normal file
10
src/routes/match/routes/player.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { authenticate } from "../../../util/api.ts";
|
||||||
|
import { createHonoRoute } from "../../../util/import.ts";
|
||||||
|
|
||||||
|
export const route = createHonoRoute("/player");
|
||||||
|
|
||||||
|
route.app.use(authenticate);
|
||||||
|
|
||||||
|
route.app.post('/login', async c => {
|
||||||
|
return c.status(200);
|
||||||
|
});
|
||||||
@@ -1,5 +1,72 @@
|
|||||||
|
import path from "node:path";
|
||||||
import { ServerContentBase } from "../ContentBase.ts";
|
import { ServerContentBase } from "../ContentBase.ts";
|
||||||
|
import { RootPath } from "../../util/path.ts";
|
||||||
|
|
||||||
|
interface AvatarImport {
|
||||||
|
allPossibleCombinations: {
|
||||||
|
_avatarItemData: {
|
||||||
|
Name: string
|
||||||
|
},
|
||||||
|
_avatarItemVisualData: {
|
||||||
|
prefabGuid: string,
|
||||||
|
maskGuid: string,
|
||||||
|
swatchGuid: string,
|
||||||
|
decalGuid: string
|
||||||
|
}
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AvatarItemExport {
|
||||||
|
AvatarItemType: AvatarItemType,
|
||||||
|
AvatarItemDesc: string,
|
||||||
|
FriendlyName: string,
|
||||||
|
Tooltip: string,
|
||||||
|
Rarity: ItemRarity
|
||||||
|
}
|
||||||
|
export enum AvatarItemType {
|
||||||
|
Outfit,
|
||||||
|
HairDye
|
||||||
|
}
|
||||||
|
export enum ItemRarity {
|
||||||
|
None = -1,
|
||||||
|
Common,
|
||||||
|
Uncommon = 10,
|
||||||
|
Rare = 20,
|
||||||
|
Epic = 30,
|
||||||
|
Legendary = 50
|
||||||
|
}
|
||||||
|
|
||||||
export class AvatarContentBase extends ServerContentBase {
|
export class AvatarContentBase extends ServerContentBase {
|
||||||
|
#rawImport: AvatarItemExport[] = [];
|
||||||
|
|
||||||
|
getAllPossibleCombinations() {
|
||||||
|
return this.#rawImport;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatAllPossibleCombinations() {
|
||||||
|
const parsed = JSON.parse(Deno.readTextFileSync(path.join(RootPath, '/res/avatar.json'))) as AvatarImport;
|
||||||
|
function formatVisualData(data: {
|
||||||
|
prefabGuid: string,
|
||||||
|
maskGuid: string,
|
||||||
|
swatchGuid: string,
|
||||||
|
decalGuid: string
|
||||||
|
}) {
|
||||||
|
const p = data.prefabGuid ? data.prefabGuid : '';
|
||||||
|
const m = data.maskGuid ? data.maskGuid : '';
|
||||||
|
const s = data.swatchGuid ? data.swatchGuid : '';
|
||||||
|
const d = data.decalGuid ? data.decalGuid : '';
|
||||||
|
return `${p},${s},${m},${d},`
|
||||||
|
}
|
||||||
|
this.#rawImport = parsed.allPossibleCombinations.map(data => ({
|
||||||
|
AvatarItemType: AvatarItemType.Outfit,
|
||||||
|
AvatarItemDesc: formatVisualData(data._avatarItemVisualData),
|
||||||
|
FriendlyName: data._avatarItemData.Name,
|
||||||
|
Tooltip: "pre-avatar update item",
|
||||||
|
Rarity: ItemRarity.None
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
override start() {
|
||||||
|
this.formatAllPossibleCombinations();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { CommandExec } from "./cmdtypes.ts";
|
import { CommandExec } from "./cmdtypes.ts";
|
||||||
|
|
||||||
export interface CommandOptions {
|
export interface CommandOptions {
|
||||||
key: string[],
|
key: string[],
|
||||||
subcommands?: Command[],
|
subcommands?: Command[],
|
||||||
@@ -16,7 +15,6 @@ export default class Command {
|
|||||||
exec: CommandExec | null;
|
exec: CommandExec | null;
|
||||||
validate: z.ZodTuple | null;
|
validate: z.ZodTuple | null;
|
||||||
help: string | null;
|
help: string | null;
|
||||||
#argsLength: number;
|
|
||||||
|
|
||||||
constructor(options: CommandOptions) {
|
constructor(options: CommandOptions) {
|
||||||
this.subCmds = options.subcommands || [];
|
this.subCmds = options.subcommands || [];
|
||||||
@@ -24,7 +22,6 @@ export default class Command {
|
|||||||
this.exec = options.exec || null;
|
this.exec = options.exec || null;
|
||||||
this.validate = options.zod || null;
|
this.validate = options.zod || null;
|
||||||
this.help = options.help || null;
|
this.help = options.help || null;
|
||||||
this.#argsLength = options.zod ? options.zod.def.items.length : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getKey() {
|
getKey() {
|
||||||
@@ -40,12 +37,10 @@ export default class Command {
|
|||||||
const cmd = this.subCmds.find(cmd => cmd.getKey().includes(root));
|
const cmd = this.subCmds.find(cmd => cmd.getKey().includes(root));
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
const newArgs = args.slice(1);
|
const newArgs = args.slice(1);
|
||||||
if (cmd.#argsLength && newArgs.length !== cmd.#argsLength && cmd.help) return new Error(cmd.help);
|
|
||||||
if (cmd.validate) {
|
if (cmd.validate) {
|
||||||
const res = cmd.validate.safeParse(newArgs);
|
const res = cmd.validate.safeParse(newArgs);
|
||||||
if (res.success) return cmd.dispatch(...res.data);
|
if (res.success) return cmd.dispatch(...res.data);
|
||||||
else if (cmd.help) return new Error(cmd.help);
|
else if (cmd.help) return new Error(cmd.help);
|
||||||
else if (cmd.#argsLength) return new Error(`'${root}' validation error: expected ${cmd.#argsLength} args, got ${newArgs.length}`);
|
|
||||||
else return new Error(`'${root}' validation error`);
|
else return new Error(`'${root}' validation error`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ export default class GameConfigsBase extends ServerContentBase {
|
|||||||
|
|
||||||
#kvKey = 'gameconfigs';
|
#kvKey = 'gameconfigs';
|
||||||
|
|
||||||
#usageError = new Error("Usage: <key: string> [<value: string>]");
|
|
||||||
|
|
||||||
async getGameConfig(key: string) {
|
async getGameConfig(key: string) {
|
||||||
return (await this.kv.getKv().get<string>([this.#kvKey, key])).value;
|
return (await this.kv.getKv().get<string>([this.#kvKey, key])).value;
|
||||||
}
|
}
|
||||||
@@ -25,17 +23,16 @@ export default class GameConfigsBase extends ServerContentBase {
|
|||||||
this.kv.getKv().list<string>({ prefix: [this.#kvKey] })
|
this.kv.getKv().list<string>({ prefix: [this.#kvKey] })
|
||||||
)).map(val =>
|
)).map(val =>
|
||||||
({ Key: val.key[1] as string, Value: val.value })
|
({ Key: val.key[1] as string, Value: val.value })
|
||||||
).filter(gc => gc.Key && gc.Value); // ensure both the key and value exist
|
).filter(gc => gc.Key && typeof gc.Value == 'string');
|
||||||
}
|
}
|
||||||
|
|
||||||
override start(): void {
|
override start() {
|
||||||
this.server.Commands.addRootCommand(new Command({
|
this.server.Commands.addRootCommand(new Command({
|
||||||
key: ['gameconfigs', 'gameconfig', 'gc'],
|
key: ['gameconfigs', 'gameconfig', 'gc'],
|
||||||
subcommands: [
|
subcommands: [
|
||||||
new Command({
|
new Command({
|
||||||
key: ['get', 'g'],
|
key: ['get', 'g'],
|
||||||
exec: async (key: string) => {
|
exec: async (key: string) => {
|
||||||
if (!key) return this.#usageError;
|
|
||||||
return await this.getGameConfig(key);
|
return await this.getGameConfig(key);
|
||||||
},
|
},
|
||||||
zod: z.tuple([
|
zod: z.tuple([
|
||||||
@@ -45,20 +42,18 @@ export default class GameConfigsBase extends ServerContentBase {
|
|||||||
}),
|
}),
|
||||||
new Command({
|
new Command({
|
||||||
key: ['set', 's'],
|
key: ['set', 's'],
|
||||||
exec: async (key: string, value: string) => {
|
exec: async (key: string, ...values: string[]) => {
|
||||||
if (!key || !value) return this.#usageError;
|
const value = values.join(' ');
|
||||||
return await this.setGameConfig(key, value);
|
return await this.setGameConfig(key, value);
|
||||||
},
|
},
|
||||||
zod: z.tuple([
|
zod: z.tuple([
|
||||||
z.string(),
|
z.string(),
|
||||||
z.string()
|
]).rest(z.string().optional()),
|
||||||
]),
|
help: 'Set a new GameConfig: <key: string>, ...<values: string[] (joined by " ")>'
|
||||||
help: 'Set a new GameConfig: <key: string>, <value: string>'
|
|
||||||
}),
|
}),
|
||||||
new Command({
|
new Command({
|
||||||
key: ['del', 'rem', 'd', 'remove'],
|
key: ['del', 'rem', 'd', 'remove'],
|
||||||
exec: async (key: string) => {
|
exec: async (key: string) => {
|
||||||
if (!key) return this.#usageError;
|
|
||||||
return await this.delGameConfig(key);
|
return await this.delGameConfig(key);
|
||||||
},
|
},
|
||||||
zod: z.tuple([
|
zod: z.tuple([
|
||||||
@@ -72,6 +67,7 @@ export default class GameConfigsBase extends ServerContentBase {
|
|||||||
const configs = await this.getAllGameConfigs();
|
const configs = await this.getAllGameConfigs();
|
||||||
return configs;
|
return configs;
|
||||||
},
|
},
|
||||||
|
zod: z.tuple([]),
|
||||||
help: 'List all saved GameConfigs'
|
help: 'List all saved GameConfigs'
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
|||||||
16
src/server/presence/base.ts
Normal file
16
src/server/presence/base.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { ServerContentBase } from "../ContentBase.ts";
|
||||||
|
import type Profile from "../profiles/profile.ts";
|
||||||
|
|
||||||
|
class Presence {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PresenceBase extends ServerContentBase {
|
||||||
|
|
||||||
|
#presenceMap: Map<Profile, Presence> = new Map();
|
||||||
|
|
||||||
|
getPresence() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
35
src/server/profiles/content/Avatar.ts
Normal file
35
src/server/profiles/content/Avatar.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import z from "zod";
|
||||||
|
import ProfileContentManager from "./base.ts";
|
||||||
|
|
||||||
|
export const profileAvatarSchema = z.object({
|
||||||
|
OutfitSelections: z.string(),
|
||||||
|
HairColor: z.string(),
|
||||||
|
SkinColor: z.string(),
|
||||||
|
FaceFeatures: z.string(),
|
||||||
|
});
|
||||||
|
export type ProfileAvatar = z.infer<typeof profileAvatarSchema>;
|
||||||
|
|
||||||
|
export class ProfileAvatarManager extends ProfileContentManager {
|
||||||
|
|
||||||
|
#key = this.profile.constructProfilePropertyKey('avatar');
|
||||||
|
#noAvatar: ProfileAvatar = {
|
||||||
|
OutfitSelections: "",
|
||||||
|
HairColor: "",
|
||||||
|
SkinColor: "",
|
||||||
|
FaceFeatures: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAvatar(): Promise<ProfileAvatar> {
|
||||||
|
const item = await this.kv.getKv().get(this.#key);
|
||||||
|
if (item.value) {
|
||||||
|
const parsed = profileAvatarSchema.safeParse(item.value);
|
||||||
|
if (parsed.success) return parsed.data;
|
||||||
|
else return this.#noAvatar;
|
||||||
|
} else return this.#noAvatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setAvatar(outfit: ProfileAvatar) {
|
||||||
|
await this.kv.getKv().set(this.#key, outfit);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
34
src/server/profiles/content/Matchmaking.ts
Normal file
34
src/server/profiles/content/Matchmaking.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import ProfileContentManager from "./base.ts";
|
||||||
|
|
||||||
|
export enum DeviceClass {
|
||||||
|
Unknown,
|
||||||
|
VR,
|
||||||
|
Screen,
|
||||||
|
Mobile,
|
||||||
|
VRLow,
|
||||||
|
Quest2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ProfileMatchmakingManager extends ProfileContentManager {
|
||||||
|
|
||||||
|
#deviceClassKey = this.profile.constructProfilePropertyKey('deviceclass');
|
||||||
|
async setLastDeviceClass(dc: DeviceClass) {
|
||||||
|
await this.kv.getKv().set(this.#deviceClassKey, dc);
|
||||||
|
}
|
||||||
|
async getLastDeviceClass(): Promise<DeviceClass | null> {
|
||||||
|
return (await this.kv.getKv().get<DeviceClass>(this.#deviceClassKey)).value || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loginLockKey = this.profile.constructProfilePropertyKey('loginlock');
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import KV from "../persistence/kv.ts";
|
|||||||
import { ProfileRole } from "../platforms/base.ts";
|
import { ProfileRole } from "../platforms/base.ts";
|
||||||
import { type ServerBase } from "../server.ts";
|
import { type ServerBase } from "../server.ts";
|
||||||
import { type SignalRSocketHandler } from "../socket/signalr/socket.ts";
|
import { type SignalRSocketHandler } from "../socket/signalr/socket.ts";
|
||||||
|
import { ProfileAvatarManager } from "./content/Avatar.ts";
|
||||||
import { ProfileSettingsManager } from "./content/Settings.ts";
|
import { ProfileSettingsManager } from "./content/Settings.ts";
|
||||||
import ProfileManagerBase from "./manager.ts";
|
import ProfileManagerBase from "./manager.ts";
|
||||||
import { recNetAccountSchema, SelfAccount, type RecNetAccount } from "./types/profile.ts";
|
import { recNetAccountSchema, SelfAccount, type RecNetAccount } from "./types/profile.ts";
|
||||||
@@ -17,6 +18,7 @@ class Profile {
|
|||||||
#selfAcc: SelfAccount;
|
#selfAcc: SelfAccount;
|
||||||
|
|
||||||
Settings: ProfileSettingsManager;
|
Settings: ProfileSettingsManager;
|
||||||
|
Avatar: ProfileAvatarManager;
|
||||||
|
|
||||||
constructor(acc: SelfAccount, kv: KV, server: ServerBase) {
|
constructor(acc: SelfAccount, kv: KV, server: ServerBase) {
|
||||||
this.#id = acc.accountId;
|
this.#id = acc.accountId;
|
||||||
@@ -25,6 +27,7 @@ class Profile {
|
|||||||
this.#server = server;
|
this.#server = server;
|
||||||
|
|
||||||
this.Settings = new ProfileSettingsManager(this, this.#kv);
|
this.Settings = new ProfileSettingsManager(this, this.#kv);
|
||||||
|
this.Avatar = new ProfileAvatarManager(this, this.#kv);
|
||||||
}
|
}
|
||||||
|
|
||||||
async #saveSelfAcc() {
|
async #saveSelfAcc() {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { AvatarContentBase } from "./avatars/base.ts";
|
||||||
import { EventManager } from "./baseevent.ts";
|
import { EventManager } from "./baseevent.ts";
|
||||||
import { CommandsBase } from "./commands/commands.ts";
|
import { CommandsBase } from "./commands/commands.ts";
|
||||||
import GameConfigsBase from "./gameconfigs/base.ts";
|
import GameConfigsBase from "./gameconfigs/base.ts";
|
||||||
@@ -16,6 +17,7 @@ class ServerBase extends EventManager<ServerEvents> {
|
|||||||
GameConfigs = new GameConfigsBase(this, 'gameconfigs', true);
|
GameConfigs = new GameConfigsBase(this, 'gameconfigs', true);
|
||||||
Commands = new CommandsBase(this, 'commands');
|
Commands = new CommandsBase(this, 'commands');
|
||||||
Platforms = new PlatformsManager(this, 'platforms', true);
|
Platforms = new PlatformsManager(this, 'platforms', true);
|
||||||
|
Avatars = new AvatarContentBase(this, 'avatars');
|
||||||
}
|
}
|
||||||
|
|
||||||
const Server = new ServerBase();
|
const Server = new ServerBase();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
import { SocketTarget } from "./targets/targetbase.ts";
|
import { SocketTarget } from "./targets/targetbase.ts";
|
||||||
import type Profile from "../../profiles/profile.ts";
|
import type Profile from "../../profiles/profile.ts";
|
||||||
import { detailedLog } from "../../../main.ts";
|
import { detailedLog } from "../../../main.ts";
|
||||||
|
import { PlayerSocketSubscriptionTarget } from "./targets/SubscribeToPlayers.ts";
|
||||||
|
|
||||||
const logmessages = true;
|
const logmessages = true;
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ export class SignalRSocketHandler {
|
|||||||
|
|
||||||
player.setSocketHandler(this);
|
player.setSocketHandler(this);
|
||||||
|
|
||||||
//this.#Targets.set('SubscribeToPlayers', new PlayerSocketSubscriptionTarget(this));
|
this.#Targets.set('SubscribeToPlayers', new PlayerSocketSubscriptionTarget(this));
|
||||||
|
|
||||||
for (const target of this.#Targets.values()) target.onInit();
|
for (const target of this.#Targets.values()) target.onInit();
|
||||||
|
|
||||||
@@ -55,7 +56,9 @@ export class SignalRSocketHandler {
|
|||||||
if (!targetExec) return { type: TargetResultType.NotATarget } as TargetResultNotATarget;
|
if (!targetExec) return { type: TargetResultType.NotATarget } as TargetResultNotATarget;
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
return { type: TargetResultType.Success, data: await targetExec.exec(args) } as TargetResultSuccess<T>;
|
const parsed = targetExec.zod.safeParse(args);
|
||||||
|
if (parsed.success) return { type: TargetResultType.Success, data: await targetExec.exec(args) } as TargetResultSuccess<T>;
|
||||||
|
else return { type: TargetResultType.Failure, err: "Argument parse failure" } as TargetResultFailure;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.#log.w(`Target '${target}' function error: ${err}`);
|
this.#log.w(`Target '${target}' function error: ${err}`);
|
||||||
if (err instanceof Error) return { type: TargetResultType.Failure, err: err } as TargetResultFailure;
|
if (err instanceof Error) return { type: TargetResultType.Failure, err: err } as TargetResultFailure;
|
||||||
|
|||||||
18
src/server/socket/signalr/targets/SubscribeToPlayers.ts
Normal file
18
src/server/socket/signalr/targets/SubscribeToPlayers.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import z from "zod";
|
||||||
|
import { SocketTarget } from "./targetbase.ts";
|
||||||
|
|
||||||
|
export class PlayerSocketSubscriptionTarget extends SocketTarget {
|
||||||
|
|
||||||
|
#ids: number[] = [];
|
||||||
|
|
||||||
|
override zod = z.tuple([]).rest(z.number());
|
||||||
|
|
||||||
|
override exec(...ids: number[]) {
|
||||||
|
this.#ids = ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
getIds() {
|
||||||
|
return this.#ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,26 +1,12 @@
|
|||||||
/* Galvanic Corrosion - Rec Room custom server for communities.
|
import z from "zod";
|
||||||
<https://gitea.proxnet.dev/zombieb/galvanic-corrosion>
|
|
||||||
Copyright (C) 2025 @zombieb (Discord / proxnet Gitea)
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published
|
|
||||||
by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
import type { SignalRSocketHandler } from "../socket.ts";
|
import type { SignalRSocketHandler } from "../socket.ts";
|
||||||
|
|
||||||
export class SocketTarget {
|
export class SocketTarget {
|
||||||
|
|
||||||
socket: SignalRSocketHandler;
|
socket: SignalRSocketHandler;
|
||||||
|
|
||||||
|
zod: z.ZodTuple = z.tuple([]);
|
||||||
|
|
||||||
constructor(socket: SignalRSocketHandler) {
|
constructor(socket: SignalRSocketHandler) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
}
|
}
|
||||||
@@ -33,8 +19,7 @@ export class SocketTarget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// deno-lint-ignore require-await
|
exec(_args: unknown): Promise<unknown> | unknown {
|
||||||
async exec(_args: unknown) {
|
|
||||||
throw new Error("Execution for this target is not set.");
|
throw new Error("Execution for this target is not set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
src/util/path.ts
Normal file
3
src/util/path.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { platform } from "node:process";
|
||||||
|
|
||||||
|
export const RootPath = Deno.mainModule.substring(platform == 'win32' ? 8 : 7, Deno.mainModule.length - 11);
|
||||||
Reference in New Issue
Block a user