diff --git a/package-lock.json b/package-lock.json index 05e5ae1..1b429bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -949,9 +949,9 @@ "license": "BSD-3-Clause" }, "node_modules/bullmq": { - "version": "5.25.6", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.25.6.tgz", - "integrity": "sha512-jxpa/DB02V20CqBAgyqpQazT630CJm0r4fky8EchH3mcJAomRtKXLS6tRA0J8tb29BDGlr/LXhlUuZwdBJBSdA==", + "version": "5.26.1", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.26.1.tgz", + "integrity": "sha512-XuxCGFlC1PQ2i1JHQiB9dqkqKQILMwQpU7ipi+cT/dzJaoXVcS0/IByUz6SsZ3xyOQY3twPt6G7J2d5GrsJuEA==", "license": "MIT", "dependencies": { "cron-parser": "^4.6.0", diff --git a/prisma/migrations/20241114191537_set_default_value_for_created_at_in_chat/migration.sql b/prisma/migrations/20241114191537_set_default_value_for_created_at_in_chat/migration.sql new file mode 100644 index 0000000..0357346 --- /dev/null +++ b/prisma/migrations/20241114191537_set_default_value_for_created_at_in_chat/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE `Chat` MODIFY `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3); diff --git a/prisma/schema/schema.prisma b/prisma/schema/schema.prisma index 7916015..75b85f9 100644 --- a/prisma/schema/schema.prisma +++ b/prisma/schema/schema.prisma @@ -11,7 +11,7 @@ // npx prisma migrate deploy generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" previewFeatures = ["prismaSchemaFolder"] } @@ -27,5 +27,5 @@ model Chat { zoneId Int zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade) message String - createdAt DateTime + createdAt DateTime @default(now()) } diff --git a/src/models/zoneCharacter.ts b/src/models/zoneCharacter.ts index 8705451..4439f1e 100644 --- a/src/models/zoneCharacter.ts +++ b/src/models/zoneCharacter.ts @@ -4,7 +4,7 @@ import prisma from '../utilities/prisma' class ZoneCharacter { public readonly character: Character public isMoving: boolean = false - public currentPath: Array<{ x: number; y: number }> | null = null; + public currentPath: Array<{ x: number; y: number }> | null = null constructor(character: Character) { this.character = character diff --git a/src/repositories/chatRepository.ts b/src/repositories/chatRepository.ts index e69de29..d20019e 100644 --- a/src/repositories/chatRepository.ts +++ b/src/repositories/chatRepository.ts @@ -0,0 +1,49 @@ +import prisma from '../utilities/prisma' +import { Chat } from '@prisma/client' + +class ChatRepository { + async getById(id: number): Promise { + return prisma.chat.findUnique({ + where: { id }, + include: { + character: true, + zone: true + } + }) + } + + async getAll(): Promise { + return prisma.chat.findMany({ + include: { + character: true, + zone: true + } + }) + } + + async getByCharacterId(characterId: number): Promise { + return prisma.chat.findMany({ + where: { + characterId + }, + include: { + character: true, + zone: true + } + }) + } + + async getByZoneId(zoneId: number): Promise { + return prisma.chat.findMany({ + where: { + zoneId + }, + include: { + character: true, + zone: true + } + }) + } +} + +export default new ChatRepository() diff --git a/src/server.ts b/src/server.ts index 9d516aa..c427d3f 100644 --- a/src/server.ts +++ b/src/server.ts @@ -27,12 +27,14 @@ export class Server { */ constructor() { this.app = express() - this.app.use(cors({ - origin: config.CLIENT_URL, - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // Add supported methods - allowedHeaders: ['Content-Type', 'Authorization'], // Add allowed headers - credentials: true // Enable if you're using cookies/credentials - })) + this.app.use( + cors({ + origin: config.CLIENT_URL, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // Add supported methods + allowedHeaders: ['Content-Type', 'Authorization'], // Add allowed headers + credentials: true // Enable if you're using cookies/credentials + }) + ) this.app.use(express.json()) this.app.use(express.urlencoded({ extended: true })) this.http = httpServer(this.app) diff --git a/src/services/chatService.ts b/src/services/chatService.ts index e69de29..d8bb979 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -0,0 +1,30 @@ +import prisma from '../utilities/prisma' +import { gameLogger } from '../utilities/logger' +import { Server } from 'socket.io' +import { TSocket } from '../utilities/types' +import ChatRepository from '../repositories/chatRepository' + +class ChatService { + async sendZoneMessage(io: Server, socket: TSocket, message: string, characterId: number, zoneId: number): Promise { + try { + const newChat = await prisma.chat.create({ + data: { + characterId, + zoneId, + message + } + }) + + const chat = await ChatRepository.getById(newChat.id) + if (!chat) return false + + io.to(zoneId.toString()).emit('chat:message', chat) + return true + } catch (error: any) { + gameLogger.error(`Failed to save chat message: ${error instanceof Error ? error.message : String(error)}`) + return false + } + } +} + +export default ChatService diff --git a/src/socketEvents/character/connect.ts b/src/socketEvents/character/connect.ts index 6fab1a2..fb66a4a 100644 --- a/src/socketEvents/character/connect.ts +++ b/src/socketEvents/character/connect.ts @@ -45,7 +45,7 @@ export default class CharacterConnectEvent { private async hasActiveCharacter(): Promise { const characters = await CharacterRepository.getByUserId(this.socket.userId!) - return characters?.some(char => ZoneManager.getCharacter(char.id)) ?? false + return characters?.some((char) => ZoneManager.getCharacter(char.id)) ?? false } private async connectCharacter(characterId: number) { @@ -53,7 +53,7 @@ export default class CharacterConnectEvent { } private emitError(message: string): void { - this.socket.emit('notification', { title: 'Server message', message}) + this.socket.emit('notification', { title: 'Server message', message }) gameLogger.error('character:connect error', `Player ${this.socket.userId}: ${message}`) } @@ -62,4 +62,4 @@ export default class CharacterConnectEvent { this.emitError(`${context}: ${errorMessage}`) gameLogger.error('character:connect error', errorMessage) } -} \ No newline at end of file +} diff --git a/src/socketEvents/chat/message.ts b/src/socketEvents/chat/message.ts index 4548785..7b3b5c3 100644 --- a/src/socketEvents/chat/message.ts +++ b/src/socketEvents/chat/message.ts @@ -17,30 +17,27 @@ export default class ChatMessageEvent { ) {} public listen(): void { - this.socket.on('chat:send_message', this.handleChatMessage.bind(this)) + this.socket.on('chat:message', this.handleEvent.bind(this)) } - private async handleChatMessage(data: TypePayload, callback: (response: boolean) => void): Promise { + private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise { try { if (!data.message || isCommand(data.message)) { - callback(false) - return + return callback(false) } const zoneCharacter = ZoneManager.getCharacter(this.socket.characterId!) if (!zoneCharacter) { - gameLogger.error('chat:send_message error', 'Character not found') - callback(false) - return + gameLogger.error('chat:message error', 'Character not found') + return callback(false) } const character = zoneCharacter.character const zone = await ZoneRepository.getById(character.zoneId) if (!zone) { - gameLogger.error('chat:send_message error', 'Zone not found') - callback(false) - return + gameLogger.error('chat:message error', 'Zone not found') + return callback(false) } const chatService = new ChatService() @@ -50,7 +47,7 @@ export default class ChatMessageEvent { callback(false) } catch (error: any) { - gameLogger.error('chat:send_message error', error.message) + gameLogger.error('chat:message error', error.message) callback(false) } } diff --git a/src/socketEvents/gameMaster/assetManager/characterType/remove.ts b/src/socketEvents/gameMaster/assetManager/characterType/remove.ts index 0006492..c4fc976 100644 --- a/src/socketEvents/gameMaster/assetManager/characterType/remove.ts +++ b/src/socketEvents/gameMaster/assetManager/characterType/remove.ts @@ -39,7 +39,7 @@ export default class ObjectRemoveEvent { const finalFilePath = getPublicPath('objects', data.object + '.png') fs.unlink(finalFilePath, (err) => { if (err) { - gameMasterLogger.error(`Error deleting object ${data.object}: ${(err.message)}`) + gameMasterLogger.error(`Error deleting object ${data.object}: ${err.message}`) callback(false) return } diff --git a/src/socketEvents/gameMaster/assetManager/object/remove.ts b/src/socketEvents/gameMaster/assetManager/object/remove.ts index fe95136..0a86382 100644 --- a/src/socketEvents/gameMaster/assetManager/object/remove.ts +++ b/src/socketEvents/gameMaster/assetManager/object/remove.ts @@ -42,7 +42,7 @@ export default class ObjectRemoveEvent { const finalFilePath = getPublicPath('objects', data.object + '.png') fs.unlink(finalFilePath, (err) => { if (err) { - gameMasterLogger.error(`Error deleting object ${data.object}: ${(err.message)}`) + gameMasterLogger.error(`Error deleting object ${data.object}: ${err.message}`) callback(false) return } diff --git a/src/socketEvents/zone/characterMove.ts b/src/socketEvents/zone/characterMove.ts index c29ae00..a92ce49 100644 --- a/src/socketEvents/zone/characterMove.ts +++ b/src/socketEvents/zone/characterMove.ts @@ -31,7 +31,7 @@ export default class CharacterMove { // If already moving, cancel current movement and wait for it to fully stop if (zoneCharacter.isMoving) { zoneCharacter.isMoving = false - await new Promise(resolve => setTimeout(resolve, 100)) + await new Promise((resolve) => setTimeout(resolve, 100)) } const path = await this.characterMoveService.calculatePath(zoneCharacter.character, positionX, positionY)