diff --git a/package-lock.json b/package-lock.json index 0ab9320..38ade7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1269,9 +1269,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz", - "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, "bin": { "semver": "bin/semver.js" diff --git a/package.json b/package.json index 1d8be14..4ced17d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "scripts": { "start": "node dist/index.js", - "dev": "nodemon --exec ts-node src/application.ts", + "dev": "nodemon --exec ts-node src/server.ts", "build": "tsc" }, "dependencies": { diff --git a/prisma/migrations/20240509150033_init/migration.sql b/prisma/migrations/20240509150033_init/migration.sql new file mode 100644 index 0000000..7afb735 --- /dev/null +++ b/prisma/migrations/20240509150033_init/migration.sql @@ -0,0 +1,46 @@ +/* + Warnings: + + - You are about to drop the column `mapId` on the `Chatlogs` table. All the data in the column will be lost. + - You are about to drop the column `mapId` on the `User` table. All the data in the column will be lost. + - You are about to drop the `Map` table. If the table is not empty, all the data it contains will be lost. + - A unique constraint covering the columns `[zoneId]` on the table `User` will be added. If there are existing duplicate values, this will fail. + - Added the required column `zoneId` to the `Chatlogs` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE `Chatlogs` DROP FOREIGN KEY `Chatlogs_mapId_fkey`; + +-- DropForeignKey +ALTER TABLE `User` DROP FOREIGN KEY `User_mapId_fkey`; + +-- AlterTable +ALTER TABLE `Chatlogs` DROP COLUMN `mapId`, + ADD COLUMN `zoneId` INTEGER NOT NULL; + +-- AlterTable +ALTER TABLE `User` DROP COLUMN `mapId`, + ADD COLUMN `zoneId` INTEGER NULL; + +-- DropTable +DROP TABLE `Map`; + +-- CreateTable +CREATE TABLE `Zone` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `name` VARCHAR(191) NOT NULL, + `width` INTEGER NOT NULL, + `height` INTEGER NOT NULL, + `tiles` JSON NOT NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateIndex +CREATE UNIQUE INDEX `User_zoneId_key` ON `User`(`zoneId`); + +-- AddForeignKey +ALTER TABLE `User` ADD CONSTRAINT `User_zoneId_fkey` FOREIGN KEY (`zoneId`) REFERENCES `Zone`(`id`) ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Chatlogs` ADD CONSTRAINT `Chatlogs_zoneId_fkey` FOREIGN KEY (`zoneId`) REFERENCES `Zone`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 098bc47..90d6f46 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -26,12 +26,12 @@ model User { position_x Int? position_y Int? rotation Int? - mapId Int? @unique - map Map? @relation(fields: [mapId], references: [id]) + zoneId Int? @unique + zone Zone? @relation(fields: [zoneId], references: [id]) chatlogs Chatlogs[] } -model Map { +model Zone { id Int @id @default(autoincrement()) name String width Int @@ -45,7 +45,7 @@ model Chatlogs { id Int @id @default(autoincrement()) userId Int message String - mapId Int - map Map @relation(fields: [mapId], references: [id]) + zoneId Int + zone Zone @relation(fields: [zoneId], references: [id]) user User @relation(fields: [userId], references: [id]) } \ No newline at end of file diff --git a/src/app/ZoneManager.ts b/src/app/ZoneManager.ts new file mode 100644 index 0000000..fc28793 --- /dev/null +++ b/src/app/ZoneManager.ts @@ -0,0 +1,53 @@ +import { Zone } from "@prisma/client"; +import { LoadedZoneEntity } from "./entities/LoadedZoneEntity"; +import ZoneRepository from "./repositories/zone"; + +class ZoneManager { + private static _instance: ZoneManager; + private _loadedZones: LoadedZoneEntity[] = []; + + private constructor() {} + + // Singleton instance getter + public static getInstance(): ZoneManager { + if (!ZoneManager._instance) { + ZoneManager._instance = new ZoneManager(); + } + return ZoneManager._instance; + } + + // Method to initialize zone loading + public async boot() { + const zoneRepository = new ZoneRepository(); + const zones = await zoneRepository.getAll(); + + for (const zone of zones) { + this.loadZone(zone); + } + + console.log('[βœ…] ZoneManager loaded'); + } + + // Method to handle individual zone loading + public loadZone(zone: Zone) { + const loadedZone = new LoadedZoneEntity(zone); + this._loadedZones.push(loadedZone); + console.log(`[βœ…] Zone ID ${zone.id} loaded`); + } + + // Method to handle individual zone unloading + public unloadZone(zoneId: number) { + const index = this._loadedZones.findIndex(loadedZone => loadedZone.getZone().id === zoneId); + if (index > -1) { + this._loadedZones.splice(index, 1); + console.log(`[❌] Zone ID ${zoneId} unloaded`); + } + } + + // Getter for loaded zones + public getLoadedZones(): LoadedZoneEntity[] { + return this._loadedZones; + } +} + +export default ZoneManager; diff --git a/src/app/entities/LoadedZoneEntity.ts b/src/app/entities/LoadedZoneEntity.ts new file mode 100644 index 0000000..f598582 --- /dev/null +++ b/src/app/entities/LoadedZoneEntity.ts @@ -0,0 +1,37 @@ +import { PlayerEntity } from "./PlayerEntity"; +import {Zone} from "@prisma/client"; + +export class LoadedZoneEntity +{ + zone: Zone; + players: Map; + + constructor(zone: Zone) { + this.zone = zone; + this.players = new Map(); + } + + getZone() { + return this.zone; + } + + addPlayer(player: PlayerEntity) { + this.players.set(player.id, player); + } + + removePlayer(player: PlayerEntity) { + this.players.delete(player.id); + } + + getPlayer(playerId: string) { + return this.players.get(playerId); + } + + getPlayers() { + return Array.from(this.players.values()); + } + + getPlayersCount() { + return this.players.size; + } +} \ No newline at end of file diff --git a/src/app/entities/PlayerEntity.ts b/src/app/entities/PlayerEntity.ts new file mode 100644 index 0000000..d6d0a1a --- /dev/null +++ b/src/app/entities/PlayerEntity.ts @@ -0,0 +1,17 @@ +export class PlayerEntity +{ + id: string; + name: string; + position: { x: number, y: number }; + + constructor(id: string, name: string, position: { x: number, y: number }) { + this.id = id; + this.name = name; + this.position = position; + } + + move(x: number, y: number) { + this.position.x = x; + this.position.y = y; + } +} \ No newline at end of file diff --git a/src/app/events/player_map_load.ts b/src/app/events/player_zone_load.ts similarity index 69% rename from src/app/events/player_map_load.ts rename to src/app/events/player_zone_load.ts index c657c6f..8ccf224 100644 --- a/src/app/events/player_map_load.ts +++ b/src/app/events/player_zone_load.ts @@ -1,6 +1,6 @@ import { Socket, Server } from "socket.io"; -export default function player_map_load(socket: Socket, io: Server) { +export default function player_zone_load(socket: Socket, io: Server) { socket.on('player:map:load', (data) => { console.log(`---User ${socket.id} has requested map.`); }); diff --git a/src/app/repositories/map.ts b/src/app/repositories/map.ts deleted file mode 100644 index 33f419a..0000000 --- a/src/app/repositories/map.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Map } from '@prisma/client'; -import prisma from '../utilities/prisma'; // Import the global Prisma instance - -class MapRepository { - async getFirst(): Promise { - try { - return await prisma.map.findFirst(); - } catch (error: any) { - // Handle error - throw new Error(`Failed to get first map: ${error.message}`); - } - } -} - -export default MapRepository; \ No newline at end of file diff --git a/src/app/repositories/zone.ts b/src/app/repositories/zone.ts new file mode 100644 index 0000000..508eb74 --- /dev/null +++ b/src/app/repositories/zone.ts @@ -0,0 +1,24 @@ +import { Zone } from '@prisma/client'; +import prisma from '../utilities/prisma'; // Import the global Prisma instance + +class ZoneRepository { + async getFirst(): Promise { + try { + return await prisma.zone.findFirst(); + } catch (error: any) { + // Handle error + throw new Error(`Failed to get first zone: ${error.message}`); + } + } + + async getAll(): Promise { + try { + return await prisma.zone.findMany(); + } catch (error: any) { + // Handle error + throw new Error(`Failed to get all zone: ${error.message}`); + } + } +} + +export default ZoneRepository; \ No newline at end of file diff --git a/src/app/services/zone.ts b/src/app/services/zone.ts new file mode 100644 index 0000000..6007f68 --- /dev/null +++ b/src/app/services/zone.ts @@ -0,0 +1,13 @@ +import {LoadedZoneEntity} from "../entities/LoadedZoneEntity"; +import {Zone} from "@prisma/client"; + +class ZoneService +{ + public loaded: LoadedZoneEntity[] = []; + + public boot() { + // Load all zones + // const zones = await new ZoneRepository().getAll(); + // this.loadedZones = zones; + } +} \ No newline at end of file diff --git a/src/app/utilities/api.ts b/src/app/utilities/api.ts index 16c5304..5b53baf 100644 --- a/src/app/utilities/api.ts +++ b/src/app/utilities/api.ts @@ -26,7 +26,7 @@ async function addAuthRoutes(app: any) { return res.status(400).json({ message: 'Failed to register user' }); }); - console.log('[🌎] Auth routes added'); + console.log('[πŸ•ΈοΈ] Auth routes added'); } export default { addAuthRoutes }; \ No newline at end of file diff --git a/src/application.ts b/src/application.ts deleted file mode 100644 index 0efe091..0000000 --- a/src/application.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Entry point of the application - * Made by Dennis Postma - * for New Quest - */ - -import { Server } from './app/server'; - -new Server().start(); \ No newline at end of file diff --git a/src/app/server.ts b/src/server.ts similarity index 78% rename from src/app/server.ts rename to src/server.ts index b19f393..89b03ea 100644 --- a/src/app/server.ts +++ b/src/server.ts @@ -4,9 +4,10 @@ import express from 'express'; import http from 'http'; import cors from 'cors'; import {Server as HttpServer, Socket} from 'socket.io'; -import config from './utilities/config'; -import prisma from './utilities/prisma'; -import api from "./utilities/api"; +import config from './app/utilities/config'; +import prisma from './app/utilities/prisma'; +import api from "./app/utilities/api"; +import ZoneManager from "./app/ZoneManager"; export class Server { @@ -31,7 +32,7 @@ export class Server */ public async start() { // Print logo - const art = fs.readFileSync(path.join(__dirname, 'logo.txt'), 'utf8'); + const art = fs.readFileSync(path.join(__dirname, 'app', 'logo.txt'), 'utf8'); console.log('\x1b[31m%s\x1b[0m', art + '\n'); // Check prisma connection @@ -53,7 +54,11 @@ export class Server // Add API routes await api.addAuthRoutes(this.app); - // Add socket events + // Load zone manager + const zoneManager = ZoneManager.getInstance(); + await zoneManager.boot(); + + // Listen for socket connections this.io.on('connection', this.handleConnection.bind(this)); } @@ -65,12 +70,8 @@ export class Server private async handleConnection(socket: Socket) { const eventsPath = path.join(__dirname, 'events'); try { - const files = await fs.promises.readdir(eventsPath); + const files: string[] = await fs.promises.readdir(eventsPath); for (const file of files) { - if (!file.endsWith('.ts')) { - continue; - } - const module = await import(path.join(eventsPath, file)); module.default(socket, this.io); } @@ -79,3 +80,7 @@ export class Server } } } + +// Start the server +const server = new Server(); +server.start(); \ No newline at end of file