1
0
forked from noxious/server

Moved service logic from repo to service, minor improvements, working hair customisation proof of concept

This commit is contained in:
Dennis Postma 2024-11-23 16:48:07 +01:00
parent d5c7cd0294
commit 1017013032
10 changed files with 118 additions and 71 deletions

View File

@ -1,5 +1,5 @@
import { Character } from '@prisma/client' import { Character } from '@prisma/client'
import { CharacterService } from '../services/character/characterService' import { CharacterService } from '../services/characterService'
class ZoneCharacter { class ZoneCharacter {
public readonly character: Character public readonly character: Character

View File

@ -5,6 +5,13 @@ class CharacterHairRepository {
async getAll(): Promise<CharacterHair[]> { async getAll(): Promise<CharacterHair[]> {
return prisma.characterHair.findMany() return prisma.characterHair.findMany()
} }
async getIsEnabledForCharCreationHair(): Promise<CharacterHair[]> {
return prisma.characterHair.findMany({
where: {
isEnabledForCharCreation: true
}
})
}
} }
export default new CharacterHairRepository() export default new CharacterHairRepository()

View File

@ -1,9 +1,8 @@
import prisma from '../utilities/prisma' // Import the global Prisma instance import prisma from '../utilities/prisma' // Import the global Prisma instance
import { Character } from '@prisma/client'
import { appLogger } from '../utilities/logger' import { appLogger } from '../utilities/logger'
class CharacterRepository { class CharacterRepository {
async getByUserId(userId: number): Promise<Character[] | null> { async getByUserId(userId: number) {
try { try {
return await prisma.character.findMany({ return await prisma.character.findMany({
where: { where: {
@ -15,6 +14,11 @@ class CharacterRepository {
include: { include: {
sprite: true sprite: true
} }
},
hair: {
include: {
sprite: true
}
} }
} }
}) })
@ -25,7 +29,7 @@ class CharacterRepository {
} }
} }
async getByUserAndId(userId: number, characterId: number): Promise<Character | null> { async getByUserAndId(userId: number, characterId: number) {
try { try {
return await prisma.character.findFirst({ return await prisma.character.findFirst({
where: { where: {
@ -38,6 +42,11 @@ class CharacterRepository {
include: { include: {
sprite: true sprite: true
} }
},
hair: {
include: {
sprite: true
}
} }
} }
}) })
@ -48,7 +57,7 @@ class CharacterRepository {
} }
} }
async getById(id: number): Promise<Character | null> { async getById(id: number) {
try { try {
return await prisma.character.findUnique({ return await prisma.character.findUnique({
where: { where: {
@ -60,6 +69,11 @@ class CharacterRepository {
include: { include: {
sprite: true sprite: true
} }
},
hair: {
include: {
sprite: true
}
} }
} }
}) })
@ -70,40 +84,7 @@ class CharacterRepository {
} }
} }
async updatePosition(id: number, positionX: number, positionY: number): Promise<Character | null> { async getByName(name: string) {
try {
return await prisma.character.update({
where: {
id: id
},
data: {
positionX,
positionY
}
})
} catch (error: any) {
// Handle error
appLogger.error(`Failed to update character: ${error instanceof Error ? error.message : String(error)}`)
return null
}
}
async deleteByUserIdAndId(userId: number, characterId: number): Promise<Character | null> {
try {
return await prisma.character.delete({
where: {
userId,
id: characterId
}
})
} catch (error: any) {
// Handle error
appLogger.error(`Failed to delete character by user ID and character ID: ${error instanceof Error ? error.message : String(error)}`)
return null
}
}
async getByName(name: string): Promise<Character | null> {
try { try {
return await prisma.character.findFirst({ return await prisma.character.findFirst({
where: { where: {
@ -115,6 +96,11 @@ class CharacterRepository {
include: { include: {
sprite: true sprite: true
} }
},
hair: {
include: {
sprite: true
}
} }
} }
}) })

View File

@ -1,9 +1,9 @@
import { AStar } from '../../utilities/character/aStar' import { AStar } from '../utilities/character/aStar'
import ZoneManager from '../../managers/zoneManager' import ZoneManager from '../managers/zoneManager'
import prisma from '../../utilities/prisma' import prisma from '../utilities/prisma'
import Rotation from '../../utilities/character/rotation' import Rotation from '../utilities/character/rotation'
import { gameLogger } from '../../utilities/logger' import { appLogger, gameLogger } from '../utilities/logger'
import { Character, CharacterGender, CharacterRace } from '@prisma/client' import { Character } from '@prisma/client'
interface Position { interface Position {
x: number x: number
@ -13,6 +13,52 @@ interface Position {
export class CharacterService { export class CharacterService {
private readonly MOVEMENT_DELAY_MS = 250 private readonly MOVEMENT_DELAY_MS = 250
async create(name: string, userId: number) {
return prisma.character.create({
data: {
name,
userId
// characterTypeId: 1 // @TODO set to chosen character type
}
})
}
async updateHair(characterId: number, hairId: number | null) {
await prisma.character.update({
where: { id: characterId },
data: {
hairId
}
})
}
async deleteByUserIdAndId(userId: number, characterId: number): Promise<Character | null> {
try {
return await prisma.character.delete({
where: {
userId,
id: characterId
}
})
} catch (error: any) {
// Handle error
appLogger.error(`Failed to delete character by user ID and character ID: ${error instanceof Error ? error.message : String(error)}`)
return null
}
}
async updateCharacterPosition(id: number, positionX: number, positionY: number, rotation: number, zoneId: number) {
await prisma.character.update({
where: { id },
data: {
positionX,
positionY,
rotation,
zoneId
}
})
}
public updatePosition(character: Character, position: Position, newZoneId?: number): void { public updatePosition(character: Character, position: Position, newZoneId?: number): void {
if (!this.isValidPosition(position)) { if (!this.isValidPosition(position)) {
gameLogger.error(`Invalid position coordinates: ${position.x}, ${position.y}`) gameLogger.error(`Invalid position coordinates: ${position.x}, ${position.y}`)
@ -55,26 +101,4 @@ export class CharacterService {
private isValidPosition(position: Position): boolean { private isValidPosition(position: Position): boolean {
return Number.isFinite(position.x) && Number.isFinite(position.y) && position.x >= 0 && position.y >= 0 return Number.isFinite(position.x) && Number.isFinite(position.y) && position.x >= 0 && position.y >= 0
} }
async create(name: string, userId: number) {
return prisma.character.create({
data: {
name,
userId
// characterTypeId: 1 // @TODO set to chosen character type
}
})
}
async updateCharacterPosition(id: number, positionX: number, positionY: number, rotation: number, zoneId: number) {
await prisma.character.update({
where: { id },
data: {
positionX,
positionY,
rotation,
zoneId
}
})
}
} }

View File

@ -0,0 +1,22 @@
import { Server } from 'socket.io'
import { CharacterHair } from '@prisma/client'
import { TSocket } from '../../../utilities/types'
import characterHairRepository from '../../../repositories/characterHairRepository'
interface IPayload {}
export default class characterHairListEvent {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
public listen(): void {
this.socket.on('character:hair:list', this.handleEvent.bind(this))
}
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
const items = await characterHairRepository.getIsEnabledForCharCreationHair()
callback(items)
}
}

View File

@ -3,9 +3,11 @@ import { TSocket } from '../../utilities/types'
import CharacterRepository from '../../repositories/characterRepository' import CharacterRepository from '../../repositories/characterRepository'
import { gameLogger } from '../../utilities/logger' import { gameLogger } from '../../utilities/logger'
import ZoneManager from '../../managers/zoneManager' import ZoneManager from '../../managers/zoneManager'
import { CharacterService } from '../../services/characterService'
interface CharacterConnectPayload { interface CharacterConnectPayload {
characterId: number characterId: number
hairId?: number
} }
export default class CharacterConnectEvent { export default class CharacterConnectEvent {
@ -18,7 +20,7 @@ export default class CharacterConnectEvent {
this.socket.on('character:connect', this.handleCharacterConnect.bind(this)) this.socket.on('character:connect', this.handleCharacterConnect.bind(this))
} }
private async handleCharacterConnect({ characterId }: CharacterConnectPayload): Promise<void> { private async handleCharacterConnect({ characterId, hairId }: CharacterConnectPayload): Promise<void> {
if (!this.socket.userId) { if (!this.socket.userId) {
this.emitError('User not authenticated') this.emitError('User not authenticated')
return return
@ -30,6 +32,10 @@ export default class CharacterConnectEvent {
return return
} }
// Update hair
const characterService = new CharacterService()
await characterService.updateHair(characterId, hairId ?? null)
const character = await this.connectCharacter(characterId) const character = await this.connectCharacter(characterId)
if (!character) { if (!character) {
this.emitError('Character not found or does not belong to this user') this.emitError('Character not found or does not belong to this user')

View File

@ -2,7 +2,7 @@ import { Server } from 'socket.io'
import { TSocket } from '../../utilities/types' import { TSocket } from '../../utilities/types'
import { Character } from '@prisma/client' import { Character } from '@prisma/client'
import CharacterRepository from '../../repositories/characterRepository' import CharacterRepository from '../../repositories/characterRepository'
import { CharacterService } from '../../services/character/characterService' import { CharacterService } from '../../services/characterService'
import { ZCharacterCreate } from '../../utilities/zodTypes' import { ZCharacterCreate } from '../../utilities/zodTypes'
import { gameLogger } from '../../utilities/logger' import { gameLogger } from '../../utilities/logger'
import { ZodError } from 'zod' import { ZodError } from 'zod'

View File

@ -2,6 +2,7 @@ import { Server } from 'socket.io'
import { TSocket } from '../../utilities/types' import { TSocket } from '../../utilities/types'
import { Character, Zone } from '@prisma/client' import { Character, Zone } from '@prisma/client'
import CharacterRepository from '../../repositories/characterRepository' import CharacterRepository from '../../repositories/characterRepository'
import { CharacterService } from '../../services/characterService'
type TypePayload = { type TypePayload = {
characterId: number characterId: number
@ -24,7 +25,8 @@ export default class CharacterDeleteEvent {
private async handleCharacterDelete(data: TypePayload, callback: (response: TypeResponse) => void): Promise<any> { private async handleCharacterDelete(data: TypePayload, callback: (response: TypeResponse) => void): Promise<any> {
try { try {
await CharacterRepository.deleteByUserIdAndId(this.socket.userId!, data.characterId!) const characterService = new CharacterService()
await characterService.deleteByUserIdAndId(this.socket.userId!, data.characterId!)
const characters: Character[] = (await CharacterRepository.getByUserId(this.socket.userId!)) as Character[] const characters: Character[] = (await CharacterRepository.getByUserId(this.socket.userId!)) as Character[]

View File

@ -1,6 +1,6 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket, ZoneEventTileWithTeleport } from '../../utilities/types' import { TSocket, ZoneEventTileWithTeleport } from '../../utilities/types'
import { CharacterService } from '../../services/character/characterService' import { CharacterService } from '../../services/characterService'
import { ZoneEventTileService } from '../../services/zoneEventTileService' import { ZoneEventTileService } from '../../services/zoneEventTileService'
import Rotation from '../../utilities/character/rotation' import Rotation from '../../utilities/character/rotation'
import { gameLogger } from '../../utilities/logger' import { gameLogger } from '../../utilities/logger'