forked from noxious/server
Moved service logic from repo to service, minor improvements, working hair customisation proof of concept
This commit is contained in:
parent
d5c7cd0294
commit
1017013032
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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')
|
||||||
|
@ -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'
|
||||||
|
@ -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[]
|
||||||
|
|
||||||
|
@ -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'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user