From 21e6f31c15c4b04f7297eee91e9ff8a94595480a Mon Sep 17 00:00:00 2001 From: Quentin Roussel Date: Mon, 10 Jun 2024 18:06:00 +0000 Subject: [PATCH] Refactored code to remove useless classes, fix #17 --- traque-back/admin_socket.js | 17 +++--- traque-back/game.js | 74 ++++++++++++------------ traque-back/index.js | 22 +------- traque-back/penalty_controller.js | 63 ++++++++++----------- traque-back/photo.js | 3 +- traque-back/team_socket.js | 10 ++-- traque-back/zone_manager.js | 94 +++++++++++++++++-------------- 7 files changed, 137 insertions(+), 146 deletions(-) diff --git a/traque-back/admin_socket.js b/traque-back/admin_socket.js index 1d4d58d..b57e2c5 100644 --- a/traque-back/admin_socket.js +++ b/traque-back/admin_socket.js @@ -1,4 +1,7 @@ -import { io, game, penaltyController } from "./index.js"; +import { io } from "./index.js"; +import game from "./game.js" +import zone from "./zone_manager.js" +import penaltyController from "./penalty_controller.js"; import { playersBroadcast, sendUpdatedTeamInformations } from "./team_socket.js"; import { config } from "dotenv"; @@ -47,11 +50,11 @@ export function initAdminSocketHandler() { //Other settings that need initialization socket.emit("penalty_settings", penaltyController.settings) socket.emit("game_settings", game.settings) - socket.emit("zone_settings", game.zone.zoneSettings) - socket.emit("zone", game.zone.currentZone) + socket.emit("zone_settings", zone.zoneSettings) + socket.emit("zone", zone.currentZone) socket.emit("new_zone", { - begin: game.zone.currentStartZone, - end: game.zone.nextZone + begin: zone.currentStartZone, + end: zone.nextZone }) } else { @@ -79,9 +82,9 @@ export function initAdminSocketHandler() { } if (!game.setZoneSettings(settings)) { socket.emit("error", "Error changing zone"); - socket.emit("zone_settings", game.zone.zoneSettings) //Still broadcast the old config to the client who submited an incorrect config to keep the client up to date + socket.emit("zone_settings", zone.zoneSettings) //Still broadcast the old config to the client who submited an incorrect config to keep the client up to date } else { - secureAdminBroadcast("zone_settings", game.zone.zoneSettings) + secureAdminBroadcast("zone_settings", zone.zoneSettings) } }) diff --git a/traque-back/game.js b/traque-back/game.js index f7ac5d7..c6e082d 100644 --- a/traque-back/game.js +++ b/traque-back/game.js @@ -1,8 +1,9 @@ import { secureAdminBroadcast } from "./admin_socket.js"; -import { penaltyController } from "./index.js"; import { isInCircle } from "./map_utils.js"; import { playersBroadcast, sendUpdatedTeamInformations } from "./team_socket.js"; -import { ZoneManager } from "./zone_manager.js"; + +import penaltyController from "./penalty_controller.js"; +import zoneManager from "./zone_manager.js"; export const GameState = { SETUP: "setup", @@ -11,23 +12,20 @@ export const GameState = { FINISHED: "finished" } -export default class Game { - constructor(onUpdateZone, onUpdateNewZone) { - this.teams = []; - this.state = GameState.SETUP; - this.zone = new ZoneManager(onUpdateZone, onUpdateNewZone) - this.settings = { - loserEndGameMessage: "", - winnerEndGameMessage: "", - capturedMessage: "", - waitingMessage: "Jeu en préparation, veuillez patienter." - } - } +export default { + teams : [], + state : GameState.SETUP, + settings : { + loserEndGameMessage: "", + winnerEndGameMessage: "", + capturedMessage: "", + waitingMessage: "Jeu en préparation, veuillez patienter." + }, changeSettings(newSettings) { this.settings = {...this.settings, ...newSettings}; return true; - } + }, setState(newState) { if (Object.values(GameState).indexOf(newState) == -1) { @@ -36,19 +34,19 @@ export default class Game { //The game has started if (newState == GameState.PLAYING) { penaltyController.start(); - if (!this.zone.ready()) { + if (!zoneManager.ready()) { return false; } this.initLastSentLocations(); - this.zone.reset() + zoneManager.reset() //If the zone cannot be setup, reset everything - if (!this.zone.start()) { + if (!zoneManager.start()) { this.setState(GameState.SETUP); return; } } if (newState != GameState.PLAYING) { - this.zone.reset(); + zoneManager.reset(); penaltyController.stop(); } //Game reset @@ -65,7 +63,7 @@ export default class Game { } this.state = newState; return true; - } + }, getNewTeamId() { let id = null; @@ -73,11 +71,11 @@ export default class Game { id = Math.floor(Math.random() * 1_000_000); } return id; - } + }, createCaptureCode() { return Math.floor(Math.random() * 10000) - } + }, addTeam(teamName) { let id = this.getNewTeamId(); @@ -100,7 +98,7 @@ export default class Game { }); this.updateTeamChasing(); return true; - } + }, playingTeamCount() { let res = 0; @@ -110,7 +108,7 @@ export default class Game { } }) return res; - } + }, updateTeamChasing() { if (this.playingTeamCount() <= 2) { @@ -138,16 +136,16 @@ export default class Game { this.getTeam(previousTeam).enemyName = this.getTeam(firstTeam).name; secureAdminBroadcast("teams", this.teams) return true; - } + }, reorderTeams(newOrder) { this.teams = newOrder; return this.updateTeamChasing(); - } + }, getTeam(teamId) { return this.teams.find(t => t.id === teamId); - } + }, updateTeam(teamId, newTeam) { this.teams = this.teams.map((t) => { @@ -160,7 +158,7 @@ export default class Game { this.updateTeamChasing(); penaltyController.checkPenalties(); return true; - } + }, updateLocation(teamId, location) { let team = this.getTeam(teamId); @@ -176,7 +174,7 @@ export default class Game { team.ready = isInCircle({ lat: location[0], lng: location[1] }, team.startingArea.center, team.startingArea.radius) } return true; - } + }, //Make it so that when a team requests the location of a team that has never sent their locaiton //Their position at the begining of the game is sent @@ -186,7 +184,7 @@ export default class Game { team.locationSendDeadline = Number(new Date()) + penaltyController.settings.allowedTimeBetweenPositionUpdate * 60 * 1000; sendUpdatedTeamInformations(team.id); } - } + }, sendLocation(teamId) { let team = this.getTeam(teamId); @@ -203,7 +201,7 @@ export default class Game { team.enemyLocation = this.getTeam(team.chasing).lastSentLocation; } return team; - } + }, removeTeam(teamId) { if (this.getTeam(teamId) == undefined) { @@ -213,7 +211,7 @@ export default class Game { this.teams = this.teams.filter(t => t.id !== teamId); this.updateTeamChasing(); return true; - } + }, /** * Request a capture initiated by the team with id teamId (the one trying to capture) @@ -231,7 +229,7 @@ export default class Game { return true; } return false; - } + }, /** * Set a team to captured and update the chase chain @@ -240,7 +238,7 @@ export default class Game { capture(teamId) { this.getTeam(teamId).captured = true this.updateTeamChasing(); - } + }, /** * Change the settings of the Zone manager @@ -253,12 +251,12 @@ export default class Game { if (this.state == GameState.PLAYING || this.state == GameState.FINISHED) { return false; } - return this.zone.udpateSettings(newSettings) - } + return zoneManager.udpateSettings(newSettings) + }, finishGame() { this.setState(GameState.FINISHED); - this.zone.reset(); + zoneManager.reset(); playersBroadcast("game_state", this.state); - } + }, } \ No newline at end of file diff --git a/traque-back/index.js b/traque-back/index.js index 88b2a4d..8eda696 100644 --- a/traque-back/index.js +++ b/traque-back/index.js @@ -2,12 +2,10 @@ import { createServer } from "https"; import express from "express"; import { Server } from "socket.io"; -import Game from "./game.js"; import { config } from "dotenv"; import { readFileSync } from "fs"; -import { initAdminSocketHandler, secureAdminBroadcast } from "./admin_socket.js"; -import { initTeamSocket, playersBroadcast } from "./team_socket.js"; -import { PenaltyController } from "./penalty_controller.js"; +import { initAdminSocketHandler } from "./admin_socket.js"; +import { initTeamSocket } from "./team_socket.js"; import { initPhotoUpload } from "./photo.js"; //extract admin password from .env file config(); @@ -35,22 +33,6 @@ export const io = new Server(httpsServer, { } }); -//Zone update broadcast function, called by the game object -function onUpdateNewZone(newZone) { - playersBroadcast("new_zone", newZone) - secureAdminBroadcast("new_zone", newZone) -} - -function onUpdateZone(zone) { - playersBroadcast("zone", zone) - secureAdminBroadcast("zone", zone) -} - - -export const game = new Game(onUpdateZone, onUpdateNewZone); -export const penaltyController = new PenaltyController(); - - initAdminSocketHandler(); initTeamSocket(); initPhotoUpload(); \ No newline at end of file diff --git a/traque-back/penalty_controller.js b/traque-back/penalty_controller.js index 88c7f56..c6e20fa 100644 --- a/traque-back/penalty_controller.js +++ b/traque-back/penalty_controller.js @@ -1,21 +1,18 @@ -import { getDistanceFromLatLon, isInCircle } from "./map_utils.js"; +import { isInCircle } from "./map_utils.js"; import { sendUpdatedTeamInformations, teamBroadcast } from "./team_socket.js"; -import { GameState } from "./game.js"; import { secureAdminBroadcast } from "./admin_socket.js"; -import { game } from "./index.js"; +import game, {GameState} from "./game.js"; +import zone from "./zone_manager.js"; -export class PenaltyController { - constructor() { +export default { + outOfBoundsSince: {}, + checkIntervalId: null, + settings: { + allowedTimeOutOfZone: 10, + allowedTimeBetweenPositionUpdate: 10, //Number of penalties needed to be eliminated - this.game = game; - this.outOfBoundsSince = {}; - this.checkIntervalId = null; - this.settings = { - allowedTimeOutOfZone: 10, - allowedTimeBetweenPositionUpdate: 10, - maxPenalties: 3 - } - } + maxPenalties: 3 + }, start() { this.outOfBoundsSince = {}; @@ -24,12 +21,12 @@ export class PenaltyController { } //Watch periodically if all teams need are following the rules this.checkIntervalId = setInterval(() => { - if (this.game.state == GameState.PLAYING) { + if (game.state == GameState.PLAYING) { this.watchPositionUpdate(); this.watchZone(); } }, 100); - } + }, stop() { this.outOfBoundsSince = {}; @@ -37,7 +34,7 @@ export class PenaltyController { clearInterval(this.checkIntervalId) this.checkIntervalId = null; } - } + }, updateSettings(newSettings) { //Sanitize input @@ -47,14 +44,14 @@ export class PenaltyController { this.settings = { ...this.settings, ...newSettings }; return true; - } + }, /** * Increment the penalty score of a team, send a message to the team and eliminated if necessary * @param {Number} teamId The team that will recieve a penalty */ addPenalty(teamId) { - let team = this.game.getTeam(teamId); + let team = game.getTeam(teamId); if (!team) { return; } @@ -63,7 +60,7 @@ export class PenaltyController { } team.penalties++; if (team.penalties >= this.settings.maxPenalties) { - this.game.capture(team.id); + game.capture(team.id); sendUpdatedTeamInformations(teamId); sendUpdatedTeamInformations(team.chased); teamBroadcast(teamId, "warning", "You have been eliminated (reason: too many penalties)") @@ -72,29 +69,29 @@ export class PenaltyController { teamBroadcast(teamId, "warning", `You recieved a penalty (${team.penalties}/${this.settings.maxPenalties})`) sendUpdatedTeamInformations(teamId); } - secureAdminBroadcast("teams", this.game.teams) - } + secureAdminBroadcast("teams", game.teams) + }, checkPenalties() { for (let team of game.teams) { if (team.penalties >= this.settings.maxPenalties) { - this.game.capture(team.id); + game.capture(team.id); sendUpdatedTeamInformations(team.id); sendUpdatedTeamInformations(team.chased); teamBroadcast(team.id, "warning", "You have been eliminated (reason: too many penalties)") teamBroadcast(team.chased, "success", "The team you were chasing has been eliminated") } } - } + }, watchZone() { - this.game.teams.forEach((team) => { + game.teams.forEach((team) => { if (team.captured) { return } //All the informations are not ready yet - if (team.currentLocation == null || this.game.zone.currentZone == null) { + if (team.currentLocation == null || zone.currentZone == null) { return; } - if (!isInCircle({ lat: team.currentLocation[0], lng: team.currentLocation[1] }, this.game.zone.currentZone.center, this.game.zone.currentZone.radius)) { + if (!isInCircle({ lat: team.currentLocation[0], lng: team.currentLocation[1] }, zone.currentZone.center, zone.currentZone.radius)) { //The team was not previously out of the zone if (!this.outOfBoundsSince[team.id]) { this.outOfBoundsSince[team.id] = new Date(); @@ -111,11 +108,11 @@ export class PenaltyController { } } }) - } + }, watchPositionUpdate() { - this.game.teams.forEach((team) => { + game.teams.forEach((team) => { //If the team has not sent their location for more than the allowed period, automatically send it and add a penalty if (team.captured) { return } if (team.locationSendDeadline == null) { @@ -124,12 +121,12 @@ export class PenaltyController { } if (new Date() > team.locationSendDeadline) { this.addPenalty(team.id); - this.game.sendLocation(team.id); + game.sendLocation(team.id); sendUpdatedTeamInformations(team.id); - secureAdminBroadcast("teams", this.game.teams) - }else if(Math.abs(new Date() - team.locationSendDeadline - 60 * 1000) < 100) { + secureAdminBroadcast("teams", game.teams) + } else if (Math.abs(new Date() - team.locationSendDeadline - 60 * 1000) < 100) { teamBroadcast(team.id, "warning", `You have one minute left to udpate your location.`) } }) - } + }, } \ No newline at end of file diff --git a/traque-back/photo.js b/traque-back/photo.js index b23defb..5ff5f6d 100644 --- a/traque-back/photo.js +++ b/traque-back/photo.js @@ -1,7 +1,8 @@ -import { app, game } from "./index.js"; +import { app } from "./index.js"; import multer from "multer"; import fs from "fs"; import path from "path"; +import game from "./game.js"; const UPLOAD_DIR = "uploads/" const ALLOWED_MIME = [ "image/png", diff --git a/traque-back/team_socket.js b/traque-back/team_socket.js index 23dbddf..c2e15b4 100644 --- a/traque-back/team_socket.js +++ b/traque-back/team_socket.js @@ -1,5 +1,7 @@ import { secureAdminBroadcast } from "./admin_socket.js"; -import { io, game } from "./index.js"; +import { io} from "./index.js"; +import game from "./game.js"; +import zone from "./zone_manager.js"; /** * Send a socket message to all the players of a team @@ -79,10 +81,10 @@ export function initTeamSocket() { socket.emit("login_response", true); socket.emit("game_state", game.state) socket.emit("game_settings", game.settings) - socket.emit("zone", game.zone.currentZone) + socket.emit("zone", zone.currentZone) socket.emit("new_zone", { - begin: game.zone.currentStartZone, - end: game.zone.nextZone + begin: zone.currentStartZone, + end: zone.nextZone }) }); diff --git a/traque-back/zone_manager.js b/traque-back/zone_manager.js index 31cdb91..d00d02f 100644 --- a/traque-back/zone_manager.js +++ b/traque-back/zone_manager.js @@ -1,42 +1,40 @@ import { randomCirclePoint } from 'random-location' import { isInCircle } from './map_utils.js'; import { map } from './util.js'; +import { playersBroadcast } from './team_socket.js'; +import { secureAdminBroadcast } from './admin_socket.js'; -export class ZoneManager { - constructor(onZoneUpdate, onNextZoneUpdate) { - //Setings storing where the zone will start, end and how it should evolve - //The zone will start by staying at its mzx value for reductionInterval minutes - //and then reduce during reductionDuration minutes, then wait again... - //The reduction factor is such that after reductionCount the zone will be the min zone - //a call to onZoneUpdate will be made every updateIntervalSeconds when the zone is changing - //a call to onNextZoneUpdate will be made when the zone reduction ends and a new next zone is announced - this.zoneSettings = { - min: { center: null, radius: null }, - max: { center: null, radius: null }, - reductionCount: 2, - reductionDuration: 1, - reductionInterval: 1, - updateIntervalSeconds: 1, - } - this.nextZoneDecrement = null; - //Live location of the zone - this.currentZone = { center: null, radius: null }; +export default { + //Setings storing where the zone will start, end and how it should evolve + //The zone will start by staying at its mzx value for reductionInterval minutes + //and then reduce during reductionDuration minutes, then wait again... + //The reduction factor is such that after reductionCount the zone will be the min zone + //a call to onZoneUpdate will be made every updateIntervalSeconds when the zone is changing + //a call to onNextZoneUpdate will be made when the zone reduction ends and a new next zone is announced + zoneSettings: { + min: { center: null, radius: null }, + max: { center: null, radius: null }, + reductionCount: 2, + reductionDuration: 1, + reductionInterval: 1, + updateIntervalSeconds: 1, + }, - //If the zone is shrinking, this is the target of the current shrinking - //If the zone is not shrinking, this will be the target of the next shrinking - this.nextZone = { center: null, radius: null }; + nextZoneDecrement: null, + //Live location of the zone + currentZone: { center: null, radius: null }, - //Zone at the begining of the shrinking - this.currentStartZone = { center: null, radius: null }; + //If the zone is shrinking, this is the target of the current shrinking + //If the zone is not shrinking, this will be the target of the next shrinking + nextZone: { center: null, radius: null }, - this.startDate = null; - this.started = false; - this.updateIntervalId = null; - this.nextZoneTimeoutId = null; + //Zone at the begining of the shrinking + currentStartZone: { center: null, radius: null }, - this.onZoneUpdate = onZoneUpdate; - this.onNextZoneUpdate = onNextZoneUpdate - } + startDate: null, + started: false, + updateIntervalId: null, + nextZoneTimeoutId: null, /** * Test if a given configuration object is valid, i.e if all needed values are well defined @@ -51,14 +49,14 @@ export class ZoneManager { if (settings.max && (typeof settings.max.radius != "number" || typeof settings.max.center.lat != "number" || typeof settings.max.center.lng != "number")) { return false } if (settings.min && (typeof settings.min.radius != "number" || typeof settings.min.center.lat != "number" || typeof settings.min.center.lng != "number")) { return false } return true; - } + }, /** * Test if the zone manager is ready to start * @returns true if the zone manager is ready to be started, false otherwise */ ready() { return this.validateSettings(this.zoneSettings); - } + }, /** * Update the settings of the zone, this can be done by passing an object containing the settings to change. @@ -80,7 +78,7 @@ export class ZoneManager { this.zoneSettings = { ...this.zoneSettings, ...newSettings }; this.nextZoneDecrement = (this.zoneSettings.max.radius - this.zoneSettings.min.radius) / this.zoneSettings.reductionCount; return true; - } + }, /** * Reinitialize the object and stop all the tasks @@ -96,7 +94,7 @@ export class ZoneManager { clearTimeout(this.nextZoneTimeoutId); this.nextZoneTimeoutId = null; } - } + }, /** * Start the zone reduction sequence @@ -110,7 +108,7 @@ export class ZoneManager { this.currentZone = JSON.parse(JSON.stringify(this.zoneSettings.max)); return this.setNextZone(); - } + }, /** * Get the center of the next zone, this center need to satisfy two properties @@ -129,14 +127,14 @@ export class ZoneManager { res = randomCirclePoint({ latitude: this.currentZone.center.lat, longitude: this.currentZone.center.lng }, this.currentZone.radius - newRadius); ok = (isInCircle({ lat: res.latitude, lng: res.longitude }, this.zoneSettings.min.center, newRadius - this.zoneSettings.min.radius)) } - if(tries>=MAX_TRIES) { + if (tries >= MAX_TRIES) { return false; } return { lat: res.latitude, lng: res.longitude } - } + }, /** * Compute the next zone satifying the given settings, update the nextZone and currentStartZone @@ -156,7 +154,7 @@ export class ZoneManager { } else if (this.currentZoneCount < this.zoneSettings.reductionCount) { this.nextZone.center = this.getRandomNextCenter(this.nextZone.radius - this.nextZoneDecrement) //Next center cannot be found - if(this.nextZone.center === false) { + if (this.nextZone.center === false) { console.log("no center") return false; } @@ -171,7 +169,7 @@ export class ZoneManager { end: JSON.parse(JSON.stringify(this.nextZone)) }) return true; - } + }, /* * Start a task that will run periodically updatinng the zone size, and calling the onZoneUpdate callback @@ -183,8 +181,8 @@ export class ZoneManager { this.updateIntervalId = setInterval(() => { const completed = ((new Date() - startTime) / (1000 * 60)) / this.zoneSettings.reductionDuration; this.currentZone.radius = map(completed, 0, 1, this.currentStartZone.radius, this.nextZone.radius) - this.currentZone.center.lat = map(completed,0,1, this.currentStartZone.center.lat, this.nextZone.center.lat) - this.currentZone.center.lng = map(completed,0,1, this.currentStartZone.center.lng, this.nextZone.center.lng) + this.currentZone.center.lat = map(completed, 0, 1, this.currentStartZone.center.lat, this.nextZone.center.lat) + this.currentZone.center.lng = map(completed, 0, 1, this.currentStartZone.center.lng, this.nextZone.center.lng) this.onZoneUpdate(JSON.parse(JSON.stringify(this.currentZone))) //Zone shrinking is over if (completed >= 1) { @@ -194,6 +192,16 @@ export class ZoneManager { return; } }, this.zoneSettings.updateIntervalSeconds * 1000); - } + }, + + onNextZoneUpdate(newZone) { + playersBroadcast("new_zone", newZone) + secureAdminBroadcast("new_zone", newZone) + }, + + onZoneUpdate(zone) { + playersBroadcast("zone", zone) + secureAdminBroadcast("zone", zone) + }, } \ No newline at end of file