From a2f21229d8c64842c513f934fb720ee336458333 Mon Sep 17 00:00:00 2001
From: Dennis Postma <dennis@directonline.io>
Date: Thu, 9 May 2024 22:03:21 +0200
Subject: [PATCH] code update

---
 package-lock.json                             |  6 +--
 package.json                                  |  2 +-
 .../20240509150033_init/migration.sql         | 46 ++++++++++++++++
 prisma/schema.prisma                          | 10 ++--
 src/app/ZoneManager.ts                        | 53 +++++++++++++++++++
 src/app/entities/LoadedZoneEntity.ts          | 37 +++++++++++++
 src/app/entities/PlayerEntity.ts              | 17 ++++++
 ...player_map_load.ts => player_zone_load.ts} |  2 +-
 src/app/repositories/map.ts                   | 15 ------
 src/app/repositories/zone.ts                  | 24 +++++++++
 src/app/services/zone.ts                      | 13 +++++
 src/app/utilities/api.ts                      |  2 +-
 src/application.ts                            |  9 ----
 src/{app => }/server.ts                       | 25 +++++----
 14 files changed, 216 insertions(+), 45 deletions(-)
 create mode 100644 prisma/migrations/20240509150033_init/migration.sql
 create mode 100644 src/app/ZoneManager.ts
 create mode 100644 src/app/entities/LoadedZoneEntity.ts
 create mode 100644 src/app/entities/PlayerEntity.ts
 rename src/app/events/{player_map_load.ts => player_zone_load.ts} (69%)
 delete mode 100644 src/app/repositories/map.ts
 create mode 100644 src/app/repositories/zone.ts
 create mode 100644 src/app/services/zone.ts
 delete mode 100644 src/application.ts
 rename src/{app => }/server.ts (78%)

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<string, PlayerEntity>;
+
+    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<Map | null> {
-        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<Zone | null> {
+        try {
+            return await prisma.zone.findFirst();
+        } catch (error: any) {
+            // Handle error
+            throw new Error(`Failed to get first zone: ${error.message}`);
+        }
+    }
+
+    async getAll(): Promise<Zone[]> {
+        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