forked from noxious/server
#161: Set default value for createdAt field, store zone chats into database
This commit is contained in:
parent
3f8f8745eb
commit
460308d555
6
package-lock.json
generated
6
package-lock.json
generated
@ -949,9 +949,9 @@
|
|||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/bullmq": {
|
"node_modules/bullmq": {
|
||||||
"version": "5.25.6",
|
"version": "5.26.1",
|
||||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.25.6.tgz",
|
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.26.1.tgz",
|
||||||
"integrity": "sha512-jxpa/DB02V20CqBAgyqpQazT630CJm0r4fky8EchH3mcJAomRtKXLS6tRA0J8tb29BDGlr/LXhlUuZwdBJBSdA==",
|
"integrity": "sha512-XuxCGFlC1PQ2i1JHQiB9dqkqKQILMwQpU7ipi+cT/dzJaoXVcS0/IByUz6SsZ3xyOQY3twPt6G7J2d5GrsJuEA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cron-parser": "^4.6.0",
|
"cron-parser": "^4.6.0",
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Chat` MODIFY `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3);
|
@ -11,7 +11,7 @@
|
|||||||
// npx prisma migrate deploy
|
// npx prisma migrate deploy
|
||||||
|
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
previewFeatures = ["prismaSchemaFolder"]
|
previewFeatures = ["prismaSchemaFolder"]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,5 +27,5 @@ model Chat {
|
|||||||
zoneId Int
|
zoneId Int
|
||||||
zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade)
|
zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade)
|
||||||
message String
|
message String
|
||||||
createdAt DateTime
|
createdAt DateTime @default(now())
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import prisma from '../utilities/prisma'
|
|||||||
class ZoneCharacter {
|
class ZoneCharacter {
|
||||||
public readonly character: Character
|
public readonly character: Character
|
||||||
public isMoving: boolean = false
|
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) {
|
constructor(character: Character) {
|
||||||
this.character = character
|
this.character = character
|
||||||
|
@ -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()
|
@ -27,12 +27,14 @@ export class Server {
|
|||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
this.app = express()
|
this.app = express()
|
||||||
this.app.use(cors({
|
this.app.use(
|
||||||
origin: config.CLIENT_URL,
|
cors({
|
||||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // Add supported methods
|
origin: config.CLIENT_URL,
|
||||||
allowedHeaders: ['Content-Type', 'Authorization'], // Add allowed headers
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // Add supported methods
|
||||||
credentials: true // Enable if you're using cookies/credentials
|
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.json())
|
||||||
this.app.use(express.urlencoded({ extended: true }))
|
this.app.use(express.urlencoded({ extended: true }))
|
||||||
this.http = httpServer(this.app)
|
this.http = httpServer(this.app)
|
||||||
|
@ -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
|
@ -45,7 +45,7 @@ export default class CharacterConnectEvent {
|
|||||||
|
|
||||||
private async hasActiveCharacter(): Promise<boolean> {
|
private async hasActiveCharacter(): Promise<boolean> {
|
||||||
const characters = await CharacterRepository.getByUserId(this.socket.userId!)
|
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) {
|
private async connectCharacter(characterId: number) {
|
||||||
@ -53,7 +53,7 @@ export default class CharacterConnectEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private emitError(message: string): void {
|
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}`)
|
gameLogger.error('character:connect error', `Player ${this.socket.userId}: ${message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,30 +17,27 @@ export default class ChatMessageEvent {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public listen(): void {
|
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 {
|
try {
|
||||||
if (!data.message || isCommand(data.message)) {
|
if (!data.message || isCommand(data.message)) {
|
||||||
callback(false)
|
return callback(false)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const zoneCharacter = ZoneManager.getCharacter(this.socket.characterId!)
|
const zoneCharacter = ZoneManager.getCharacter(this.socket.characterId!)
|
||||||
if (!zoneCharacter) {
|
if (!zoneCharacter) {
|
||||||
gameLogger.error('chat:send_message error', 'Character not found')
|
gameLogger.error('chat:message error', 'Character not found')
|
||||||
callback(false)
|
return callback(false)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const character = zoneCharacter.character
|
const character = zoneCharacter.character
|
||||||
|
|
||||||
const zone = await ZoneRepository.getById(character.zoneId)
|
const zone = await ZoneRepository.getById(character.zoneId)
|
||||||
if (!zone) {
|
if (!zone) {
|
||||||
gameLogger.error('chat:send_message error', 'Zone not found')
|
gameLogger.error('chat:message error', 'Zone not found')
|
||||||
callback(false)
|
return callback(false)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatService = new ChatService()
|
const chatService = new ChatService()
|
||||||
@ -50,7 +47,7 @@ export default class ChatMessageEvent {
|
|||||||
|
|
||||||
callback(false)
|
callback(false)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
gameLogger.error('chat:send_message error', error.message)
|
gameLogger.error('chat:message error', error.message)
|
||||||
callback(false)
|
callback(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ export default class ObjectRemoveEvent {
|
|||||||
const finalFilePath = getPublicPath('objects', data.object + '.png')
|
const finalFilePath = getPublicPath('objects', data.object + '.png')
|
||||||
fs.unlink(finalFilePath, (err) => {
|
fs.unlink(finalFilePath, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
gameMasterLogger.error(`Error deleting object ${data.object}: ${(err.message)}`)
|
gameMasterLogger.error(`Error deleting object ${data.object}: ${err.message}`)
|
||||||
callback(false)
|
callback(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ export default class ObjectRemoveEvent {
|
|||||||
const finalFilePath = getPublicPath('objects', data.object + '.png')
|
const finalFilePath = getPublicPath('objects', data.object + '.png')
|
||||||
fs.unlink(finalFilePath, (err) => {
|
fs.unlink(finalFilePath, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
gameMasterLogger.error(`Error deleting object ${data.object}: ${(err.message)}`)
|
gameMasterLogger.error(`Error deleting object ${data.object}: ${err.message}`)
|
||||||
callback(false)
|
callback(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ export default class CharacterMove {
|
|||||||
// If already moving, cancel current movement and wait for it to fully stop
|
// If already moving, cancel current movement and wait for it to fully stop
|
||||||
if (zoneCharacter.isMoving) {
|
if (zoneCharacter.isMoving) {
|
||||||
zoneCharacter.isMoving = false
|
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)
|
const path = await this.characterMoveService.calculatePath(zoneCharacter.character, positionX, positionY)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user