From 069b69567ba040aa7b10dff1527c113374ec5e02 Mon Sep 17 00:00:00 2001 From: Quentin Roussel Date: Thu, 18 Apr 2024 21:47:19 +0000 Subject: [PATCH] basic penalty backend implemented --- traque-back/game.js | 22 +++++++-- traque-back/index.js | 3 ++ traque-back/penalty_controller.js | 81 ++++++++++++++++++++++++++++--- traque-back/readme.md | 2 + traque-back/team_socket.js | 4 +- traque-back/zone_manager.js | 1 + 6 files changed, 99 insertions(+), 14 deletions(-) diff --git a/traque-back/game.js b/traque-back/game.js index d3a77cf..d7087fd 100644 --- a/traque-back/game.js +++ b/traque-back/game.js @@ -1,7 +1,7 @@ import { isInCircle } from "./map_utils.js"; import { ZoneManager } from "./zone_manager.js"; -const GameState = { +export const GameState = { SETUP: "setup", PLACEMENT: "placement", PLAYING: "playing", @@ -10,8 +10,6 @@ const GameState = { export default class Game { constructor(onUpdateZone,onUpdateNewZone) { - //Number of penalties needed to be eliminated - this.MAX_PENALTIES = 3; this.teams = []; this.state = GameState.SETUP; this.zone = new ZoneManager(onUpdateZone, onUpdateNewZone) @@ -58,6 +56,7 @@ export default class Game { chased: null, currentLocation: null, lastSentLocation: null, + lastSentLocationDate: null, enemyLocation: null, captureCode: this.createCaptureCode(), sockets: [], @@ -70,8 +69,18 @@ export default class Game { return true; } + playingTeamCount() { + let res = 0; + this.teams.forEach((t) => { + if(!t.captured) { + res++; + } + }) + return res; + } + updateTeamChasing() { - if(this.teams.length <= 1) { + if(this.playingTeamCount() <= 1) { return false; } let firstTeam = null; @@ -139,8 +148,11 @@ export default class Game { if(team == undefined) { return false; } + team.lastSentLocationDate = new Date(); team.lastSentLocation = team.currentLocation; - team.enemyLocation = this.getTeam(team.chasing).lastSentLocation; + if(this.getTeam(team.chasing) != null) { + team.enemyLocation = this.getTeam(team.chasing).lastSentLocation; + } return team; } diff --git a/traque-back/index.js b/traque-back/index.js index 44eced3..456924c 100644 --- a/traque-back/index.js +++ b/traque-back/index.js @@ -5,6 +5,7 @@ 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"; //extract admin password from .env file config(); const HOST = process.env.HOST; @@ -41,6 +42,8 @@ function onUpdateZone(zone) { export const game = new Game(onUpdateZone, onUpdateNewZone); +const penaltyController = new PenaltyController(game); +penaltyController.init() initAdminSocketHandler(); diff --git a/traque-back/penalty_controller.js b/traque-back/penalty_controller.js index 44681af..af24885 100644 --- a/traque-back/penalty_controller.js +++ b/traque-back/penalty_controller.js @@ -1,27 +1,92 @@ -import { game } from "."; -import { sendUpdatedTeamInformations, teamBroadcast } from "./team_socket"; +import { config } from "dotenv"; +import { getDistanceFromLatLon, isInCircle } from "./map_utils.js"; +import { sendUpdatedTeamInformations, teamBroadcast } from "./team_socket.js"; +import { GameState } from "./game.js"; +config() export class PenaltyController { constructor(game) { - + //Number of penalties needed to be eliminated + this.game = game; + this.outOfBoundsSince = {}; + this.checkIntervalId = null; } + init() { + this.outOfBoundsSince = {}; + 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) { + this.watchPositionUpdate(); + this.watchZone(); + } + }, 100); + } + + /** + * 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 = game.getTeam(teamId); - if(team.captured) { + let team = this.game.getTeam(teamId); + if (team.captured) { return; } team.penalties++; - if(team.penalties == game.MAX_PENALTIES) { - game.requestCapture(team.chased); + if (team.penalties >= process.env.MAX_PENALTIES) { + 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 are chasing has changed") + 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})`) + sendUpdatedTeamInformations(teamId); } } watchZone() { + 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) { + return; + } + 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(); + 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(); + } + } + } else { + if (this.outOfBoundsSince[team.id]) { + delete this.outOfBoundsSince[team.id]; + } + } + }) + } + 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.lastSentLocationDate == null) { + team.lastSentLocationDate = new Date(); + return; + } + if (new Date() - team.lastSentLocationDate > process.env.ALLOWED_TIME_BETWEEN_POSITION_UPDATE_IN_MINUTES * 60 * 1000) { + this.addPenalty(team.id); + this.game.sendLocation(team.id); + sendUpdatedTeamInformations(team.id); + } + }) } } \ No newline at end of file diff --git a/traque-back/readme.md b/traque-back/readme.md index 086c451..99088bd 100644 --- a/traque-back/readme.md +++ b/traque-back/readme.md @@ -5,4 +5,6 @@ HOST = '10.192.64.61' PORT = 3001 SSL_KEY = "key.pem" SSL_CERT = "server.crt" +ALLOWED_TIME_OUT_OF_ZONE_IN_MINUTES = 0.1 +ALLOWED_TIME_BETWEEN_POSITION_UPDATE_IN_MINUTES = 1 ``` \ No newline at end of file diff --git a/traque-back/team_socket.js b/traque-back/team_socket.js index 03c7a0b..c51ed34 100644 --- a/traque-back/team_socket.js +++ b/traque-back/team_socket.js @@ -42,10 +42,12 @@ export function sendUpdatedTeamInformations(teamId) { enemyLocation: team.enemyLocation, currentLocation: team.currentLocation, lastSentLocation: team.lastSentLocation, + lastSentLocationDate: team.lastSentLocationDate, captureCode: team.captureCode, startingArea: team.startingArea, ready: team.ready, - captured: team.captured + captured: team.captured, + penalties: team.penalties, }) }) } diff --git a/traque-back/zone_manager.js b/traque-back/zone_manager.js index 9c0f898..384ecc9 100644 --- a/traque-back/zone_manager.js +++ b/traque-back/zone_manager.js @@ -154,6 +154,7 @@ export class ZoneManager { this.nextZoneTimeoutId = setTimeout(() => this.startShrinking(), 1000 * 60 * this.zoneSettings.reductionInterval) this.currentZoneCount++; } + this.onZoneUpdate(JSON.parse(JSON.stringify(this.currentStartZone))) this.onNextZoneUpdate({ begin: JSON.parse(JSON.stringify(this.currentStartZone)), end: JSON.parse(JSON.stringify(this.nextZone))