Bestenliste und Scores gespeichert und beim Ende eines Spiel Rang anzeige

This commit is contained in:
2025-04-28 20:56:06 +02:00
parent 76392172b5
commit 80ae256638
12 changed files with 155 additions and 32 deletions

View File

@@ -1,5 +1,6 @@
const mySql = require("mysql2"); const mySql = require("mysql2");
const UserManager = require("./UserManager/UserManager"); const UserManager = require("./UserManager/UserManager");
const ScoreManager = require("./ScoreManager/ScoreManager");
require("dotenv").config(); require("dotenv").config();
class DataBaseManager { class DataBaseManager {
@@ -20,6 +21,7 @@ class DataBaseManager {
}); });
this.usermanager = new UserManager(this.connection); this.usermanager = new UserManager(this.connection);
this.scoremanager = new ScoreManager(this.connection);
} }
} }

View File

@@ -0,0 +1,12 @@
class Score{
/** @param {UserManager} usermanager */
constructor(scoreJson){
this.id = scoreJson.id;
this.user1ID = scoreJson.user1;
this.user2ID = scoreJson.user2;
this.score = scoreJson.score;
this.time = new Date(scoreJson.time);
}
}
module.exports = Score;

View File

@@ -0,0 +1,44 @@
const mySql = require("mysql2");
const Score = require("./Score");
class ScoreManager{
/**@param {mySql.Connection} connection*/
constructor(connection) {
this.connection = connection;
}
async getAllScores(){
const response = await this.connection.promise().query("SELECT * FROM scores ORDER BY score DESC");
const sortedScores = response[0].map(scoreData => new Score(scoreData));
return sortedScores;
}
async createScore(newScoreJson){
const sql = `INSERT INTO scores (user1, user2, score) VALUES (
${newScoreJson.user1}, ${newScoreJson.user2}, ${newScoreJson.score})`;
const insertResult = await this.connection.promise().query(sql);
const insertId = insertResult[0].insertId;
const selectSql = `SELECT * FROM scores WHERE id = ${insertId}`;
const selectResult = await this.connection.promise().query(selectSql);
return new Score(selectResult[0][0]);
}
async getScoreById(id){
const response = await this.connection.promise().query(`SELECT * FROM scores WHERE id = ${id}`);
return new Score(response.rows[0][0]);
}
async getScoreRang(id){
const allSortetScores = await this.getAllScores();
const score = allSortetScores.find(score => score.id === id);
return allSortetScores.indexOf(score) + 1;
}
}
module.exports = ScoreManager;

View File

@@ -17,7 +17,7 @@ class ClientHandler {
this.socket this.socket
); );
this.socket.on("disconnect", () => { this.defaultDisconnect() }); this.socket.on("disconnect", async () => { await this.defaultDisconnect() });
this.joinGame(); this.joinGame();
} }
@@ -31,8 +31,8 @@ class ClientHandler {
this.socket.join(`game-${this.currentGameCode}`); this.socket.join(`game-${this.currentGameCode}`);
} }
defaultDisconnect(){ async defaultDisconnect(){
this.gameManager.leaveGame(this.user, this.currentGameCode); await this.gameManager.leaveGame(this.user, this.currentGameCode);
} }
} }

View File

@@ -28,7 +28,7 @@ class Snake{
this.setup(startTiles); this.setup(startTiles);
} }
setup(startTiles){ async setup(startTiles){
this.player.socket.emit("color", this.color); this.player.socket.emit("color", this.color);
const headX = startTiles.x; const headX = startTiles.x;
@@ -66,7 +66,7 @@ class Snake{
}); });
} }
this.checkAndDrawTiles(); await this.checkAndDrawTiles();
} }
movementToAxes(movement){ movementToAxes(movement){
@@ -82,7 +82,7 @@ class Snake{
return {num, axes}; return {num, axes};
} }
move(){ async move(){
// Aktuelles Movement Klonen nicht das es zwischendurch geändert wird // Aktuelles Movement Klonen nicht das es zwischendurch geändert wird
if( if(
this.movementToAxes(this.nextMovement).axes this.movementToAxes(this.nextMovement).axes
@@ -156,7 +156,7 @@ class Snake{
else if (dy === 1) end.deg = 270; else if (dy === 1) end.deg = 270;
else if (dy === -1) end.deg = 90; else if (dy === -1) end.deg = 90;
this.checkAndDrawTiles(); await this.checkAndDrawTiles();
} }
getBigger(){ getBigger(){
@@ -181,22 +181,24 @@ class Snake{
this.tiles.push(newEnd); this.tiles.push(newEnd);
} }
checkAndDrawTiles(){ async checkAndDrawTiles(){
this.tiles.forEach(tile => { for (const tile of this.tiles) {
const exsitingtile = this.playground.getTile(tile.x, tile.y); const exsitingtile = this.playground.getTile(tile.x, tile.y);
// TODO: Klammert man das ein Kann man alleine Spielen, da es keine Kolisionserkennung gibt // TODO: Klammert man das ein Kann man alleine Spielen, da es keine Kolisionserkennung gibt
if(exsitingtile === undefined){ if(exsitingtile === undefined){
// End Game weil außerhalb des Spielfeldes // End Game weil außerhalb des Spielfeldes
this.game.endGame(`${this.player.username} hat die Wand berührt!`) await this.game.endGame(`${this.player.username} hat die Wand berührt!`);
return;
} }
if(exsitingtile?.class === "Snake"){ if(exsitingtile?.class === "Snake"){
// Eng Game weil schon belegt mit anderer oder eigender Schlange // Eng Game weil schon belegt mit anderer oder eigender Schlange
this.game.endGame(`Es gab eine Kollision!`) await this.game.endGame(`Es gab eine Kollision!`);
return;
} }
this.playground.setTile(tile.x, tile.y, tile); this.playground.setTile(tile.x, tile.y, tile);
}) }
} }
updateNextMovement(data){ updateNextMovement(data){

View File

@@ -21,6 +21,9 @@ class Game{
/**@type {Array<SocketUser>} */ /**@type {Array<SocketUser>} */
this.players = []; this.players = [];
/**@type {Array<SocketUser>} */
this.playerIds = [];
this.gameLoop = new GameLoop(io, this); this.gameLoop = new GameLoop(io, this);
setTimeout(() => { this.waitingForPlayers(true) }, 100); setTimeout(() => { this.waitingForPlayers(true) }, 100);
@@ -35,6 +38,9 @@ class Game{
// 2 Schlangen für die Spieler Instazieren // 2 Schlangen für die Spieler Instazieren
this.players.forEach((player, i) => { this.players.forEach((player, i) => {
// id pushen um nachher das Endgame zu beenden
this.playerIds.push(player.id);
const start = (10 * i + 5) - 1; const start = (10 * i + 5) - 1;
const snake = new Snake( const snake = new Snake(
player, player,
@@ -54,20 +60,29 @@ class Game{
this.gameLoop.loop(); this.gameLoop.loop();
} }
endGame(msg){ async endGame(msg){
// TODO: Spielende in die Datenbank eintragen // TODO: Spielende in die Datenbank eintragen
this.gameStarted = false;
const scoreDb = await this.gameManager.db.scoremanager.createScore({
user1: this.playerIds[0],
user2: this.playerIds[1],
score: this.score
});
const rang = await this.gameManager.db.scoremanager.getScoreRang(scoreDb.id);
this.io.to(`game-${this.code}`).emit("gameEnd", { this.io.to(`game-${this.code}`).emit("gameEnd", {
msg: msg, msg: msg,
score: this.score, score: this.score,
rang: rang
}); });
} }
waitingForPlayers(changeTime = false){ async waitingForPlayers(changeTime = false){
if(this.waitingSeconds <= 0){ if(this.waitingSeconds <= 0){
if(this.players.length < 2) { if(this.players.length < 2) {
this.endGame("Das Spiel ist zu Ende, da nicht genug Spieler beigetreten sind!"); await this.endGame("Das Spiel ist zu Ende, da nicht genug Spieler beigetreten sind!");
this.gameManager.games.delete(this.code); this.gameManager.games.delete(this.code);
return; return;
} }
@@ -108,16 +123,15 @@ class Game{
} }
/** @param {SocketUser} user */ /** @param {SocketUser} user */
leaveUser(user){ async leaveUser(user){
this.players.forEach((player, index) => { this.players.forEach((player, index) => {
if(player.id === user.id){ if(player.id === user.id){
this.players.splice(index, 1); this.players.splice(index, 1);
return;
} }
}); });
if(this.players.length != 2){ if(this.players.length != 2){
this.endGame("Das Spiel ist zu Ende, da ein Spieler verlassen hat!"); if(this.gameStarted) await this.endGame("Das Spiel ist zu Ende, da ein Spieler verlassen hat!");
return 1; return 1;
} }
} }

View File

@@ -18,17 +18,19 @@ class GameLoop{
this.fruitManager = new FruitManager(this.io, this.game, this.playground); this.fruitManager = new FruitManager(this.io, this.game, this.playground);
} }
loop(){ async loop(){
this.playground.resetPlayground(); this.playground.resetPlayground();
this.snakes.forEach(snake => { snake.move()}); await Promise.all(this.snakes.map(snake => snake.move()));
this.fruitManager.updateFruits(); this.fruitManager.updateFruits();
this.sendUpdate(); this.sendUpdate();
setTimeout(() => { if(this.game.gameStarted){
this.loop(); setTimeout(() => {
}, 125); this.loop();
}, 125);
}
} }
sendUpdate(){ sendUpdate(){

View File

@@ -2,12 +2,14 @@ const socketIO = require("socket.io");
const SocketUser = require("../Classes/SocketUser"); const SocketUser = require("../Classes/SocketUser");
const TemporaryLobby = require("../LobbyManager/Classes/TemporaryLobby"); const TemporaryLobby = require("../LobbyManager/Classes/TemporaryLobby");
const LobbyManager = require("../LobbyManager/LobbyManager"); const LobbyManager = require("../LobbyManager/LobbyManager");
const DataBaseManager = require("../../Database/DataBaseManager");
const Game = require("./Game/Game"); const Game = require("./Game/Game");
class GameManager { class GameManager {
/** @param {socketIO.Server} io @param {LobbyManager} lobbyManager */ /** @param {socketIO.Server} io @param {DataBaseManager} db @param {LobbyManager} lobbyManager */
constructor(io, lobbyManager) { constructor(io, db, lobbyManager) {
this.io = io; this.io = io;
this.db = db;
this.lobbyManager = lobbyManager; this.lobbyManager = lobbyManager;
/** @type {Map<string, Game>}*/ /** @type {Map<string, Game>}*/
@@ -52,11 +54,11 @@ class GameManager {
return oldLobbySave.gameCode; return oldLobbySave.gameCode;
} }
leaveGame(user, code){ async leaveGame(user, code){
const game = this.games.get(code); const game = this.games.get(code);
if(!game) return 1; if(!game) return 1;
const response = game.leaveUser(user); const response = await game.leaveUser(user);
if(response === 1) this.games.delete(code); if(response === 1) this.games.delete(code);
} }

View File

@@ -31,7 +31,7 @@ class SocketIOManager {
Der LobbyManager kümmert sich um alle Lobbys Der LobbyManager kümmert sich um alle Lobbys
*/ */
this.lobbyManager = new LobbyManager(this.io); this.lobbyManager = new LobbyManager(this.io);
this.gameManager = new GameManager(this.io, this.lobbyManager); this.gameManager = new GameManager(this.io, this.db, this.lobbyManager);
this.io.on("connection", (socket) => { this.sendToRightManager(socket) }); this.io.on("connection", (socket) => { this.sendToRightManager(socket) });

View File

@@ -47,6 +47,26 @@ INSERT INTO `users` (`id`, `username`, `email`, `password`, `fullName`, `created
(2, 'test2', 'test2@gmail.com', '$2b$10$fRrqNkFVA4TIyy61ovgPae83drW6oZOCND94BErwthKmt1MxzgTci', 'Test Person 2', '2025-03-22 19:48:45'), (2, 'test2', 'test2@gmail.com', '$2b$10$fRrqNkFVA4TIyy61ovgPae83drW6oZOCND94BErwthKmt1MxzgTci', 'Test Person 2', '2025-03-22 19:48:45'),
(3, 'test3', NULL, '$2b$10$5QEkQuI/HxnaNovp8XPbnOnkuPpfSLdjwyqXMXKCB5oHZQR3ILTkq', NULL, '2025-03-22 19:48:51'); (3, 'test3', NULL, '$2b$10$5QEkQuI/HxnaNovp8XPbnOnkuPpfSLdjwyqXMXKCB5oHZQR3ILTkq', NULL, '2025-03-22 19:48:51');
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `scores`
--
CREATE TABLE `scores` (
`id` int(11) NOT NULL,
`user1` int(11) NOT NULL,
`user2` int(11) NOT NULL,
`score` int(11) NOT NULL,
`time` timestamp NOT NULL DEFAULT current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Daten für Tabelle `scores`
--
INSERT INTO `scores` (`id`, `user1`, `user2`, `score`, `time`) VALUES
(1, 1, 2, 100, '2025-03-22 20:00:00'),
(2, 2, 3, 150, '2025-03-22 20:15:00'),
(3, 1, 3, 200, '2025-03-22 20:30:00');
-- --
-- Indizes der exportierten Tabellen -- Indizes der exportierten Tabellen
-- --
@@ -58,6 +78,14 @@ ALTER TABLE `users`
ADD PRIMARY KEY (`id`), ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `username` (`username`); ADD UNIQUE KEY `username` (`username`);
--
-- Indizes für die Tabelle `scores`
--
ALTER TABLE `scores`
ADD PRIMARY KEY (`id`),
ADD KEY `user1` (`user1`),
ADD KEY `user2` (`user2`);
-- --
-- AUTO_INCREMENT für exportierte Tabellen -- AUTO_INCREMENT für exportierte Tabellen
-- --
@@ -67,6 +95,19 @@ ALTER TABLE `users`
-- --
ALTER TABLE `users` ALTER TABLE `users`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
--
-- AUTO_INCREMENT für Tabelle `scores`
--
ALTER TABLE `scores`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
--
-- Constraints der Tabelle `scores`
--
ALTER TABLE `scores`
ADD CONSTRAINT `scores_ibfk_1` FOREIGN KEY (`user1`) REFERENCES `users` (`id`),
ADD CONSTRAINT `scores_ibfk_2` FOREIGN KEY (`user2`) REFERENCES `users` (`id`);
COMMIT; COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

View File

@@ -107,6 +107,10 @@ class UIManager{
const score = document.createElement("h2"); const score = document.createElement("h2");
score.innerText = data.score; score.innerText = data.score;
scoreDiv.appendChild(score); scoreDiv.appendChild(score);
const rang = document.createElement("h2");
rang.innerText = `Bestenliste Platz: ${data.rang}`;
rang.style.color = "rgb(255, 223, 0)";
scoreDiv.appendChild(rang);
endDiv.appendChild(scoreDiv); endDiv.appendChild(scoreDiv);
const dashButton = document.createElement("button"); const dashButton = document.createElement("button");

View File

@@ -86,7 +86,7 @@ img{
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: space-evenly; justify-content: space-evenly;
height: 150px; height: 200px;
} }
#scoreEndDiv h3{ #scoreEndDiv h3{