diff --git a/traque-back/admin_socket.js b/traque-back/admin_socket.js index 93c7b2d..1d4d58d 100644 --- a/traque-back/admin_socket.js +++ b/traque-back/admin_socket.js @@ -1,4 +1,4 @@ -import { io, game } from "./index.js"; +import { io, game, penaltyController } from "./index.js"; import { playersBroadcast, sendUpdatedTeamInformations } from "./team_socket.js"; import { config } from "dotenv"; @@ -45,6 +45,8 @@ export function initAdminSocketHandler() { //Send the current state socket.emit("game_state", game.state) //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("new_zone", { @@ -58,6 +60,18 @@ export function initAdminSocketHandler() { } }); + socket.on("set_game_settings", (settings) => { + if (!loggedIn) { + socket.emit("error", "Not logged in"); + return; + } + if(!game.changeSettings(settings)) { + socket.emit("error", "Invalid settings"); + } + secureAdminBroadcast("game_settings",game.settings); + playersBroadcast("game_settings", game.settings); + }) + socket.on("set_zone_settings", (settings) => { if (!loggedIn) { socket.emit("error", "Not logged in"); @@ -72,6 +86,20 @@ export function initAdminSocketHandler() { }) + socket.on("set_penalty_settings", (settings) => { + if (!loggedIn) { + socket.emit("error", "Not logged in"); + return; + } + if(!penaltyController.updateSettings(settings)) { + socket.emit("error", "Invalid settings"); + socket.emit("penalty_settings", penaltyController.settings) + }else { + secureAdminBroadcast("penalty_settings", penaltyController.settings) + } + + }) + //User is attempting to add a new team socket.on("add_team", (teamName) => { if (!loggedIn) { diff --git a/traque-back/game.js b/traque-back/game.js index 411f3da..9e9d6f3 100644 --- a/traque-back/game.js +++ b/traque-back/game.js @@ -16,6 +16,17 @@ export default class Game { this.teams = []; this.state = GameState.SETUP; this.zone = new ZoneManager(onUpdateZone, onUpdateNewZone) + this.settings = { + loserEndGameMessage: "", + winnerEndGameMessage: "", + capturedMessage: "", + waitingMessage: "Jeu en préparation, veuillez patienter." + } + } + + changeSettings(newSettings) { + this.settings = {...this.settings, ...newSettings}; + return true; } setState(newState) { @@ -31,7 +42,7 @@ export default class Game { this.initLastSentLocations(); this.zone.reset() //If the zone cannot be setup, reset everything - if(!this.zone.start()) { + if (!this.zone.start()) { this.setState(GameState.SETUP); return; } @@ -41,10 +52,14 @@ export default class Game { penaltyController.stop(); } //Game reset - if(newState == GameState.SETUP) { - for(let team of this.teams) { + if (newState == GameState.SETUP) { + for (let team of this.teams) { team.penalties = 0; team.captured = false; + team.enemyLocation = null; + team.enemyName = null; + team.currentLocation = null; + team.lastSentLocation = null; } this.updateTeamChasing(); } @@ -99,7 +114,7 @@ export default class Game { updateTeamChasing() { if (this.playingTeamCount() <= 2) { - if(this.state == GameState.PLAYING) { + if (this.state == GameState.PLAYING) { this.finishGame() } return false; @@ -143,6 +158,7 @@ export default class Game { } }) this.updateTeamChasing(); + penaltyController.checkPenalties(); return true; } @@ -164,7 +180,7 @@ export default class Game { initLastSentLocations() { for (let team of this.teams) { team.lastSentLocation = team.currentLocation; - team.locationSendDeadline = Number(new Date()) + process.env.ALLOWED_TIME_BETWEEN_POSITION_UPDATE_IN_MINUTES * 60 * 1000; + team.locationSendDeadline = Number(new Date()) + penaltyController.settings.allowedTimeBetweenPositionUpdate * 60 * 1000; sendUpdatedTeamInformations(team.id); } } @@ -174,7 +190,7 @@ export default class Game { if (team == undefined) { return false; } - team.locationSendDeadline = Number(new Date()) + process.env.ALLOWED_TIME_BETWEEN_POSITION_UPDATE_IN_MINUTES * 60 * 1000; + team.locationSendDeadline = Number(new Date()) + penaltyController.settings.allowedTimeBetweenPositionUpdate * 60 * 1000; team.lastSentLocation = team.currentLocation; if (this.getTeam(team.chasing) != null) { team.enemyLocation = this.getTeam(team.chasing).lastSentLocation; diff --git a/traque-back/penalty_controller.js b/traque-back/penalty_controller.js index 926d3c2..102c799 100644 --- a/traque-back/penalty_controller.js +++ b/traque-back/penalty_controller.js @@ -1,10 +1,8 @@ -import { config } from "dotenv"; import { getDistanceFromLatLon, 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"; -config() export class PenaltyController { constructor() { @@ -12,75 +10,100 @@ export class PenaltyController { this.game = game; this.outOfBoundsSince = {}; this.checkIntervalId = null; + this.settings = { + allowedTimeOutOfZone: 10, + allowedTimeBetweenPositionUpdate: 10, + maxPenalties: 3 + } } start() { this.outOfBoundsSince = {}; - if(this.checkIntervalId) { + if (this.checkIntervalId) { clearInterval(this.checkIntervalId) } //Watch periodically if all teams need are following the rules this.checkIntervalId = setInterval(() => { - if(this.game.state == GameState.PLAYING) { + if (this.game.state == GameState.PLAYING) { this.watchPositionUpdate(); this.watchZone(); } }, 100); } - stop(){ + stop() { this.outOfBoundsSince = {}; - if(this.checkIntervalId) { + if (this.checkIntervalId) { clearInterval(this.checkIntervalId) this.checkIntervalId = null; } } + + updateSettings(newSettings) { + //Sanitize input + if (newSettings.maxPenalties && (isNaN(parseInt(newSettings.maxPenalties)) || newSettings.maxPenalties < 0)) { return false } + if (newSettings.allowedTimeBetweenPositionUpdate && (isNaN(parseFloat(newSettings.allowedTimeBetweenPositionUpdate)) || newSettings.allowedTimeBetweenPositionUpdate < 0)) { return false } + if (newSettings.allowedTimeOutOfZone && (isNaN(parseFloat(newSettings.allowedTimeOutOfZone)) || newSettings.allowedTimeOutOfZone < 0)) { return false } + + 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); - if(!team) { + if (!team) { return; } if (team.captured) { return; } team.penalties++; - if (team.penalties >= process.env.MAX_PENALTIES) { + if (team.penalties >= this.settings.maxPenalties) { this.game.capture(team.id); sendUpdatedTeamInformations(teamId); sendUpdatedTeamInformations(team.chased); teamBroadcast(teamId, "warning", "You have been eliminated (reason: too many penalties)") teamBroadcast(team.chased, "success", "The team you were chasing has been eliminated") } else { - teamBroadcast(teamId, "warning", `You recieved a penalty (${team.penalties}/${process.env.MAX_PENALTIES})`) + teamBroadcast(teamId, "warning", `You recieved a penalty (${team.penalties}/${this.settings.maxPenalties})`) sendUpdatedTeamInformations(teamId); } secureAdminBroadcast("teams", this.game.teams) } + checkPenalties() { + for (let team of game.teams) { + if (team.penalties >= this.settings.maxPenalties) { + this.game.capture(team.id); + sendUpdatedTeamInformations(teamId); + sendUpdatedTeamInformations(team.chased); + teamBroadcast(teamId, "warning", "You have been eliminated (reason: too many penalties)") + teamBroadcast(team.chased, "success", "The team you were chasing has been eliminated") + } + } + } + watchZone() { - console.log("watching zone") this.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 || this.game.zone.currentZone == null) { return; } - if (!isInCircle({lat: team.currentLocation[0], lng: team.currentLocation[1]}, this.game.zone.currentZone.center, this.game.zone.currentZone.radius)) { - console.log("tema " + team.name + " out of zone") + if (!isInCircle({ lat: team.currentLocation[0], lng: team.currentLocation[1] }, this.game.zone.currentZone.center, this.game.zone.currentZone.radius)) { //The team was not previously out of the zone if (!this.outOfBoundsSince[team.id]) { this.outOfBoundsSince[team.id] = new Date(); - console.log("tema " + team.name + " warned") - teamBroadcast(team.id, "warning", `You left the zone, you have ${process.env.ALLOWED_TIME_OUT_OF_ZONE_IN_MINUTES} minutes to get back in the marked area.`) - } else { - if (new Date() - this.outOfBoundsSince[team.id] > process.env.ALLOWED_TIME_OUT_OF_ZONE_IN_MINUTES * 60 * 1000) { - this.addPenalty(team.id) - this.outOfBoundsSince[team.id] = new Date(); - } + teamBroadcast(team.id, "warning", `You left the zone, you have ${this.settings.allowedTimeOutOfZone} minutes to get back in the marked area.`) + } else if (new Date() - this.outOfBoundsSince[team.id] > this.settings.allowedTimeOutOfZone * 60 * 1000) { + this.addPenalty(team.id) + this.outOfBoundsSince[team.id] = new Date(); + } else if (Math.abs(new Date() - this.outOfBoundsSince[team.id] - (this.settings.allowedTimeOutOfZone - 1) * 60 * 1000) < 100) { + teamBroadcast(team.id, "warning", `You left the zone, you have 1 minutes to get back in the marked area.`) } } else { if (this.outOfBoundsSince[team.id]) { @@ -90,12 +113,13 @@ export class PenaltyController { }) } + watchPositionUpdate() { this.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) { - team.locationSendDeadline = Number(new Date()) + process.env.ALLOWED_TIME_BETWEEN_POSITION_UPDATE_IN_MINUTES * 60 * 1000; + if (team.locationSendDeadline == null) { + team.locationSendDeadline = Number(new Date()) + this.settings.allowedTimeBetweenPositionUpdate * 60 * 1000; return; } if (new Date() > team.locationSendDeadline) { @@ -103,6 +127,8 @@ export class PenaltyController { this.game.sendLocation(team.id); sendUpdatedTeamInformations(team.id); secureAdminBroadcast("teams", this.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.`) } }) } diff --git a/traque-back/team_socket.js b/traque-back/team_socket.js index 8fcd726..87485f0 100644 --- a/traque-back/team_socket.js +++ b/traque-back/team_socket.js @@ -75,6 +75,7 @@ export function initTeamSocket() { sendUpdatedTeamInformations(loginTeamId); 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("new_zone", { begin: game.zone.currentStartZone, @@ -111,6 +112,7 @@ export function initTeamSocket() { } game.updateTeamChasing(); teamBroadcast(teamId, "update_team", { enemyLocation: team.enemyLocation,locationSendDeadline: team.locationSendDeadline }); + teamBroadcast(teamId,"success", "Position udpated") secureAdminBroadcast("teams", game.teams) });