forked from noxious/server
Mass replace parameter order (socket,io)>(io,socket), worked on queueing system
This commit is contained in:
28
src/socketEvents/gameMaster/assetManager/object/list.ts
Normal file
28
src/socketEvents/gameMaster/assetManager/object/list.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import { Object } from '@prisma/client'
|
||||
import ObjectRepository from '../../../../repositories/objectRepository'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
/**
|
||||
* Handle game master list object event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:object:list', async (data: any, callback: (response: Object[]) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return callback([])
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return callback([])
|
||||
}
|
||||
|
||||
// get all objects
|
||||
const objects = await ObjectRepository.getAll()
|
||||
callback(objects)
|
||||
})
|
||||
}
|
53
src/socketEvents/gameMaster/assetManager/object/remove.ts
Normal file
53
src/socketEvents/gameMaster/assetManager/object/remove.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
|
||||
interface IPayload {
|
||||
object: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle game master remove object event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:object:remove', async (data: IPayload, callback: (response: boolean) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return callback(false)
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.object.delete({
|
||||
where: {
|
||||
id: data.object
|
||||
}
|
||||
})
|
||||
|
||||
// get root path
|
||||
const public_folder = path.join(process.cwd(), 'public', 'objects')
|
||||
|
||||
// remove the tile from the disk
|
||||
const finalFilePath = path.join(public_folder, data.object + '.png')
|
||||
fs.unlink(finalFilePath, (err) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
callback(false)
|
||||
return
|
||||
}
|
||||
|
||||
callback(true)
|
||||
})
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
callback(false)
|
||||
}
|
||||
})
|
||||
}
|
55
src/socketEvents/gameMaster/assetManager/object/update.ts
Normal file
55
src/socketEvents/gameMaster/assetManager/object/update.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
|
||||
type Payload = {
|
||||
id: string
|
||||
name: string
|
||||
tags: string[]
|
||||
originX: number
|
||||
originY: number
|
||||
isAnimated: boolean
|
||||
frameSpeed: number
|
||||
frameWidth: number
|
||||
frameHeight: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle game master object update event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:object:update', async (data: Payload, callback: (success: boolean) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return callback(false)
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
try {
|
||||
const object = await prisma.object.update({
|
||||
where: {
|
||||
id: data.id
|
||||
},
|
||||
data: {
|
||||
name: data.name,
|
||||
tags: data.tags,
|
||||
originX: data.originX,
|
||||
originY: data.originY,
|
||||
isAnimated: data.isAnimated,
|
||||
frameSpeed: data.frameSpeed,
|
||||
frameWidth: data.frameWidth,
|
||||
frameHeight: data.frameHeight
|
||||
}
|
||||
})
|
||||
callback(true)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
callback(false)
|
||||
}
|
||||
})
|
||||
}
|
71
src/socketEvents/gameMaster/assetManager/object/upload.ts
Normal file
71
src/socketEvents/gameMaster/assetManager/object/upload.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
import path from 'path'
|
||||
import fs from 'fs/promises'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import sharp from 'sharp'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||
|
||||
interface IObjectData {
|
||||
[key: string]: Buffer
|
||||
}
|
||||
|
||||
export default class ObjectUploadEvent {
|
||||
constructor(
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {}
|
||||
|
||||
public listen(): void {
|
||||
this.socket.on('gm:object:upload', this.handleObjectUpload.bind(this))
|
||||
}
|
||||
|
||||
private async handleObjectUpload(data: IObjectData, callback: (response: boolean) => void): Promise<void> {
|
||||
try {
|
||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
||||
if (!character) return callback(false)
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return callback(false)
|
||||
}
|
||||
const public_folder = path.join(process.cwd(), 'public', 'objects')
|
||||
|
||||
// Ensure the folder exists
|
||||
await fs.mkdir(public_folder, { recursive: true })
|
||||
|
||||
const uploadPromises = Object.entries(data).map(async ([key, objectData]) => {
|
||||
// Get image dimensions
|
||||
const metadata = await sharp(objectData).metadata()
|
||||
const width = metadata.width || 0
|
||||
const height = metadata.height || 0
|
||||
|
||||
const object = await prisma.object.create({
|
||||
data: {
|
||||
name: key,
|
||||
tags: [],
|
||||
originX: 0,
|
||||
originY: 0,
|
||||
frameWidth: width,
|
||||
frameHeight: height
|
||||
}
|
||||
})
|
||||
|
||||
const uuid = object.id
|
||||
const filename = `${uuid}.png`
|
||||
const finalFilePath = path.join(public_folder, filename)
|
||||
await writeFile(finalFilePath, objectData)
|
||||
|
||||
gameMasterLogger.info('gm:object:upload', `Object ${key} uploaded with id ${uuid}`)
|
||||
})
|
||||
|
||||
await Promise.all(uploadPromises)
|
||||
|
||||
callback(true)
|
||||
} catch (error: any) {
|
||||
gameMasterLogger.error('gm:object:upload error', error.message)
|
||||
callback(false)
|
||||
}
|
||||
}
|
||||
}
|
46
src/socketEvents/gameMaster/assetManager/sprite/create.ts
Normal file
46
src/socketEvents/gameMaster/assetManager/sprite/create.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import path from 'path'
|
||||
import fs from 'fs/promises'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
|
||||
/**
|
||||
* Handle game master new sprite event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:sprite:create', async (data: undefined, callback: (response: boolean) => void) => {
|
||||
try {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return callback(false)
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
const public_folder = path.join(process.cwd(), 'public', 'sprites')
|
||||
|
||||
// Ensure the folder exists
|
||||
await fs.mkdir(public_folder, { recursive: true })
|
||||
|
||||
const sprite = await prisma.sprite.create({
|
||||
data: {
|
||||
name: 'New sprite'
|
||||
}
|
||||
})
|
||||
const uuid = sprite.id
|
||||
|
||||
// Create folder with uuid
|
||||
const sprite_folder = path.join(public_folder, uuid)
|
||||
await fs.mkdir(sprite_folder, { recursive: true })
|
||||
|
||||
callback(true)
|
||||
} catch (error) {
|
||||
console.error('Error creating sprite:', error)
|
||||
callback(false)
|
||||
}
|
||||
})
|
||||
}
|
60
src/socketEvents/gameMaster/assetManager/sprite/delete.ts
Normal file
60
src/socketEvents/gameMaster/assetManager/sprite/delete.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||
|
||||
type Payload = {
|
||||
id: string
|
||||
}
|
||||
|
||||
export default class GMSpriteDeleteEvent {
|
||||
private readonly public_folder: string
|
||||
|
||||
constructor(
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {
|
||||
this.public_folder = path.join(process.cwd(), 'public', 'sprites')
|
||||
}
|
||||
|
||||
public listen(): void {
|
||||
this.socket.on('gm:sprite:delete', this.handleSpriteDelete.bind(this))
|
||||
}
|
||||
|
||||
private async handleSpriteDelete(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
||||
const character = CharacterManager.getCharacterFromSocket(this.socket)
|
||||
if (character?.role !== 'gm') {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
try {
|
||||
await this.deleteSpriteFolder(data.id)
|
||||
await this.deleteSpriteFromDatabase(data.id)
|
||||
|
||||
gameMasterLogger.info(`Sprite ${data.id} deleted.`)
|
||||
callback(true)
|
||||
} catch (error: any) {
|
||||
gameMasterLogger.error('gm:sprite:delete error', error.message)
|
||||
callback(false)
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteSpriteFolder(spriteId: string): Promise<void> {
|
||||
const finalFilePath = path.join(this.public_folder, spriteId)
|
||||
|
||||
if (fs.existsSync(finalFilePath)) {
|
||||
await fs.promises.rmdir(finalFilePath, { recursive: true })
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteSpriteFromDatabase(spriteId: string): Promise<void> {
|
||||
await prisma.sprite.delete({
|
||||
where: {
|
||||
id: spriteId
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
28
src/socketEvents/gameMaster/assetManager/sprite/list.ts
Normal file
28
src/socketEvents/gameMaster/assetManager/sprite/list.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import { Sprite } from '@prisma/client'
|
||||
import SpriteRepository from '../../../../repositories/spriteRepository'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
/**
|
||||
* Handle game master list sprite event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:sprite:list', async (data: any, callback: (response: Sprite[]) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return callback([])
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return callback([])
|
||||
}
|
||||
|
||||
// get all sprites
|
||||
const sprites = await SpriteRepository.getAll()
|
||||
callback(sprites)
|
||||
})
|
||||
}
|
146
src/socketEvents/gameMaster/assetManager/sprite/update.ts
Normal file
146
src/socketEvents/gameMaster/assetManager/sprite/update.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import type { Prisma, SpriteAction } from '@prisma/client'
|
||||
import path from 'path'
|
||||
import { writeFile, mkdir } from 'node:fs/promises'
|
||||
import sharp from 'sharp'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
|
||||
type SpriteActionInput = Omit<SpriteAction, 'id' | 'spriteId' | 'frameWidth' | 'frameHeight'> & {
|
||||
sprites: string[]
|
||||
}
|
||||
|
||||
type Payload = {
|
||||
id: string
|
||||
name: string
|
||||
spriteActions: Prisma.JsonValue
|
||||
}
|
||||
|
||||
interface ProcessedSpriteAction extends SpriteActionInput {
|
||||
frameWidth: number
|
||||
frameHeight: number
|
||||
buffersWithDimensions: Array<{
|
||||
buffer: Buffer
|
||||
width: number | undefined
|
||||
height: number | undefined
|
||||
}>
|
||||
}
|
||||
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:sprite:update', async (data: Payload, callback: (success: boolean) => void) => {
|
||||
const character = CharacterManager.getCharacterFromSocket(socket)
|
||||
if (character?.role !== 'gm') {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedSpriteActions = validateSpriteActions(data.spriteActions)
|
||||
const processedActions = await processSprites(parsedSpriteActions)
|
||||
|
||||
await updateDatabase(data.id, data.name, processedActions)
|
||||
await saveSpritesToDisk(data.id, processedActions)
|
||||
|
||||
callback(true)
|
||||
} catch (error) {
|
||||
console.error('Error updating sprite:', error)
|
||||
callback(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function validateSpriteActions(spriteActions: Prisma.JsonValue): SpriteActionInput[] {
|
||||
try {
|
||||
const parsed = JSON.parse(JSON.stringify(spriteActions)) as SpriteActionInput[]
|
||||
if (!Array.isArray(parsed)) {
|
||||
throw new Error('spriteActions is not an array')
|
||||
}
|
||||
return parsed
|
||||
} catch (error) {
|
||||
console.error('Error parsing spriteActions:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function processSprites(spriteActions: SpriteActionInput[]): Promise<ProcessedSpriteAction[]> {
|
||||
return Promise.all(
|
||||
spriteActions.map(async (spriteAction) => {
|
||||
const { action, sprites } = spriteAction
|
||||
|
||||
if (!Array.isArray(sprites) || sprites.length === 0) {
|
||||
throw new Error(`Invalid sprites array for action: ${action}`)
|
||||
}
|
||||
|
||||
const buffersWithDimensions = await Promise.all(
|
||||
sprites.map(async (sprite: string) => {
|
||||
const buffer = Buffer.from(sprite.split(',')[1], 'base64')
|
||||
const { width, height } = await sharp(buffer).metadata()
|
||||
return { buffer, width, height }
|
||||
})
|
||||
)
|
||||
|
||||
const frameWidth = Math.max(...buffersWithDimensions.map((b) => b.width || 0))
|
||||
const frameHeight = Math.max(...buffersWithDimensions.map((b) => b.height || 0))
|
||||
|
||||
return {
|
||||
...spriteAction,
|
||||
frameWidth,
|
||||
frameHeight,
|
||||
buffersWithDimensions
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async function updateDatabase(id: string, name: string, processedActions: ProcessedSpriteAction[]) {
|
||||
await prisma.sprite.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name,
|
||||
spriteActions: {
|
||||
deleteMany: { spriteId: id },
|
||||
create: processedActions.map(({ action, sprites, originX, originY, isAnimated, isLooping, frameWidth, frameHeight, frameSpeed }) => ({
|
||||
action,
|
||||
sprites,
|
||||
originX,
|
||||
originY,
|
||||
isAnimated,
|
||||
isLooping,
|
||||
frameWidth,
|
||||
frameHeight,
|
||||
frameSpeed
|
||||
}))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function saveSpritesToDisk(id: string, processedActions: ProcessedSpriteAction[]) {
|
||||
const publicFolder = path.join(process.cwd(), 'public', 'sprites', id)
|
||||
await mkdir(publicFolder, { recursive: true })
|
||||
|
||||
await Promise.all(
|
||||
processedActions.map(async ({ action, buffersWithDimensions, frameWidth, frameHeight }) => {
|
||||
const combinedImage = await sharp({
|
||||
create: {
|
||||
width: frameWidth * buffersWithDimensions.length,
|
||||
height: frameHeight,
|
||||
channels: 4,
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||
}
|
||||
})
|
||||
.composite(
|
||||
buffersWithDimensions.map(({ buffer }, index) => ({
|
||||
input: buffer,
|
||||
left: index * frameWidth,
|
||||
top: 0
|
||||
}))
|
||||
)
|
||||
.png()
|
||||
.toBuffer()
|
||||
|
||||
const filename = path.join(publicFolder, `${action}.png`)
|
||||
await writeFile(filename, combinedImage)
|
||||
})
|
||||
)
|
||||
}
|
67
src/socketEvents/gameMaster/assetManager/tile/delete.ts
Normal file
67
src/socketEvents/gameMaster/assetManager/tile/delete.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs/promises'
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||
|
||||
type Payload = {
|
||||
id: string
|
||||
}
|
||||
|
||||
export default class GMTileDeleteEvent {
|
||||
private readonly public_folder: string
|
||||
|
||||
constructor(
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {
|
||||
this.public_folder = path.join(process.cwd(), 'public', 'tiles')
|
||||
}
|
||||
|
||||
public listen(): void {
|
||||
this.socket.on('gm:tile:delete', this.handleTileDelete.bind(this))
|
||||
}
|
||||
|
||||
private async handleTileDelete(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
||||
if (!character) return callback(false)
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
gameMasterLogger.info(`Deleting tile ${data.id}`)
|
||||
await this.deleteTileFromDatabase(data.id)
|
||||
await this.deleteTileFile(data.id)
|
||||
|
||||
gameMasterLogger.info(`Tile ${data.id} deleted successfully.`)
|
||||
callback(true)
|
||||
} catch (error: any) {
|
||||
gameMasterLogger.error('gm:tile:delete error', error.message)
|
||||
callback(false)
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteTileFromDatabase(tileId: string): Promise<void> {
|
||||
await prisma.tile.delete({
|
||||
where: {
|
||||
id: tileId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private async deleteTileFile(tileId: string): Promise<void> {
|
||||
const finalFilePath = path.join(this.public_folder, `${tileId}.png`)
|
||||
try {
|
||||
await fs.unlink(finalFilePath)
|
||||
} catch (error: any) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error
|
||||
}
|
||||
gameMasterLogger.warn(`File ${finalFilePath} does not exist.`)
|
||||
}
|
||||
}
|
||||
}
|
28
src/socketEvents/gameMaster/assetManager/tile/list.ts
Normal file
28
src/socketEvents/gameMaster/assetManager/tile/list.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import { Tile } from '@prisma/client'
|
||||
import TileRepository from '../../../../repositories/tileRepository'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
/**
|
||||
* Handle game master list tile event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:tile:list', async (data: any, callback: (response: Tile[]) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return
|
||||
}
|
||||
|
||||
// get all tiles
|
||||
const tiles = await TileRepository.getAll()
|
||||
callback(tiles)
|
||||
})
|
||||
}
|
44
src/socketEvents/gameMaster/assetManager/tile/update.ts
Normal file
44
src/socketEvents/gameMaster/assetManager/tile/update.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
|
||||
type Payload = {
|
||||
id: string
|
||||
name: string
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle game master tile update event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:tile:update', async (data: Payload, callback: (success: boolean) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return callback(false)
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const Tile = await prisma.tile.update({
|
||||
where: {
|
||||
id: data.id
|
||||
},
|
||||
data: {
|
||||
name: data.name,
|
||||
tags: data.tags
|
||||
}
|
||||
})
|
||||
|
||||
callback(true)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
callback(false)
|
||||
}
|
||||
})
|
||||
}
|
54
src/socketEvents/gameMaster/assetManager/tile/upload.ts
Normal file
54
src/socketEvents/gameMaster/assetManager/tile/upload.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
import path from 'path'
|
||||
import fs from 'fs/promises'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||
|
||||
interface ITileData {
|
||||
[key: string]: Buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle game master upload tile event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:tile:upload', async (data: ITileData, callback: (response: boolean) => void) => {
|
||||
try {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return callback(false)
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
return
|
||||
}
|
||||
|
||||
const public_folder = path.join(process.cwd(), 'public', 'tiles')
|
||||
|
||||
// Ensure the folder exists
|
||||
await fs.mkdir(public_folder, { recursive: true })
|
||||
|
||||
const uploadPromises = Object.entries(data).map(async ([key, tileData]) => {
|
||||
const tile = await prisma.tile.create({
|
||||
data: {
|
||||
name: 'New tile'
|
||||
}
|
||||
})
|
||||
const uuid = tile.id
|
||||
const filename = `${uuid}.png`
|
||||
const finalFilePath = path.join(public_folder, filename)
|
||||
await writeFile(finalFilePath, tileData)
|
||||
})
|
||||
|
||||
await Promise.all(uploadPromises)
|
||||
|
||||
callback(true)
|
||||
} catch (error) {
|
||||
gameMasterLogger.error('Error uploading tile:', error)
|
||||
callback(false)
|
||||
}
|
||||
})
|
||||
}
|
52
src/socketEvents/gameMaster/zoneEditor/create.ts
Normal file
52
src/socketEvents/gameMaster/zoneEditor/create.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../utilities/types'
|
||||
import ZoneRepository from '../../../repositories/zoneRepository'
|
||||
import { Zone } from '@prisma/client'
|
||||
import prisma from '../../../utilities/prisma'
|
||||
import characterRepository from '../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../utilities/logger'
|
||||
|
||||
type Payload = {
|
||||
name: string
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle game master zone create event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:zone_editor:zone:create', async (data: Payload, callback: (response: Zone[]) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
gameMasterLogger.info(`User ${character.id} tried to create zone but is not a game master.`)
|
||||
return
|
||||
}
|
||||
|
||||
gameMasterLogger.info(`User ${character.id} has created a new zone via zone editor.`)
|
||||
|
||||
let zoneList: Zone[] = []
|
||||
try {
|
||||
const zone = await prisma.zone.create({
|
||||
data: {
|
||||
name: data.name,
|
||||
width: data.width,
|
||||
height: data.height,
|
||||
tiles: Array.from({ length: data.height }, () => Array.from({ length: data.width }, () => 'blank_tile'))
|
||||
}
|
||||
})
|
||||
|
||||
zoneList = await ZoneRepository.getAll()
|
||||
callback(zoneList)
|
||||
// send over zone and characters to socket
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
socket.emit('notification', { message: 'Failed to create zoneEditor.' })
|
||||
callback(zoneList)
|
||||
}
|
||||
})
|
||||
}
|
49
src/socketEvents/gameMaster/zoneEditor/delete.ts
Normal file
49
src/socketEvents/gameMaster/zoneEditor/delete.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../utilities/types'
|
||||
import ZoneRepository from '../../../repositories/zoneRepository'
|
||||
import prisma from '../../../utilities/prisma'
|
||||
import characterRepository from '../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../utilities/logger'
|
||||
|
||||
type Payload = {
|
||||
zoneId: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle game master zone delete event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:zone_editor:zone:delete', async (data: Payload, callback: (response: boolean) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
gameMasterLogger.info(`User ${character.id} tried to delete zone but is not a game master.`)
|
||||
return
|
||||
}
|
||||
|
||||
gameMasterLogger.info(`User ${character.id} has deleted a zone via zone editor.`)
|
||||
|
||||
try {
|
||||
const zone = await ZoneRepository.getById(data.zoneId)
|
||||
|
||||
if (!zone) {
|
||||
console.log(`---Zone not found.`)
|
||||
return
|
||||
}
|
||||
|
||||
await prisma.zone.delete({
|
||||
where: {
|
||||
id: data.zoneId
|
||||
}
|
||||
})
|
||||
|
||||
callback(true)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
callback(false)
|
||||
}
|
||||
})
|
||||
}
|
35
src/socketEvents/gameMaster/zoneEditor/list.ts
Normal file
35
src/socketEvents/gameMaster/zoneEditor/list.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../utilities/types'
|
||||
import { Zone } from '@prisma/client'
|
||||
import ZoneRepository from '../../../repositories/zoneRepository'
|
||||
import characterRepository from '../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../utilities/logger'
|
||||
|
||||
interface IPayload {}
|
||||
|
||||
/**
|
||||
* Handle game master list zones event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:zone_editor:zone:list', async (data: IPayload, callback: (response: Zone[]) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
gameMasterLogger.info(`User ${character.id} tried to list zones but is not a game master.`)
|
||||
return
|
||||
}
|
||||
|
||||
gameMasterLogger.info(`User ${character.id} has requested zone list via zone editor.`)
|
||||
|
||||
try {
|
||||
const zones = await ZoneRepository.getAll()
|
||||
callback(zones)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
callback([])
|
||||
}
|
||||
})
|
||||
}
|
47
src/socketEvents/gameMaster/zoneEditor/request.ts
Normal file
47
src/socketEvents/gameMaster/zoneEditor/request.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../utilities/types'
|
||||
import ZoneRepository from '../../../repositories/zoneRepository'
|
||||
import { Zone } from '@prisma/client'
|
||||
import characterRepository from '../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../utilities/logger'
|
||||
|
||||
interface IPayload {
|
||||
zoneId: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle game master zone request event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:zone_editor:zone:request', async (data: IPayload, callback: (response: Zone) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
gameMasterLogger.info(`User ${character!.id} tried to request zone but is not a game master.`)
|
||||
return
|
||||
}
|
||||
|
||||
gameMasterLogger.info(`User ${character.id} has requested zone via zone editor.`)
|
||||
|
||||
if (!data.zoneId) {
|
||||
gameMasterLogger.info(`User ${character.id} tried to request zone but did not provide a zone id.`)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const zone = await ZoneRepository.getById(data.zoneId)
|
||||
|
||||
if (!zone) {
|
||||
gameMasterLogger.info(`User ${character.id} tried to request zone ${data.zoneId} but it does not exist.`)
|
||||
return
|
||||
}
|
||||
|
||||
callback(zone)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
}
|
122
src/socketEvents/gameMaster/zoneEditor/update.ts
Normal file
122
src/socketEvents/gameMaster/zoneEditor/update.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../utilities/types'
|
||||
import ZoneRepository from '../../../repositories/zoneRepository'
|
||||
import { Zone, ZoneEventTileType, ZoneObject } from '@prisma/client'
|
||||
import prisma from '../../../utilities/prisma'
|
||||
import zoneManager from '../../../managers/zoneManager'
|
||||
import characterRepository from '../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../utilities/logger'
|
||||
|
||||
interface IPayload {
|
||||
zoneId: number
|
||||
name: string
|
||||
width: number
|
||||
height: number
|
||||
tiles: string[][]
|
||||
pvp: boolean
|
||||
zoneEventTiles: {
|
||||
type: ZoneEventTileType
|
||||
positionX: number
|
||||
positionY: number
|
||||
teleport?: {
|
||||
toZoneId: number
|
||||
toPositionX: number
|
||||
toPositionY: number
|
||||
}
|
||||
}[]
|
||||
zoneObjects: ZoneObject[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle game master zone update event
|
||||
* @param socket
|
||||
* @param io
|
||||
*/
|
||||
export default function (io: Server, socket: TSocket) {
|
||||
socket.on('gm:zone_editor:zone:update', async (data: IPayload, callback: (response: Zone) => void) => {
|
||||
const character = await characterRepository.getById(socket.characterId as number)
|
||||
if (!character) return
|
||||
|
||||
if (character.role !== 'gm') {
|
||||
gameMasterLogger.info(`User ${character.id} tried to update zone but is not a game master.`)
|
||||
return
|
||||
}
|
||||
|
||||
gameMasterLogger.info(`User ${character.id} has updated zone via zone editor.`)
|
||||
|
||||
if (!data.zoneId) {
|
||||
gameMasterLogger.info(`User ${character.id} tried to update zone but did not provide a zone id.`)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
let zone = await ZoneRepository.getById(data.zoneId)
|
||||
|
||||
if (!zone) {
|
||||
gameMasterLogger.info(`User ${character.id} tried to update zone ${data.zoneId} but it does not exist.`)
|
||||
return
|
||||
}
|
||||
|
||||
await prisma.zone.update({
|
||||
where: {
|
||||
id: data.zoneId
|
||||
},
|
||||
data: {
|
||||
name: data.name,
|
||||
width: data.width,
|
||||
height: data.height,
|
||||
tiles: data.tiles,
|
||||
pvp: data.pvp,
|
||||
zoneEventTiles: {
|
||||
deleteMany: {
|
||||
zoneId: data.zoneId // Ensure only event tiles related to the zone are deleted
|
||||
},
|
||||
// Save new zone event tiles
|
||||
create: data.zoneEventTiles.map((zoneEventTile) => ({
|
||||
type: zoneEventTile.type,
|
||||
positionX: zoneEventTile.positionX,
|
||||
positionY: zoneEventTile.positionY,
|
||||
...(zoneEventTile.type === 'TELEPORT' && zoneEventTile.teleport
|
||||
? {
|
||||
teleport: {
|
||||
create: {
|
||||
toZoneId: zoneEventTile.teleport.toZoneId,
|
||||
toPositionX: zoneEventTile.teleport.toPositionX,
|
||||
toPositionY: zoneEventTile.teleport.toPositionY
|
||||
}
|
||||
}
|
||||
}
|
||||
: {})
|
||||
}))
|
||||
},
|
||||
zoneObjects: {
|
||||
deleteMany: {
|
||||
zoneId: data.zoneId // Ensure only objects related to the zone are deleted
|
||||
},
|
||||
// Save new zone objects
|
||||
create: data.zoneObjects.map((zoneObject) => ({
|
||||
objectId: zoneObject.objectId,
|
||||
depth: zoneObject.depth,
|
||||
positionX: zoneObject.positionX,
|
||||
positionY: zoneObject.positionY
|
||||
}))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
zone = await ZoneRepository.getById(data.zoneId)
|
||||
|
||||
if (!zone) {
|
||||
gameMasterLogger.info(`User ${character.id} tried to update zone ${data.zoneId} but it does not exist.`)
|
||||
return
|
||||
}
|
||||
|
||||
callback(zone)
|
||||
|
||||
zoneManager.unloadZone(data.zoneId)
|
||||
await zoneManager.loadZone(zone)
|
||||
} catch (error: any) {
|
||||
gameMasterLogger.error(`Error updating zone: ${error.message}`)
|
||||
}
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user