g
This commit is contained in:
@@ -43,7 +43,8 @@ await routeImporter(AppRoot.app, 'src/', [
|
||||
'routes/api',
|
||||
'routes/auth',
|
||||
'routes/accounts',
|
||||
'routes/match'
|
||||
'routes/match',
|
||||
'routes/img'
|
||||
]);
|
||||
|
||||
// deno-lint-ignore require-await
|
||||
@@ -59,7 +60,7 @@ const onListen = async () => {
|
||||
await Deno.mkdir('persist');
|
||||
|
||||
await Promise.all(Object.values(Server).map(base => ((base as ServerContentBase).kvInit ? (base as ServerContentBase).kvInit() : undefined)));
|
||||
Object.values(Server).forEach(base => ((base as ServerContentBase).start ? (base as ServerContentBase).start() : undefined));
|
||||
Server.emit('server.start', undefined);
|
||||
}
|
||||
|
||||
const netConfig = getNetConfig();
|
||||
@@ -162,7 +163,7 @@ Deno.addSignalListener('SIGINT', () => {
|
||||
for (const socket of consoleSockets) socket.destroy();
|
||||
for (const socket of gameSockets) socket.sendNotification(PushNotificationId.ModerationQuitGame);
|
||||
|
||||
Object.values(Server).forEach(base => ((base as ServerContentBase).destroy ? (base as ServerContentBase).destroy() : undefined));
|
||||
Server.emit('server.destroy', undefined);
|
||||
});
|
||||
|
||||
Server.Commands.addRootCommand(new Command({
|
||||
|
||||
3
src/routes/api/routes/messages.ts
Normal file
3
src/routes/api/routes/messages.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { createHonoRoute } from "../../../util/import.ts";
|
||||
|
||||
export const route = createHonoRoute("/messages");
|
||||
29
src/routes/api/routes/playerReputation.ts
Normal file
29
src/routes/api/routes/playerReputation.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import z from "zod";
|
||||
import { createHonoRoute } from "../../../util/import.ts";
|
||||
import { typedZValidator } from "../../../util/validators.ts";
|
||||
import { authenticate } from "../../../util/api.ts";
|
||||
import Server from "../../../server/server.ts";
|
||||
|
||||
export const route = createHonoRoute("/playerReputation");
|
||||
|
||||
const getRepIdParamSchema = z.object({
|
||||
id: z.coerce.number().min(0).max(2_147_483_647)
|
||||
})
|
||||
route.app.get('/v1/:id', typedZValidator('param', getRepIdParamSchema), authenticate, async c => {
|
||||
const { id } = c.req.valid('param');
|
||||
const prof = await Server.Profiles.get(id);
|
||||
|
||||
if (!prof) return c.status(404);
|
||||
else return c.json({
|
||||
AccountId: id,
|
||||
Noteriety: 0.0,
|
||||
CheerGeneral: 0,
|
||||
CheerHelpful: 0,
|
||||
CheerGreatHost: 0,
|
||||
CheerSportsman: 0,
|
||||
CheerCreative: 0,
|
||||
CheerCredit: 0,
|
||||
SubscriberCount: 0,
|
||||
SubscribedCount: 0,
|
||||
});
|
||||
});
|
||||
74
src/routes/img/base.ts
Normal file
74
src/routes/img/base.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import z from "zod";
|
||||
import Server from "../../server/server.ts";
|
||||
import { createHonoRoute } from "../../util/import.ts";
|
||||
import { typedZValidator } from "../../util/validators.ts";
|
||||
import { FileType, File } from "../../server/content/base.ts";
|
||||
import sharp from "sharp";
|
||||
import path from "node:path";
|
||||
import { RootPath } from "../../util/path.ts";
|
||||
import Logging from "@proxnet/undead-logging";
|
||||
|
||||
export const route = createHonoRoute('/img');
|
||||
|
||||
async function convertImage(query: {cropSquare?: boolean | undefined;width?: number | undefined;height?: number | undefined;}, data: Uint8Array<ArrayBufferLike>): Promise<Uint8Array<ArrayBufferLike>> {
|
||||
const image = sharp(data);
|
||||
const rootMetadata = await image.metadata();
|
||||
const rootWidth = rootMetadata.width;
|
||||
const rootHeight = rootMetadata.height;
|
||||
const squareSize = Math.min(rootWidth, rootHeight);
|
||||
if (query.cropSquare) image.resize(squareSize, squareSize);
|
||||
|
||||
const newImage = sharp(await image.png().toBuffer());
|
||||
if (query.width && query.height)
|
||||
newImage.resize(query.width, query.height);
|
||||
else if (query.width)
|
||||
newImage.resize(query.width);
|
||||
else if (query.height)
|
||||
newImage.resize(undefined, query.height);
|
||||
|
||||
return await newImage.png().toBuffer();
|
||||
}
|
||||
|
||||
const imgNameParamSchema = z.object({
|
||||
imgName: z.string().min(4).includes('.')
|
||||
});
|
||||
const imgQuerySchema = z.object({
|
||||
cropSquare: z.coerce.boolean().optional(),
|
||||
width: z.coerce.number().max(3840).optional(),
|
||||
height: z.coerce.number().max(2160).optional(),
|
||||
});
|
||||
route.app.get('/:imgName',
|
||||
typedZValidator('param', imgNameParamSchema),
|
||||
typedZValidator('query', imgQuerySchema),
|
||||
async c => {
|
||||
|
||||
const { imgName } = c.req.valid('param');
|
||||
const query = c.req.valid('query');
|
||||
|
||||
const file: File | null = await Server.Content.getFile(`img/${imgName}`);
|
||||
let raw: Uint8Array<ArrayBuffer> | null = null;
|
||||
if (!file) {
|
||||
try {
|
||||
raw = await Deno.readFile(path.join(RootPath, "res/baseimg/", imgName));
|
||||
} catch {
|
||||
raw = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!raw && file && file.Meta.Type !== FileType.Image) return c.status(404);
|
||||
|
||||
try {
|
||||
let result: Uint8Array<ArrayBufferLike> | null = null;
|
||||
if (file) result = await convertImage(query, file.Data);
|
||||
else if (raw) result = await convertImage(query, raw);
|
||||
|
||||
if (result) return c.body(result, 200, { "Cache-Control": "public, no-transform, max-age=1800", "Content-Type": "image/png" });
|
||||
else return c.status(404);
|
||||
|
||||
} catch (err) {
|
||||
new Logging("ImageRoute").w(`Sharp error: ${err}`);
|
||||
return c.status(500);
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
@@ -8,6 +8,9 @@ export class ServerContentBase {
|
||||
constructor(server: ServerBase, id: string, kv?: boolean) {
|
||||
this.server = server;
|
||||
this.kv = new KV(id, kv);
|
||||
|
||||
server.on('server.start', this.start);
|
||||
server.on('server.destroy', this.destroy);
|
||||
}
|
||||
|
||||
async kvInit() {
|
||||
@@ -19,7 +22,7 @@ export class ServerContentBase {
|
||||
*
|
||||
* Override me!
|
||||
*/
|
||||
start() {
|
||||
protected start() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,7 +31,7 @@ export class ServerContentBase {
|
||||
*
|
||||
* Override me!
|
||||
*/
|
||||
destroy() {
|
||||
protected destroy() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ export class AvatarContentBase extends ServerContentBase {
|
||||
return this.#rawImport;
|
||||
}
|
||||
|
||||
// unused
|
||||
formatAllPossibleCombinations() {
|
||||
const parsed = JSON.parse(Deno.readTextFileSync(path.join(RootPath, '/res/avatar.json'))) as AvatarImport;
|
||||
function formatVisualData(data: {
|
||||
@@ -68,8 +69,11 @@ export class AvatarContentBase extends ServerContentBase {
|
||||
Rarity: ItemRarity.None
|
||||
}));
|
||||
}
|
||||
importAllItems() {
|
||||
this.#rawImport = JSON.parse(Deno.readTextFileSync(path.join(RootPath, '/res/AvatarItems.json'))) as AvatarItemExport[];
|
||||
}
|
||||
|
||||
override start() {
|
||||
this.formatAllPossibleCombinations();
|
||||
this.importAllItems();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ interface MetaFile {
|
||||
SavedBy?: Profile
|
||||
OriginalFilename?: string
|
||||
}
|
||||
interface File {
|
||||
export interface File {
|
||||
Meta: MetaFile,
|
||||
Data: Uint8Array<ArrayBufferLike>
|
||||
}
|
||||
@@ -172,8 +172,7 @@ export class ServerContentManager extends ServerContentBase {
|
||||
Data: data
|
||||
}
|
||||
return file;
|
||||
} catch (err) {
|
||||
this.#log.w(`Could not get file "${path}": ${err}`);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CloudRegionCode } from "../../util/photon.ts";
|
||||
import type Profile from "../profiles/profile.ts";
|
||||
import { RoomLocation } from "./base.ts";
|
||||
import { RoomInstance, RoomLocation } from "./base.ts";
|
||||
|
||||
export class Instance {
|
||||
|
||||
@@ -8,7 +9,18 @@ export class Instance {
|
||||
#players: Set<Profile> = new Set();
|
||||
|
||||
#instanceId: number;
|
||||
//#roomId: number;
|
||||
//#subRoomId: number;
|
||||
#location: RoomLocation;
|
||||
//#name: string;
|
||||
//#maxCapacity: number;
|
||||
#isFull: boolean = false;
|
||||
//#isPrivate: boolean;
|
||||
#isInProgress: boolean = false;
|
||||
#photonRegionId: string = CloudRegionCode.us;
|
||||
//#photonRoomId: string;
|
||||
#dataBlob?: string;
|
||||
#eventId?: string
|
||||
|
||||
constructor(options: {
|
||||
id: number,
|
||||
@@ -30,10 +42,18 @@ export class Instance {
|
||||
}
|
||||
addPlayer(profile: Profile) {
|
||||
this.#players.add(profile);
|
||||
|
||||
}
|
||||
|
||||
getCreatedAt() {
|
||||
return this.#createdAt;
|
||||
}
|
||||
|
||||
export() {
|
||||
/*const inst: RoomInstance = {
|
||||
roomInstanceId: this.#instanceId,
|
||||
|
||||
}*/
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export interface RoomInstance {
|
||||
photonRegionId: string,
|
||||
photonRoomId: string,
|
||||
dataBlob?: string,
|
||||
eventId?: string
|
||||
eventId?: number
|
||||
}
|
||||
|
||||
export class InstanceManager extends ServerContentBase {
|
||||
|
||||
@@ -55,7 +55,6 @@ export class Presence {
|
||||
isOnline ?
|
||||
this.#statusVisibility :
|
||||
PlayerStatusVisibility.Offline;
|
||||
this.#roomInstance = this.#profile.getInstance();
|
||||
|
||||
this.#server.emit('presence.update', { profile: this.#profile, presence: this });
|
||||
}
|
||||
|
||||
40
src/server/profiles/content/Messages.ts
Normal file
40
src/server/profiles/content/Messages.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import ProfileContentManager from "./base.ts";
|
||||
|
||||
enum MessageType {
|
||||
GameInvite,
|
||||
GameInviteDeclined,
|
||||
GameJoinFailed,
|
||||
PartyActivitySwitch,
|
||||
FriendInvite,
|
||||
VoteToKick,
|
||||
GameInviteV2,
|
||||
PartyActivitySwitchV2,
|
||||
RequestGameInvite = 10,
|
||||
RequestGameInviteDeclined,
|
||||
FriendStatusOnline = 20,
|
||||
TextMessage = 30,
|
||||
FriendRequestAccepted = 40,
|
||||
PlayerCheer = 50,
|
||||
PlayerCheerAnonymous,
|
||||
RoomCoOwnerAdded = 60,
|
||||
RoomCoOwnerRemoved,
|
||||
RoomCoOwnerInvited,
|
||||
CreatorPublishedNewRoom = 70,
|
||||
PlayerAttendingEvent = 80,
|
||||
PlayerEventInvitation,
|
||||
GroupInvitation = 90,
|
||||
PlayerJoinedGroup,
|
||||
CoachMessage = 100
|
||||
}
|
||||
|
||||
interface MessageBase {
|
||||
Id: number,
|
||||
FromPlayerId: number,
|
||||
SentTime: string,
|
||||
Type: MessageType,
|
||||
Data: string,
|
||||
}
|
||||
|
||||
export class ProfileMessageManager extends ProfileContentManager {
|
||||
|
||||
}
|
||||
5
src/server/profiles/content/Reputation.ts
Normal file
5
src/server/profiles/content/Reputation.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import ProfileContentManager from "./base.ts";
|
||||
|
||||
export class ProfileReputationManager extends ProfileContentManager {
|
||||
|
||||
}
|
||||
5
src/server/profiles/content/Subscriptions.ts
Normal file
5
src/server/profiles/content/Subscriptions.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import ProfileContentManager from "./base.ts";
|
||||
|
||||
export class ProfileSubscriptionsManager extends ProfileContentManager {
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RoomInstance } from "../instances/base.ts";
|
||||
import { Instance } from "../instances/Instance.ts";
|
||||
import KV from "../persistence/kv.ts";
|
||||
import { PlatformMask, ProfileRole } from "../platforms/types.ts";
|
||||
import { type ServerBase } from "../server.ts";
|
||||
@@ -6,7 +6,10 @@ import { type SignalRSocketHandler } from "../socket/signalr/socket.ts";
|
||||
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 { ProfileReputationManager } from "./content/Reputation.ts";
|
||||
import { ProfileSettingsManager } from "./content/Settings.ts";
|
||||
import { ProfileSubscriptionsManager } from "./content/Subscriptions.ts";
|
||||
import ProfileManagerBase from "./manager.ts";
|
||||
import { recNetAccountSchema, SelfAccount, type RecNetAccount } from "./types/profile.ts";
|
||||
|
||||
@@ -16,7 +19,7 @@ class Profile {
|
||||
#kv: KV;
|
||||
|
||||
#socket: SignalRSocketHandler | null = null;
|
||||
#instance: RoomInstance | null = null;
|
||||
#instance: Instance | null = null;
|
||||
|
||||
#server: ServerBase;
|
||||
#selfAcc: SelfAccount;
|
||||
@@ -26,6 +29,9 @@ class Profile {
|
||||
Settings: ProfileSettingsManager;
|
||||
Avatar: ProfileAvatarManager;
|
||||
Matchmaking: ProfileMatchmakingManager;
|
||||
Reputation: ProfileReputationManager;
|
||||
Subscriptions: ProfileSubscriptionsManager;
|
||||
Messages: ProfileMessageManager;
|
||||
|
||||
constructor(acc: SelfAccount, kv: KV, server: ServerBase) {
|
||||
this.#id = acc.accountId;
|
||||
@@ -36,6 +42,9 @@ class Profile {
|
||||
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);
|
||||
}
|
||||
|
||||
async #saveSelfAcc() {
|
||||
@@ -121,7 +130,7 @@ class Profile {
|
||||
getInstance() {
|
||||
return this.#instance;
|
||||
}
|
||||
setInstance(inst: RoomInstance) {
|
||||
setInstance(inst: Instance) {
|
||||
this.#instance = inst;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ interface ServerEvents {
|
||||
'profile.roleupdate': RoleUpdateEvent,
|
||||
'profile.update': ProfileUpdateEvent,
|
||||
'presence.update': PresenceUpdateEvent,
|
||||
'server.start': undefined,
|
||||
'server.destroy': undefined,
|
||||
}
|
||||
|
||||
class ServerBase extends EventManager<ServerEvents> {
|
||||
|
||||
0
src/server/storefronts/test.ts
Normal file
0
src/server/storefronts/test.ts
Normal file
@@ -1,19 +1,23 @@
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import type { MiddlewareHandler } from "@hono/hono";
|
||||
import { z } from "zod";
|
||||
import { z, ZodObject } from "zod";
|
||||
import type { HonoEnv } from "./types.ts";
|
||||
|
||||
// thanks claude, this hurt my brain!
|
||||
export const typedZValidator = <T extends z.ZodSchema>(
|
||||
target: 'query' | 'json' | 'form' | 'header' | 'param' | 'cookie',
|
||||
schema: T
|
||||
|
||||
export const typedZValidator = <
|
||||
Target extends 'query' | 'json' | 'form' | 'header' | 'param' | 'cookie',
|
||||
Schema extends ZodObject
|
||||
>(
|
||||
target: Target,
|
||||
schema: Schema
|
||||
) => {
|
||||
return zValidator(target, schema) as MiddlewareHandler<
|
||||
HonoEnv,
|
||||
string,
|
||||
{
|
||||
in: { [K in typeof target]: z.input<T> };
|
||||
out: { [K in typeof target]: z.output<T> };
|
||||
in: { [K in Target]: z.input<Schema> };
|
||||
out: { [K in Target]: z.output<Schema> };
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user