diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e1d8681..9aa1d97 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -3,7 +3,7 @@ // npx prisma init // 2. Create a new database schema // npx prisma db push -// 3. Generate Prisma Client +// 3. Generate Prisma Client and type-safe models based on schema // npx prisma generate // 4. Create a new migration // npx prisma migrate dev --name init @@ -24,6 +24,7 @@ model Sprite { name String origin_x Decimal @default(0) origin_y Decimal @default(0) + type String frameSpeed Int @default(0) isAnimated Boolean @default(false) isLooping Boolean @default(false) diff --git a/src/app/events/gm/sprite/List.ts b/src/app/events/gm/sprite/List.ts new file mode 100644 index 0000000..7a5f3e2 --- /dev/null +++ b/src/app/events/gm/sprite/List.ts @@ -0,0 +1,26 @@ +import { Server } from "socket.io"; +import {TSocket} from "../../../utilities/Types"; +import { Sprite } from '@prisma/client' +import SpriteRepository from '../../../repositories/SpriteRepository' + +interface IPayload { +} + +/** + * Handle game master list sprite event + * @param socket + * @param io + */ +export default function (socket: TSocket, io: Server) { + socket.on('gm:sprite:list', async (data: any, callback: (response: Sprite[]) => void) => { + + if (socket.character?.role !== 'gm') { + console.log(`---Character #${socket.character?.id} is not a game master.`); + return; + } + + // get all sprites + const sprites = await SpriteRepository.getAll(); + callback(sprites); + }); +} \ No newline at end of file diff --git a/src/app/events/gm/sprite/Remove.ts b/src/app/events/gm/sprite/Remove.ts new file mode 100644 index 0000000..5151306 --- /dev/null +++ b/src/app/events/gm/sprite/Remove.ts @@ -0,0 +1,45 @@ +import { Server } from "socket.io"; +import {TSocket} from "../../../utilities/Types"; +import path from "path"; +import fs from "fs"; +import SpriteRepository from '../../../repositories/SpriteRepository' + +interface IPayload { + sprite: string; +} + +/** + * Handle game master remove sprite event + * @param socket + * @param io + */ +export default function (socket: TSocket, io: Server) { + socket.on('gm:sprite:remove', async (data: IPayload, callback: (response: boolean) => void) => { + + if (socket.character?.role !== 'gm') { + return; + } + + try { + await SpriteRepository.delete(data.sprite); + + // get root path + const public_folder = path.join(process.cwd(), 'public', 'sprites'); + + // remove the tile from the disk + const finalFilePath = path.join(public_folder, data.sprite + '.png'); + fs.unlink(finalFilePath, (err) => { + if (err) { + console.log(err); + callback(false); + return; + } + + callback(true); + }); + } catch (e) { + console.log(e); + callback(false); + } + }); +} \ No newline at end of file diff --git a/src/app/events/gm/sprite/Update.ts b/src/app/events/gm/sprite/Update.ts new file mode 100644 index 0000000..13f9314 --- /dev/null +++ b/src/app/events/gm/sprite/Update.ts @@ -0,0 +1,34 @@ +import { Server } from "socket.io"; +import {TSocket} from "../../../utilities/Types"; +import SpriteRepository from '../../../repositories/SpriteRepository' +import { Sprite } from '@prisma/client' + +interface IPayload { + id: string; + name: string; + origin_x: number; + origin_y: number; +} + +/** + * Handle game master sprite update event + * @param socket + * @param io + */ +export default function (socket: TSocket, io: Server) { + socket.on('gm:sprite:update', async (data: IPayload, callback: (success: boolean) => void) => { + + if (socket.character?.role !== 'gm') { + return; + } + + try { + const sprite = await SpriteRepository.update(data.id, data.name, data.origin_x, data.origin_y); + + callback(true); + } catch (error) { + console.error(error); + callback(false); + } + }); +} \ No newline at end of file diff --git a/src/app/events/gm/sprite/Upload.ts b/src/app/events/gm/sprite/Upload.ts new file mode 100644 index 0000000..8b20d70 --- /dev/null +++ b/src/app/events/gm/sprite/Upload.ts @@ -0,0 +1,46 @@ +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 spriteRepository from '../../../repositories/SpriteRepository' + +interface ISpriteData { + [key: string]: Buffer; +} + +/** + * Handle game master upload sprite event + * @param socket + * @param io + */ +export default function (socket: TSocket, io: Server) { + socket.on('gm:sprite:upload', async (data: ISpriteData, callback: (response: boolean) => void) => { + try { + if (socket.character?.role !== 'gm') { + callback(false); + return; + } + + const public_folder = path.join(process.cwd(), 'public', 'sprites'); + + // Ensure the folder exists + await fs.mkdir(public_folder, { recursive: true }); + + const uploadPromises = Object.entries(data).map(async ([key, spriteData]) => { + const sprite = await spriteRepository.create('New sprite', 0, 0); + const uuid = sprite.id; + const filename = `${uuid}.png`; + const finalFilePath = path.join(public_folder, filename); + await writeFile(finalFilePath, spriteData); + }); + + await Promise.all(uploadPromises); + + callback(true); + } catch (error) { + console.error('Error uploading tile:', error); + callback(false); + } + }); +} \ No newline at end of file diff --git a/src/app/repositories/CharacterRepository.ts b/src/app/repositories/CharacterRepository.ts index 8614d5d..2885439 100644 --- a/src/app/repositories/CharacterRepository.ts +++ b/src/app/repositories/CharacterRepository.ts @@ -1,6 +1,5 @@ import prisma from '../utilities/Prisma'; // Import the global Prisma instance import {Character} from '@prisma/client'; -import CharacterService from "../services/CharacterService"; class CharacterRepository { async getByUserId(userId: number): Promise { diff --git a/src/app/repositories/SpriteRepository.ts b/src/app/repositories/SpriteRepository.ts new file mode 100644 index 0000000..6f89172 --- /dev/null +++ b/src/app/repositories/SpriteRepository.ts @@ -0,0 +1,43 @@ +import prisma from '../utilities/Prisma'; // Import the global Prisma instance +import { Sprite } from '@prisma/client' + +class SpriteRepository { + async getById(id: string): Promise { + return prisma.sprite.findUnique({ + where: { id }, + }); + } + + async getAll(): Promise { + return prisma.sprite.findMany(); + } + + async create(name: string, origin_x: number, origin_y: number): Promise { + return prisma.sprite.create({ + data: { + name, + origin_x, + origin_y + }, + }); + } + + async update(id: string, name: string, origin_x: number, origin_y: number): Promise { + return prisma.sprite.update({ + where: { id }, + data: { + name, + origin_x, + origin_y + }, + }); + } + + async delete(id: string): Promise { + return prisma.sprite.delete({ + where: { id }, + }); + } +} + +export default new SpriteRepository(); \ No newline at end of file diff --git a/src/app/utilities/Http.ts b/src/app/utilities/Http.ts index 48f99f8..f454c33 100644 --- a/src/app/utilities/Http.ts +++ b/src/app/utilities/Http.ts @@ -9,9 +9,9 @@ import config from "./Config"; import {loginAccountSchema, registerAccountSchema} from "./ZodTypes"; import path from "path"; import { TAsset } from './Types' -import fs from 'fs' import tileRepository from '../repositories/TileRepository' import objectRepository from '../repositories/ObjectRepository' +import spriteRepository from '../repositories/SpriteRepository' async function addHttpRoutes(app: Application) { app.get('/assets', async (req: Request, res: Response) => {