diff --git a/src/server/api/match-maker.ts b/src/server/api/match-maker.ts index af84cee2..94a462a9 100644 --- a/src/server/api/match-maker.ts +++ b/src/server/api/match-maker.ts @@ -39,19 +39,19 @@ const makeMatch = () => { let botsInMatch = getRandomInt({ max: MAX_BOTS_PER_MATCH, min: 1 }); const humansInMatch = env.PLAYERS_PER_MATCH - botsInMatch; - const playerIds = lobbyQueue.pickPlayers(humansInMatch); + const humans = lobbyQueue.pickPlayers(humansInMatch); - if (playerIds.length < humansInMatch) { - botsInMatch = env.PLAYERS_PER_MATCH - playerIds.length; + if (humans.length < humansInMatch) { + botsInMatch = env.PLAYERS_PER_MATCH - humans.length; } - const totalPlayers = playerIds.length + botsInMatch; + const totalPlayers = humans.length + botsInMatch; if ( botsInMatch <= MAX_BOTS_PER_MATCH && totalPlayers === env.PLAYERS_PER_MATCH ) { - const match = new Match(playerIds, botsInMatch); + const match = new Match(humans, botsInMatch); matches.set(match.id, match); } diff --git a/src/server/api/routers/lobby.ts b/src/server/api/routers/lobby.ts index ddf43765..2ff0afb8 100644 --- a/src/server/api/routers/lobby.ts +++ b/src/server/api/routers/lobby.ts @@ -3,6 +3,7 @@ import { observable } from "@trpc/server/observable"; import type { QueueUpdatePayload, ReadyToPlayPayload } from "~/types/index.js"; import { lobbyQueue } from "~/server/service/index.js"; +import { selectUserById } from "~/server/db/user.js"; import { createTRPCRouter, protectedProcedure } from "../trpc.js"; import { ee, getOngoingMatchByUserId } from "../match-maker.js"; @@ -34,9 +35,13 @@ export const lobbyRouter = createTRPCRouter({ }; }); }), - join: protectedProcedure.mutation(({ ctx }) => { + join: protectedProcedure.mutation(async ({ ctx }) => { const { id } = ctx.session.user; - const joinCount = lobbyQueue.join(id); + const user = await selectUserById(id); + + if (!user) throw new TRPCError({ code: "FORBIDDEN" }); + + const joinCount = lobbyQueue.join(user); const userIsPlaying = !!getOngoingMatchByUserId(id); if (joinCount > 1 || userIsPlaying) { diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index 41c185f3..922c09a8 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -9,7 +9,7 @@ import { uuid, varchar, } from "drizzle-orm/pg-core"; -import { createInsertSchema } from "drizzle-zod"; +import { createInsertSchema, createSelectSchema } from "drizzle-zod"; import { type z } from "zod"; import { PUBLIC_KEY_LENGTH } from "~/constants/index.js"; @@ -35,7 +35,7 @@ export const usersRelations = relations(users, ({ one }) => ({ }), })); -export const userSchema = createInsertSchema(users); +export const userSchema = createSelectSchema(users); export type User = z.infer; export const userAchievements = bbPgTable("user_achievement", { diff --git a/src/server/service/agent.ts b/src/server/service/agent.ts index 45a24e18..8341fa48 100644 --- a/src/server/service/agent.ts +++ b/src/server/service/agent.ts @@ -182,6 +182,7 @@ export class Agent { votes: [], achievements: [], isOnline: true, + isVerified: true, }; } diff --git a/src/server/service/lobby-queue.ts b/src/server/service/lobby-queue.ts index 07d1caa1..3698ef25 100644 --- a/src/server/service/lobby-queue.ts +++ b/src/server/service/lobby-queue.ts @@ -1,19 +1,21 @@ +import { type User } from "~/server/db/schema.js"; + class LobbyQueue { private _listeners = new Map(); - private _queue: string[] = []; + private _queue: User[] = []; get queue() { return this._queue; } - join(userId: string) { - const count = this._listeners.get(userId); + join(user: User) { + const count = this._listeners.get(user.id); const newCount = count ? count + 1 : 1; - this._listeners.set(userId, newCount); + this._listeners.set(user.id, newCount); if (newCount === 1) { - this._queue.push(userId); + this._queue.push(user); } return newCount; @@ -27,7 +29,7 @@ class LobbyQueue { if (count <= 1) { this._listeners.delete(userId); - const index = this._queue.indexOf(userId); + const index = this._queue.findIndex((u) => u.id === userId); this._queue.splice(index, 1); } else { this._listeners.set(userId, count - 1); @@ -35,15 +37,15 @@ class LobbyQueue { } pickPlayers(humansInMatch: number) { - const ids = this._queue.splice(0, humansInMatch); + const users = this._queue.splice(0, humansInMatch); - ids.forEach((id) => this._listeners.delete(id)); + users.forEach((user) => this._listeners.delete(user.id)); - return ids; + return users; } getPlayerPosition(userId: string) { - return this._queue.indexOf(userId) + 1; + return this._queue.findIndex((u) => u.id === userId) + 1; } has(userId: string) { diff --git a/src/server/service/match.ts b/src/server/service/match.ts index f11ebfe6..08e0e535 100644 --- a/src/server/service/match.ts +++ b/src/server/service/match.ts @@ -20,6 +20,7 @@ import { userAchievements, type UserAchievements, users, + type User, } from "~/server/db/schema.js"; import { selectMatchPlayedByUser, @@ -91,7 +92,7 @@ export class Match { return this._agents; } - constructor(playerIds: string[], botsInMatch: number) { + constructor(users: User[], botsInMatch: number) { this._id = uuid(); this._agents = lodash.range(0, botsInMatch).map(() => { @@ -101,16 +102,18 @@ export class Match { const botPlayers = this.agents.map((agent) => agent.toPlayer()); - const humanPlayers = playerIds.map((id) => { + const humanPlayers = users.map((user) => { const characterId = this.popCharacterId(); - return this.generatePlayer(id, characterId); + return this.generatePlayer(user, characterId); }); this._players = lodash.shuffle([...botPlayers, ...humanPlayers]); this.addPrompt(); - this.initMatch(playerIds).catch((err) => + const userIds = users.map((u) => u.id); + + this.initMatch(userIds).catch((err) => console.error("Error initializing match: ", err), ); @@ -122,7 +125,6 @@ export class Match { private async initMatch(playerIds: string[]) { await this.getPlayerStats(); - await this.checkVerifiedPlayers(); ee.emit("readyToPlay", { roomId: this._id, @@ -172,9 +174,9 @@ export class Match { return characterId; } - private generatePlayer(userId: string, characterId: CharacterId): PlayerType { + private generatePlayer(user: User, characterId: CharacterId): PlayerType { return { - userId, + userId: user.id, characterId, score: 0, isBot: false, @@ -189,6 +191,7 @@ export class Match { correctGuesses: 0, achievements: [], isOnline: true, + isVerified: !!(user.username && user.address), }; } @@ -242,18 +245,6 @@ export class Match { ); } - private async checkVerifiedPlayers() { - const promises = this.players - .filter((player) => !player.isBot) - .map(async (player) => { - if (player.isVerified !== undefined) return; - - const checkPlayer = await selectUserById(player.userId); - player.isVerified = !!(checkPlayer?.username && checkPlayer?.address); - }); - await Promise.allSettled(promises); - } - // TODO: make a proper DB relation with user and matches instead of doing this private async getPlayerStats() { const promises = this.players diff --git a/src/types/match.ts b/src/types/match.ts index 78e58849..76348059 100644 --- a/src/types/match.ts +++ b/src/types/match.ts @@ -63,7 +63,7 @@ export const playerSchema = z.object({ humansFooledScore: z.number(), correctGuesses: z.number(), votes: z.array(z.string().uuid()).optional(), // array of voted ids - isVerified: z.boolean().optional(), + isVerified: z.boolean(), achievements: z.array(achievementIdSchema), // array of achievement ids isOnline: z.boolean(), });