basic penalty backend implemented

This commit is contained in:
2024-04-18 21:47:19 +00:00
parent f384878964
commit 069b69567b
6 changed files with 99 additions and 14 deletions

View File

@@ -1,7 +1,7 @@
import { isInCircle } from "./map_utils.js"; import { isInCircle } from "./map_utils.js";
import { ZoneManager } from "./zone_manager.js"; import { ZoneManager } from "./zone_manager.js";
const GameState = { export const GameState = {
SETUP: "setup", SETUP: "setup",
PLACEMENT: "placement", PLACEMENT: "placement",
PLAYING: "playing", PLAYING: "playing",
@@ -10,8 +10,6 @@ const GameState = {
export default class Game { export default class Game {
constructor(onUpdateZone,onUpdateNewZone) { constructor(onUpdateZone,onUpdateNewZone) {
//Number of penalties needed to be eliminated
this.MAX_PENALTIES = 3;
this.teams = []; this.teams = [];
this.state = GameState.SETUP; this.state = GameState.SETUP;
this.zone = new ZoneManager(onUpdateZone, onUpdateNewZone) this.zone = new ZoneManager(onUpdateZone, onUpdateNewZone)
@@ -58,6 +56,7 @@ export default class Game {
chased: null, chased: null,
currentLocation: null, currentLocation: null,
lastSentLocation: null, lastSentLocation: null,
lastSentLocationDate: null,
enemyLocation: null, enemyLocation: null,
captureCode: this.createCaptureCode(), captureCode: this.createCaptureCode(),
sockets: [], sockets: [],
@@ -70,8 +69,18 @@ export default class Game {
return true; return true;
} }
playingTeamCount() {
let res = 0;
this.teams.forEach((t) => {
if(!t.captured) {
res++;
}
})
return res;
}
updateTeamChasing() { updateTeamChasing() {
if(this.teams.length <= 1) { if(this.playingTeamCount() <= 1) {
return false; return false;
} }
let firstTeam = null; let firstTeam = null;
@@ -139,8 +148,11 @@ export default class Game {
if(team == undefined) { if(team == undefined) {
return false; return false;
} }
team.lastSentLocationDate = new Date();
team.lastSentLocation = team.currentLocation; team.lastSentLocation = team.currentLocation;
if(this.getTeam(team.chasing) != null) {
team.enemyLocation = this.getTeam(team.chasing).lastSentLocation; team.enemyLocation = this.getTeam(team.chasing).lastSentLocation;
}
return team; return team;
} }

View File

@@ -5,6 +5,7 @@ import { config } from "dotenv";
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import { initAdminSocketHandler, secureAdminBroadcast } from "./admin_socket.js"; import { initAdminSocketHandler, secureAdminBroadcast } from "./admin_socket.js";
import { initTeamSocket, playersBroadcast } from "./team_socket.js"; import { initTeamSocket, playersBroadcast } from "./team_socket.js";
import { PenaltyController } from "./penalty_controller.js";
//extract admin password from .env file //extract admin password from .env file
config(); config();
const HOST = process.env.HOST; const HOST = process.env.HOST;
@@ -41,6 +42,8 @@ function onUpdateZone(zone) {
export const game = new Game(onUpdateZone, onUpdateNewZone); export const game = new Game(onUpdateZone, onUpdateNewZone);
const penaltyController = new PenaltyController(game);
penaltyController.init()
initAdminSocketHandler(); initAdminSocketHandler();

View File

@@ -1,27 +1,92 @@
import { game } from "."; import { config } from "dotenv";
import { sendUpdatedTeamInformations, teamBroadcast } from "./team_socket"; import { getDistanceFromLatLon, isInCircle } from "./map_utils.js";
import { sendUpdatedTeamInformations, teamBroadcast } from "./team_socket.js";
import { GameState } from "./game.js";
config()
export class PenaltyController { export class PenaltyController {
constructor(game) { 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) { addPenalty(teamId) {
let team = game.getTeam(teamId); let team = this.game.getTeam(teamId);
if (team.captured) { if (team.captured) {
return; return;
} }
team.penalties++; team.penalties++;
if(team.penalties == game.MAX_PENALTIES) { if (team.penalties >= process.env.MAX_PENALTIES) {
game.requestCapture(team.chased); this.game.capture(team.id);
sendUpdatedTeamInformations(teamId); sendUpdatedTeamInformations(teamId);
sendUpdatedTeamInformations(team.chased); sendUpdatedTeamInformations(team.chased);
teamBroadcast(teamId, "warning", "You have been eliminated (reason: too many penalties)") 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() { 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);
}
})
} }
} }

View File

@@ -5,4 +5,6 @@ HOST = '10.192.64.61'
PORT = 3001 PORT = 3001
SSL_KEY = "key.pem" SSL_KEY = "key.pem"
SSL_CERT = "server.crt" SSL_CERT = "server.crt"
ALLOWED_TIME_OUT_OF_ZONE_IN_MINUTES = 0.1
ALLOWED_TIME_BETWEEN_POSITION_UPDATE_IN_MINUTES = 1
``` ```

View File

@@ -42,10 +42,12 @@ export function sendUpdatedTeamInformations(teamId) {
enemyLocation: team.enemyLocation, enemyLocation: team.enemyLocation,
currentLocation: team.currentLocation, currentLocation: team.currentLocation,
lastSentLocation: team.lastSentLocation, lastSentLocation: team.lastSentLocation,
lastSentLocationDate: team.lastSentLocationDate,
captureCode: team.captureCode, captureCode: team.captureCode,
startingArea: team.startingArea, startingArea: team.startingArea,
ready: team.ready, ready: team.ready,
captured: team.captured captured: team.captured,
penalties: team.penalties,
}) })
}) })
} }

View File

@@ -154,6 +154,7 @@ export class ZoneManager {
this.nextZoneTimeoutId = setTimeout(() => this.startShrinking(), 1000 * 60 * this.zoneSettings.reductionInterval) this.nextZoneTimeoutId = setTimeout(() => this.startShrinking(), 1000 * 60 * this.zoneSettings.reductionInterval)
this.currentZoneCount++; this.currentZoneCount++;
} }
this.onZoneUpdate(JSON.parse(JSON.stringify(this.currentStartZone)))
this.onNextZoneUpdate({ this.onNextZoneUpdate({
begin: JSON.parse(JSON.stringify(this.currentStartZone)), begin: JSON.parse(JSON.stringify(this.currentStartZone)),
end: JSON.parse(JSON.stringify(this.nextZone)) end: JSON.parse(JSON.stringify(this.nextZone))