2026-01-07 14:23:06 +00:00
|
|
|
|
import { Injectable, Inject, forwardRef } from '@nestjs/common';
|
2026-01-03 14:07:04 +00:00
|
|
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
|
|
|
|
import { customAlphabet } from 'nanoid';
|
2026-01-07 14:32:51 +00:00
|
|
|
|
import { RoomEventsService } from '../game/room-events.service';
|
2026-01-03 14:07:04 +00:00
|
|
|
|
|
|
|
|
|
|
const nanoid = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 6);
|
|
|
|
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
|
export class RoomsService {
|
2026-01-07 14:23:06 +00:00
|
|
|
|
constructor(
|
|
|
|
|
|
private prisma: PrismaService,
|
2026-01-07 14:32:51 +00:00
|
|
|
|
@Inject(forwardRef(() => RoomEventsService))
|
|
|
|
|
|
private roomEventsService: RoomEventsService,
|
2026-01-07 14:23:06 +00:00
|
|
|
|
) {}
|
2026-01-03 14:07:04 +00:00
|
|
|
|
|
2026-01-06 20:12:36 +00:00
|
|
|
|
async createRoom(hostId: string, questionPackId?: string, settings?: any) {
|
2026-01-03 14:07:04 +00:00
|
|
|
|
const code = nanoid();
|
|
|
|
|
|
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours
|
|
|
|
|
|
|
2026-01-06 20:33:44 +00:00
|
|
|
|
// Remove undefined values from settings and ensure questionPackId is handled correctly
|
|
|
|
|
|
const cleanSettings = settings ? { ...settings } : {};
|
|
|
|
|
|
if ('questionPackId' in cleanSettings) {
|
|
|
|
|
|
delete cleanSettings.questionPackId;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-03 14:07:04 +00:00
|
|
|
|
const room = await this.prisma.room.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
code,
|
|
|
|
|
|
hostId,
|
|
|
|
|
|
expiresAt,
|
2026-01-06 20:33:44 +00:00
|
|
|
|
...cleanSettings,
|
|
|
|
|
|
questionPackId: questionPackId || null,
|
2026-01-03 14:07:04 +00:00
|
|
|
|
},
|
|
|
|
|
|
include: {
|
|
|
|
|
|
host: true,
|
|
|
|
|
|
questionPack: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
await this.prisma.participant.create({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
userId: hostId,
|
|
|
|
|
|
roomId: room.id,
|
|
|
|
|
|
name: room.host.name || 'Host',
|
|
|
|
|
|
role: 'HOST',
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return room;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async getRoomByCode(code: string) {
|
|
|
|
|
|
return this.prisma.room.findUnique({
|
|
|
|
|
|
where: { code },
|
|
|
|
|
|
include: {
|
|
|
|
|
|
host: true,
|
|
|
|
|
|
participants: {
|
|
|
|
|
|
include: { user: true },
|
|
|
|
|
|
},
|
|
|
|
|
|
questionPack: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async joinRoom(roomId: string, userId: string, name: string, role: 'PLAYER' | 'SPECTATOR') {
|
2026-01-07 14:23:06 +00:00
|
|
|
|
const participant = await this.prisma.participant.create({
|
2026-01-03 14:07:04 +00:00
|
|
|
|
data: {
|
|
|
|
|
|
userId,
|
|
|
|
|
|
roomId,
|
|
|
|
|
|
name,
|
|
|
|
|
|
role,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
2026-01-07 14:23:06 +00:00
|
|
|
|
|
|
|
|
|
|
// Получаем обновленную комнату со всеми участниками
|
|
|
|
|
|
const room = await this.prisma.room.findUnique({
|
|
|
|
|
|
where: { id: roomId },
|
|
|
|
|
|
include: {
|
|
|
|
|
|
host: true,
|
|
|
|
|
|
participants: {
|
|
|
|
|
|
include: { user: true },
|
|
|
|
|
|
},
|
|
|
|
|
|
questionPack: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Отправляем событие roomUpdate всем клиентам в комнате
|
2026-01-07 14:32:51 +00:00
|
|
|
|
if (room) {
|
|
|
|
|
|
this.roomEventsService.emitRoomUpdate(room.code, room);
|
2026-01-07 14:23:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return participant;
|
2026-01-03 14:07:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async updateRoomStatus(roomId: string, status: 'WAITING' | 'PLAYING' | 'FINISHED') {
|
|
|
|
|
|
return this.prisma.room.update({
|
|
|
|
|
|
where: { id: roomId },
|
|
|
|
|
|
data: { status },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async updateParticipantScore(participantId: string, score: number) {
|
|
|
|
|
|
return this.prisma.participant.update({
|
|
|
|
|
|
where: { id: participantId },
|
|
|
|
|
|
data: { score },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-01-06 20:12:36 +00:00
|
|
|
|
|
|
|
|
|
|
async updateQuestionPack(roomId: string, questionPackId: string) {
|
|
|
|
|
|
return this.prisma.room.update({
|
|
|
|
|
|
where: { id: roomId },
|
|
|
|
|
|
data: {
|
|
|
|
|
|
questionPackId,
|
|
|
|
|
|
currentQuestionIndex: 0,
|
|
|
|
|
|
revealedAnswers: {},
|
|
|
|
|
|
},
|
|
|
|
|
|
include: {
|
|
|
|
|
|
host: true,
|
|
|
|
|
|
participants: {
|
|
|
|
|
|
include: { user: true },
|
|
|
|
|
|
},
|
|
|
|
|
|
questionPack: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-01-08 13:18:07 +00:00
|
|
|
|
|
|
|
|
|
|
async updateCustomQuestions(roomId: string, questions: any) {
|
|
|
|
|
|
const room = await this.prisma.room.update({
|
|
|
|
|
|
where: { id: roomId },
|
|
|
|
|
|
data: {
|
|
|
|
|
|
customQuestions: questions,
|
|
|
|
|
|
currentQuestionIndex: 0,
|
|
|
|
|
|
revealedAnswers: {},
|
|
|
|
|
|
},
|
|
|
|
|
|
include: {
|
|
|
|
|
|
host: true,
|
|
|
|
|
|
participants: {
|
|
|
|
|
|
include: { user: true },
|
|
|
|
|
|
},
|
|
|
|
|
|
questionPack: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.roomEventsService.emitCustomQuestionsUpdated(room.code, room);
|
|
|
|
|
|
return room;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async getEffectiveQuestions(roomId: string) {
|
|
|
|
|
|
const room = await this.prisma.room.findUnique({
|
|
|
|
|
|
where: { id: roomId },
|
|
|
|
|
|
include: { questionPack: true },
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!room) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Если есть кастомные вопросы, используем их
|
|
|
|
|
|
if (room.customQuestions) {
|
|
|
|
|
|
return room.customQuestions;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Иначе используем вопросы из пака
|
|
|
|
|
|
if (room.questionPack) {
|
|
|
|
|
|
return room.questionPack.questions;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async updateRoomSettings(roomId: string, settings: any) {
|
|
|
|
|
|
const room = await this.prisma.room.update({
|
|
|
|
|
|
where: { id: roomId },
|
|
|
|
|
|
data: settings,
|
|
|
|
|
|
include: {
|
|
|
|
|
|
host: true,
|
|
|
|
|
|
participants: {
|
|
|
|
|
|
include: { user: true },
|
|
|
|
|
|
},
|
|
|
|
|
|
questionPack: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.roomEventsService.emitRoomUpdate(room.code, room);
|
|
|
|
|
|
return room;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async restartGame(roomId: string) {
|
|
|
|
|
|
await this.prisma.room.update({
|
|
|
|
|
|
where: { id: roomId },
|
|
|
|
|
|
data: {
|
|
|
|
|
|
status: 'WAITING',
|
|
|
|
|
|
currentQuestionIndex: 0,
|
|
|
|
|
|
revealedAnswers: {},
|
|
|
|
|
|
currentPlayerId: null,
|
|
|
|
|
|
isGameOver: false,
|
|
|
|
|
|
answeredQuestions: 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
await this.prisma.participant.updateMany({
|
|
|
|
|
|
where: { roomId },
|
|
|
|
|
|
data: { score: 0 },
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const room = await this.prisma.room.findUnique({
|
|
|
|
|
|
where: { id: roomId },
|
|
|
|
|
|
include: {
|
|
|
|
|
|
host: true,
|
|
|
|
|
|
participants: {
|
|
|
|
|
|
include: { user: true },
|
|
|
|
|
|
},
|
|
|
|
|
|
questionPack: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (room) {
|
|
|
|
|
|
this.roomEventsService.emitGameRestarted(room.code, room);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return room;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async setCurrentPlayer(roomId: string, playerId: string) {
|
|
|
|
|
|
const room = await this.prisma.room.update({
|
|
|
|
|
|
where: { id: roomId },
|
|
|
|
|
|
data: { currentPlayerId: playerId },
|
|
|
|
|
|
include: {
|
|
|
|
|
|
host: true,
|
|
|
|
|
|
participants: {
|
|
|
|
|
|
include: { user: true },
|
|
|
|
|
|
},
|
|
|
|
|
|
questionPack: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.roomEventsService.emitCurrentPlayerChanged(room.code, { playerId });
|
|
|
|
|
|
return room;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async kickPlayer(roomId: string, participantId: string) {
|
|
|
|
|
|
await this.prisma.participant.update({
|
|
|
|
|
|
where: { id: participantId },
|
|
|
|
|
|
data: { isActive: false },
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const room = await this.prisma.room.findUnique({
|
|
|
|
|
|
where: { id: roomId },
|
|
|
|
|
|
include: {
|
|
|
|
|
|
host: true,
|
|
|
|
|
|
participants: {
|
|
|
|
|
|
include: { user: true },
|
|
|
|
|
|
},
|
|
|
|
|
|
questionPack: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (room) {
|
|
|
|
|
|
this.roomEventsService.emitPlayerKicked(room.code, { participantId, room });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return room;
|
|
|
|
|
|
}
|
2026-01-03 14:07:04 +00:00
|
|
|
|
}
|