1
0
forked from noxious/server

#161: Set default value for createdAt field, store zone chats into database

This commit is contained in:
Dennis Postma 2024-11-14 20:45:37 +01:00
parent 3f8f8745eb
commit 460308d555
12 changed files with 109 additions and 29 deletions

6
package-lock.json generated
View File

@ -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",

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `Chat` MODIFY `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3);

View File

@ -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())
}

View File

@ -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

View File

@ -0,0 +1,49 @@
import prisma from '../utilities/prisma'
import { Chat } from '@prisma/client'
class ChatRepository {
async getById(id: number): Promise<Chat | null> {
return prisma.chat.findUnique({
where: { id },
include: {
character: true,
zone: true
}
})
}
async getAll(): Promise<Chat[]> {
return prisma.chat.findMany({
include: {
character: true,
zone: true
}
})
}
async getByCharacterId(characterId: number): Promise<Chat[]> {
return prisma.chat.findMany({
where: {
characterId
},
include: {
character: true,
zone: true
}
})
}
async getByZoneId(zoneId: number): Promise<Chat[]> {
return prisma.chat.findMany({
where: {
zoneId
},
include: {
character: true,
zone: true
}
})
}
}
export default new ChatRepository()

View File

@ -27,12 +27,14 @@ export class Server {
*/
constructor() {
this.app = express()
this.app.use(cors({
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)

View File

@ -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<boolean> {
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

View File

@ -45,7 +45,7 @@ export default class CharacterConnectEvent {
private async hasActiveCharacter(): Promise<boolean> {
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) {

View File

@ -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<void> {
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
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)
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)