Major refractor, cleaning and improvements.

This commit is contained in:
Dennis Postma 2024-08-24 03:08:43 +02:00
parent e0b376cb83
commit 39f4e79a88
30 changed files with 123 additions and 69 deletions

6
package-lock.json generated
View File

@ -2546,9 +2546,9 @@
} }
}, },
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.6.3", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"license": "0BSD", "license": "0BSD",
"optional": true "optional": true
}, },

View File

@ -20,7 +20,7 @@ export default function (socket: TSocket, io: Server) {
ZoneManager.removeCharacterFromZone(socket.character.zoneId, socket.character) ZoneManager.removeCharacterFromZone(socket.character.zoneId, socket.character)
io.in(socket.character.zoneId.toString()).emit('zone:character:leave', socket.character.id) io.in(socket.character.zoneId.toString()).emit('zoneEditor:character:leave', socket.character.id)
io.emit('character:disconnect', socket.character.id) io.emit('character:disconnect', socket.character.id)
}) })
} }

View File

@ -1,7 +1,7 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import { Object } from '@prisma/client' import { Object } from '@prisma/client'
import ObjectRepository from '../../../repositories/objectRepository' import ObjectRepository from '../../../../repositories/objectRepository'
interface IPayload {} interface IPayload {}

View File

@ -1,8 +1,8 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import path from 'path' import path from 'path'
import fs from 'fs' import fs from 'fs'
import prisma from '../../../utilities/prisma' import prisma from '../../../../utilities/prisma'
interface IPayload { interface IPayload {
object: string object: string

View File

@ -1,6 +1,6 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import prisma from '../../../utilities/prisma' import prisma from '../../../../utilities/prisma'
type Payload = { type Payload = {
id: string id: string

View File

@ -1,9 +1,9 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import { writeFile } from 'node:fs/promises' import { writeFile } from 'node:fs/promises'
import path from 'path' import path from 'path'
import fs from 'fs/promises' import fs from 'fs/promises'
import prisma from '../../../utilities/prisma' import prisma from '../../../../utilities/prisma'
interface IObjectData { interface IObjectData {
[key: string]: Buffer [key: string]: Buffer

View File

@ -1,8 +1,8 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import path from 'path' import path from 'path'
import fs from 'fs/promises' import fs from 'fs/promises'
import prisma from '../../../utilities/prisma' import prisma from '../../../../utilities/prisma'
/** /**
* Handle game master new sprite event * Handle game master new sprite event

View File

@ -1,7 +1,7 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import { Sprite } from '@prisma/client' import { Sprite } from '@prisma/client'
import SpriteRepository from '../../../repositories/spriteRepository' import SpriteRepository from '../../../../repositories/spriteRepository'
interface IPayload {} interface IPayload {}

View File

@ -1,8 +1,8 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import prisma from '../../../utilities/prisma' import prisma from '../../../../utilities/prisma'
type Payload = { type Payload = {
id: string id: string

View File

@ -1,6 +1,6 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import prisma from '../../../utilities/prisma' import prisma from '../../../../utilities/prisma'
import type { Prisma, SpriteAction } from '@prisma/client' import type { Prisma, SpriteAction } from '@prisma/client'
import path from 'path' import path from 'path'
import { writeFile, mkdir } from 'node:fs/promises' import { writeFile, mkdir } from 'node:fs/promises'

View File

@ -1,7 +1,7 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import { Tile } from '@prisma/client' import { Tile } from '@prisma/client'
import TileRepository from '../../../repositories/tileRepository' import TileRepository from '../../../../repositories/tileRepository'
interface IPayload {} interface IPayload {}

View File

@ -1,8 +1,8 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import path from 'path' import path from 'path'
import fs from 'fs' import fs from 'fs'
import prisma from '../../../utilities/prisma' import prisma from '../../../../utilities/prisma'
type Payload = { type Payload = {
id: string id: string

View File

@ -1,6 +1,6 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import prisma from '../../../utilities/prisma' import prisma from '../../../../utilities/prisma'
type Payload = { type Payload = {
id: string id: string

View File

@ -1,9 +1,9 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../../utilities/types'
import { writeFile } from 'node:fs/promises' import { writeFile } from 'node:fs/promises'
import path from 'path' import path from 'path'
import fs from 'fs/promises' import fs from 'fs/promises'
import prisma from '../../../utilities/prisma' import prisma from '../../../../utilities/prisma'
interface ITileData { interface ITileData {
[key: string]: Buffer [key: string]: Buffer

View File

@ -11,7 +11,7 @@ type Payload = {
} }
/** /**
* Handle game master zone create event * Handle game master zoneEditor create event
* @param socket * @param socket
* @param io * @param io
*/ */
@ -36,10 +36,10 @@ export default function (socket: TSocket, io: Server) {
zoneList = await ZoneRepository.getAll() zoneList = await ZoneRepository.getAll()
callback(zoneList) callback(zoneList)
// send over zone and characters to socket // send over zoneEditor and characters to socket
} catch (e) { } catch (e) {
console.error(e) console.error(e)
socket.emit('notification', { message: 'Failed to create zone.' }) socket.emit('notification', { message: 'Failed to create zoneEditor.' })
callback(zoneList) callback(zoneList)
} }
}) })

View File

@ -8,7 +8,7 @@ type Payload = {
} }
/** /**
* Handle game master zone delete event * Handle game master zoneEditor delete event
* @param socket * @param socket
* @param io * @param io
*/ */

View File

@ -8,7 +8,7 @@ interface IPayload {
} }
/** /**
* Handle game master zone request event * Handle game master zoneEditor request event
* @param socket * @param socket
* @param io * @param io
*/ */

View File

@ -26,7 +26,7 @@ interface IPayload {
} }
/** /**
* Handle game master zone update event * Handle game master zoneEditor update event
* @param socket * @param socket
* @param io * @param io
*/ */
@ -66,9 +66,9 @@ export default function (socket: TSocket, io: Server) {
pvp: data.pvp, pvp: data.pvp,
zoneEventTiles: { zoneEventTiles: {
deleteMany: { deleteMany: {
zoneId: data.zoneId // Ensure only event tiles related to the zone are deleted zoneId: data.zoneId // Ensure only event tiles related to the zoneEditor are deleted
}, },
// Save new zone event tiles // Save new zoneEditor event tiles
create: data.zoneEventTiles.map((zoneEventTile) => ({ create: data.zoneEventTiles.map((zoneEventTile) => ({
type: zoneEventTile.type, type: zoneEventTile.type,
positionX: zoneEventTile.positionX, positionX: zoneEventTile.positionX,
@ -86,9 +86,9 @@ export default function (socket: TSocket, io: Server) {
}, },
zoneObjects: { zoneObjects: {
deleteMany: { deleteMany: {
zoneId: data.zoneId // Ensure only objects related to the zone are deleted zoneId: data.zoneId // Ensure only objects related to the zoneEditor are deleted
}, },
// Save new zone objects // Save new zoneEditor objects
create: data.zoneObjects.map((zoneObject) => ({ create: data.zoneObjects.map((zoneObject) => ({
objectId: zoneObject.objectId, objectId: zoneObject.objectId,
depth: zoneObject.depth, depth: zoneObject.depth,
@ -106,7 +106,7 @@ export default function (socket: TSocket, io: Server) {
return return
} }
// send over zone and characters to socket // send over zoneEditor and characters to socket
socket.emit('gm:zone_editor:zone:load', zone) socket.emit('gm:zone_editor:zone:load', zone)
zoneManager.unloadZone(data.zoneId) zoneManager.unloadZone(data.zoneId)

View File

@ -1,5 +1,5 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../../utilities/types' import { ExtendedCharacter, TSocket } from '../../utilities/types'
import ZoneRepository from '../../repositories/zoneRepository' import ZoneRepository from '../../repositories/zoneRepository'
import ZoneManager from '../../managers/zoneManager' import ZoneManager from '../../managers/zoneManager'
import { Character, Zone } from '@prisma/client' import { Character, Zone } from '@prisma/client'
@ -20,7 +20,7 @@ interface IResponse {
* @param io * @param io
*/ */
export default function (socket: TSocket, io: Server) { export default function (socket: TSocket, io: Server) {
socket.on('character:zone:request', async (data: IPayload, callback: (response: IResponse) => void) => { socket.on('zone:characterJoin', async (data: IPayload, callback: (response: IResponse) => void) => {
try { try {
console.log(`---User ${socket.character?.id} has requested zone.`) console.log(`---User ${socket.character?.id} has requested zone.`)
@ -43,11 +43,10 @@ export default function (socket: TSocket, io: Server) {
io.to(socket.character.zoneId.toString()).emit('zone:character:leave', socket.character) io.to(socket.character.zoneId.toString()).emit('zone:character:leave', socket.character)
} }
socket.character.zoneId = zone.id
socket.join(zone.id.toString()) socket.join(zone.id.toString())
// let other clients know of new character // let other clients know of new character
io.to(zone.id.toString()).emit('zone:character:join', socket.character) io.to(zone.id.toString()).emit('zone:characterJoin', socket.character)
// add character to zone manager // add character to zone manager
ZoneManager.addCharacterToZone(zone.id, socket.character as Character) ZoneManager.addCharacterToZone(zone.id, socket.character as Character)
@ -55,8 +54,8 @@ export default function (socket: TSocket, io: Server) {
// send over zone and characters to socket // send over zone and characters to socket
callback({ zone, characters: ZoneManager.getCharactersInZone(zone.id) }) callback({ zone, characters: ZoneManager.getCharactersInZone(zone.id) })
} catch (error: any) { } catch (error: any) {
console.log(`Error requesting zone: ${error.message}`)
logger.error(`Error requesting zone: ${error.message}`) logger.error(`Error requesting zone: ${error.message}`)
socket.disconnect();
} }
}) })
} }

View File

@ -10,7 +10,7 @@ import { Character, Zone } from '@prisma/client'
* @param io * @param io
*/ */
export default function (socket: TSocket, io: Server) { export default function (socket: TSocket, io: Server) {
socket.on('character:zone:leave', async () => { socket.on('zone:characterLeave', async () => {
console.log(`---Socket ${socket.character?.id} has leaved zone.`) console.log(`---Socket ${socket.character?.id} has leaved zone.`)
if (!socket.character) { if (!socket.character) {

View File

@ -13,7 +13,7 @@ type TLoadedZone = {
class ZoneManager { class ZoneManager {
private loadedZones: TLoadedZone[] = [] private loadedZones: TLoadedZone[] = []
// Method to initialize zone manager // Method to initialize zoneEditor manager
public async boot() { public async boot() {
if (!(await ZoneRepository.getById(1))) { if (!(await ZoneRepository.getById(1))) {
const zoneService = new ZoneService() const zoneService = new ZoneService()
@ -29,9 +29,9 @@ class ZoneManager {
logger.info('Zone manager loaded') logger.info('Zone manager loaded')
} }
// Method to handle individual zone loading // Method to handle individual zoneEditor loading
public async loadZone(zone: Zone) { public async loadZone(zone: Zone) {
const grid = await this.getGrid(zone.id) // Create the grid for the zone const grid = await this.getGrid(zone.id) // Create the grid for the zoneEditor
this.loadedZones.push({ this.loadedZones.push({
zone, zone,
characters: [], characters: [],
@ -40,7 +40,7 @@ class ZoneManager {
logger.info(`Zone ID ${zone.id} loaded`) logger.info(`Zone ID ${zone.id} loaded`)
} }
// Method to handle individual zone unloading // Method to handle individual zoneEditor unloading
public unloadZone(zoneId: number) { public unloadZone(zoneId: number) {
this.loadedZones = this.loadedZones.filter((loadedZone) => { this.loadedZones = this.loadedZones.filter((loadedZone) => {
return loadedZone.zone.id !== zoneId return loadedZone.zone.id !== zoneId
@ -56,15 +56,43 @@ class ZoneManager {
// Check if position is walkable // Check if position is walkable
private isPositionWalkable(zoneId: number, x: number, y: number): boolean { private isPositionWalkable(zoneId: number, x: number, y: number): boolean {
const loadedZone = this.loadedZones.find((lz) => lz.zone.id === zoneId) const loadedZone = this.loadedZones.find((lz) => lz.zone.id === zoneId)
return loadedZone ? loadedZone.grid[y][x] === 0 : false if (!loadedZone) {
console.log(`Zone ${zoneId} not found in loadedZones`);
return false;
}
if (!loadedZone.grid) {
console.log(`Grid for zone ${zoneId} is undefined`);
return false;
}
if (!loadedZone.grid[y]) {
console.log(`Row ${y} in grid for zone ${zoneId} is undefined`);
return false;
}
return loadedZone.grid[y][x] === 0;
} }
public addCharacterToZone(zoneId: number, character: Character) { public addCharacterToZone(zoneId: number, character: Character) {
console.log(`Adding character ${character.id} to zone ${zoneId}`);
console.log(`Character position: x=${character.positionX}, y=${character.positionY}`);
const loadedZone = this.loadedZones.find((loadedZone) => { const loadedZone = this.loadedZones.find((loadedZone) => {
return loadedZone.zone.id === zoneId return loadedZone.zone.id === zoneId
}) })
if (loadedZone && this.isPositionWalkable(zoneId, character.positionX, character.positionY)) {
if (!loadedZone) {
console.log(`Zone ${zoneId} not found in loadedZones`);
return;
}
if (this.isPositionWalkable(zoneId, character.positionX, character.positionY)) {
loadedZone.characters.push(character) loadedZone.characters.push(character)
console.log(`Character ${character.id} added to zone ${zoneId}`);
} else {
// set position to 0,0 if not walkable
console.log(`Position (${character.positionX}, ${character.positionY}) is not walkable in zone ${zoneId}`);
character.positionX = 0;
character.positionY = 0;
loadedZone.characters.push(character);
} }
} }

View File

@ -1,16 +1,23 @@
// socket io jwt auth middleware
import { verify } from 'jsonwebtoken' import { verify } from 'jsonwebtoken'
import { TSocket } from '../utilities/types' import { TSocket } from '../utilities/types'
import config from '../utilities/config' import config from '../utilities/config'
import UserRepository from '../repositories/userRepository' import UserRepository from '../repositories/userRepository'
import { User } from '@prisma/client' import { User } from '@prisma/client'
/**
* Socket io jwt auth middleware
* @param socket
* @param next
*/
export async function Authentication(socket: TSocket, next: any) { export async function Authentication(socket: TSocket, next: any) {
if (!socket.request.headers.cookie) { if (!socket.request.headers.cookie) {
console.log('No cookie provided') console.log('No cookie provided')
return next(new Error('Authentication error')) return next(new Error('Authentication error'))
} }
/**
* Parse cookies
*/
const cookies = socket.request.headers.cookie.split('; ').reduce((prev: any, current: any) => { const cookies = socket.request.headers.cookie.split('; ').reduce((prev: any, current: any) => {
const [name, value] = current.split('=') const [name, value] = current.split('=')
prev[name] = value prev[name] = value
@ -19,6 +26,9 @@ export async function Authentication(socket: TSocket, next: any) {
const token = cookies['token'] const token = cookies['token']
/**
* Verify token, if valid, set user on socket and continue
*/
if (token) { if (token) {
verify(token, config.JWT_SECRET, async (err: any, decoded: any) => { verify(token, config.JWT_SECRET, async (err: any, decoded: any) => {
if (err) { if (err) {

View File

@ -14,6 +14,7 @@ class UserRepository {
throw new Error(`Failed to get user by ID: ${error.message}`) throw new Error(`Failed to get user by ID: ${error.message}`)
} }
} }
async getByUsername(username: string): Promise<User | null> { async getByUsername(username: string): Promise<User | null> {
try { try {
return await prisma.user.findUnique({ return await prisma.user.findUnique({

View File

@ -1,13 +1,14 @@
import { Zone } from '@prisma/client' import { Zone, ZoneEventTile, ZoneObject } from '@prisma/client'
import prisma from '../utilities/prisma' // Import the global Prisma instance import prisma from '../utilities/prisma'
import logger from '../utilities/logger'
class ZoneRepository { class ZoneRepository {
async getFirst(): Promise<Zone | null> { async getFirst(): Promise<Zone | null> {
try { try {
return await prisma.zone.findFirst() return await prisma.zone.findFirst()
} catch (error: any) { } catch (error: any) {
// Handle error logger.error(`Failed to get first zone: ${error.message}`)
throw new Error(`Failed to get first zone: ${error.message}`) return null
} }
} }
@ -15,12 +16,12 @@ class ZoneRepository {
try { try {
return await prisma.zone.findMany() return await prisma.zone.findMany()
} catch (error: any) { } catch (error: any) {
// Handle error logger.error(`Failed to get all zone: ${error.message}`)
throw new Error(`Failed to get all zone: ${error.message}`) return []
} }
} }
async getById(id: number) { async getById(id: number): Promise<Zone | null> {
try { try {
return await prisma.zone.findUnique({ return await prisma.zone.findUnique({
where: { where: {
@ -41,12 +42,12 @@ class ZoneRepository {
} }
}) })
} catch (error: any) { } catch (error: any) {
// Handle error logger.error(`Failed to get zone by id: ${error.message}`)
throw new Error(`Failed to get zone by id: ${error.message}`) return null
} }
} }
async getEventTiles(id: number) { async getEventTiles(id: number): Promise<ZoneEventTile[]> {
try { try {
return await prisma.zoneEventTile.findMany({ return await prisma.zoneEventTile.findMany({
where: { where: {
@ -54,12 +55,12 @@ class ZoneRepository {
} }
}) })
} catch (error: any) { } catch (error: any) {
// Handle error logger.error(`Failed to get zone event tiles: ${error.message}`)
throw new Error(`Failed to get zone event tiles: ${error.message}`) return []
} }
} }
async getObjects(id: number) { async getObjects(id: number): Promise<ZoneObject[]> {
try { try {
return await prisma.zoneObject.findMany({ return await prisma.zoneObject.findMany({
where: { where: {
@ -67,8 +68,8 @@ class ZoneRepository {
} }
}) })
} catch (error: any) { } catch (error: any) {
// Handle error logger.error(`Failed to get zone objects: ${error.message}`)
throw new Error(`Failed to get zone objects: ${error.message}`) return []
} }
} }
} }

View File

@ -59,7 +59,7 @@ export class Server {
// Load user manager // Load user manager
await UserManager.boot() await UserManager.boot()
// Load zone manager // Load zoneEditor manager
await ZoneManager.boot() await ZoneManager.boot()
// Load command manager - Disabled for now // Load command manager - Disabled for now

View File

@ -3,7 +3,17 @@ import UserRepository from '../repositories/userRepository'
import prisma from '../utilities/prisma' import prisma from '../utilities/prisma'
import { User } from '@prisma/client' import { User } from '@prisma/client'
/**
* User service
* Handles user login and registration
* @class UserService
*/
class UserService { class UserService {
/**
* Login user
* @param username
* @param password
*/
async login(username: string, password: string): Promise<boolean | User> { async login(username: string, password: string): Promise<boolean | User> {
const user = await UserRepository.getByUsername(username) const user = await UserRepository.getByUsername(username)
if (!user) { if (!user) {
@ -18,6 +28,11 @@ class UserService {
return user return user
} }
/**
* Register user
* @param username
* @param password
*/
async register(username: string, password: string): Promise<boolean | User> { async register(username: string, password: string): Promise<boolean | User> {
const user = await UserRepository.getByUsername(username) const user = await UserRepository.getByUsername(username)
if (user) { if (user) {

View File

@ -24,7 +24,7 @@ class ZoneService {
} }
}) })
console.log('Demo zone created.') console.log('Demo zoneEditor created.')
return true return true
} }
} }