From 40241311ee39743790219052c8e2ab6ffa3bd314 Mon Sep 17 00:00:00 2001 From: Quentin Roussel Date: Tue, 16 Apr 2024 21:35:56 +0000 Subject: [PATCH] =?UTF-8?q?premier=20test=20impl=C3=A9mentation=20d'un=20c?= =?UTF-8?q?omposant=20calculant=20la=20zone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- traque-back/game.js | 20 ++++++++-- traque-back/index.js | 34 ++++++++++++++-- traque-back/util.js | 13 +++++++ traque-back/zoneManager.js | 79 ++++++++++++++++++++++++++++++-------- 4 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 traque-back/util.js diff --git a/traque-back/game.js b/traque-back/game.js index c38f84f..cbbe555 100644 --- a/traque-back/game.js +++ b/traque-back/game.js @@ -1,4 +1,5 @@ import { isInCircle } from "./map_utils.js"; +import { ZoneManager } from "./zoneManager.js"; const GameState = { SETUP: "setup", @@ -8,10 +9,10 @@ const GameState = { } export default class Game { - constructor() { + constructor(onUpdateZone,onUpdateNewZone) { this.teams = []; this.state = GameState.SETUP; - this.zone = new ZoneManager(null, null) + this.zone = new ZoneManager(onUpdateZone, onUpdateNewZone) } setState(newState) { @@ -21,6 +22,11 @@ export default class Game { //The game has started if(newState == GameState.PLAYING) { this.initLastSentLocations(); + this.zone.reset() + this.zone.start() + } + if(newState != GameState.PLAYING) { + this.zone.reset(); } this.state = newState; return true; @@ -152,9 +158,15 @@ export default class Game { return false; } - setZone(newSettings) { + /** + * Change the settings of the Zone manager + * The game should not be in PLAYING or FINISHED state + * @param {Object} newSettings The object containing the settings to be changed + * @returns false if failed + */ + setZoneSettings(newSettings) { //cannot change zones while playing - if(game.state == GameState.PLAYING || game.state == GameState.FINISHED) { + if(this.state == GameState.PLAYING || this.state == GameState.FINISHED) { return false; } this.zone.udpateSettings(newSettings) diff --git a/traque-back/index.js b/traque-back/index.js index 21f488a..590d5d7 100644 --- a/traque-back/index.js +++ b/traque-back/index.js @@ -29,7 +29,7 @@ const io = new Server(httpsServer, { /** - * Send a message to all logged in sockets + * Send a message to all logged in admin sockets * @param {String} event The event name * @param {String} data The data to send */ @@ -39,27 +39,52 @@ function secureBroadcast(event, data) { }); } +/** + * Send a socket message to all the players of a team + * @param {String} teamId The team that will receive the message + * @param {String} event Event name + * @param {*} data The payload + */ function teamBroadcast(teamId, event, data) { for (let socketId of game.getTeam(teamId).sockets) { io.of("player").to(socketId).emit(event, data) } } +/** + * Send a message to all logged in players + * @param {String} event Event name + * @param {String} data payload + */ function playersBroadcast(event, data) { for (let team of game.teams) { teamBroadcast(team.id, event, data); } } +/** + * Remove a player from the list of logged in players + * @param {Number} id The id of the player to log out + */ function logoutPlayer(id) { for (let team of game.teams) { team.sockets = team.sockets.filter((sid) => sid != id); } } +//Zone update broadcast function, called by the game object +function onUpdateNewZone(newZone) { + playersBroadcast("new_zone", newZone) + secureBroadcast("new_zone", newZone) +} + +function onUpdateZone(zone) { + playersBroadcast("zone", zone) + secureBroadcast("zone", "zone") +} -const game = new Game(); +const game = new Game(onUpdateZone, onUpdateNewZone); //Array of logged in sockets let loggedInSockets = []; @@ -95,15 +120,16 @@ io.of("admin").on("connection", (socket) => { } }); - socket.on("set_zone", (zone) => { + socket.on("set_zone_settings", (zone) => { if (!loggedIn) { socket.emit("error", "Not logged in"); return; } - if(!game.setZone(zone)) { + if(!game.setZoneSettings(zone)) { socket.emit("error", "Error changing zone"); } + }) //User is attempting to add a new team diff --git a/traque-back/util.js b/traque-back/util.js new file mode 100644 index 0000000..1699761 --- /dev/null +++ b/traque-back/util.js @@ -0,0 +1,13 @@ +/** + * Scale a value that is known to be in a range to a new range + * for instance map(50,0,100,1000,2000) will return 1500 as 50 is halfway between 0 and 100 and 1500 is halfway through 1000 and 2000 + * @param {Number} value value to map + * @param {Number} oldMin minimum value of the number + * @param {Number} oldMax maximum value of the number + * @param {Number} newMin minimum value of the output + * @param {Number} newMax maximum value of the output + * @returns + */ +export function map(value, oldMin, oldMax, newMin, newMax) { + return ((value - oldMin) / (oldMax - oldMin)) * (newMax - newMin) + newMin; +} \ No newline at end of file diff --git a/traque-back/zoneManager.js b/traque-back/zoneManager.js index bb217cf..ee37296 100644 --- a/traque-back/zoneManager.js +++ b/traque-back/zoneManager.js @@ -1,7 +1,8 @@ import randomLocation, { randomCirclePoint } from 'random-location' import { isInCircle } from './map_utils'; +import { map } from './util'; -class ZoneManager { +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 @@ -19,17 +20,20 @@ class ZoneManager { } this.shrinkFactor = null; //Live location of the zone - this.currentZone = null; - this.currentDecrement = null; + this.currentZone = {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 - this.nextZone = null; + this.nextZone = {center: null, radius: null}; + + //Zone at the begining of the shrinking + this.currentStartZone = {center: null, radius: null}; this.startDate = null; this.zoneSettings = zoneSettings; this.started = false; - this.timeoutId = null; + this.updateIntervalId = null; + this.nextZoneTimeoutId = null; this.onZoneUpdate = onZoneUpdate; this.onNextZoneUpdate = onNextZoneUpdate @@ -40,14 +44,30 @@ class ZoneManager { this.shrinkFactor = Math.pow(this.zoneSettings.min.radius / this.zoneSettings.max.radius, 1/this.zoneSettings.reductionCount) } + /** + * Reinitialize the object and stop all the tasks + */ reset() { this.currentZoneCount = 0; + this.started = false; + if(this.updateIntervalId != null) { + clearInterval(this.updateIntervalId); + this.updateIntervalId = null; + } + if(this.nextZoneTimeoutId != null) { + clearTimeout(this.nextZoneTimeoutId); + this.nextZoneTimeoutId = null; + } } + /** + * Start the zone reduction sequence + */ start() { this.started = true; this.startDate = new Date(); - requestAnimationFrame(tick); + //initialize the zone to its max value + this.currentStartZone = this.currentZone = JSON.parse(JSON.stringify(this.zoneSettings.max)); } /** @@ -60,33 +80,60 @@ class ZoneManager { getRandomNextCenter(newRadius) { let ok = false; let res = null + //take a random point satisfying both conditions while(!ok) { - res = randomCirclePoint({latitude: this.currentZone.lat, longitude: this.currentZone.long}, this.currentZone.radius - newRadius); - ok = (isInCircle({lat: res.latitude, long: res.longitude}, this.zoneSettings.min.center, newRadius - this.zoneSettings.min.radius)) - } - return { - lat: res.latitude, - long: res.longitude + res = randomCirclePoint([ this.currentZone.lat, this.currentZone.long], this.currentZone.radius - newRadius); + ok = (isInCircle([res.latitude, res.longitude], this.zoneSettings.min.center, newRadius - this.zoneSettings.min.radius)) } + return [ + res.latitude, + res.longitude + ] } + /** + * Compute the next zone satifying the given settings, update the nextZone and currentStartZone + * Wait for the appropriate duration before starting a new zone reduction if needed + */ setNextZone() { //At this point, nextZone == currentZone, we need to update the next zone, the raidus decrement, and start a timer before the next shrink - //last zone if(this.currentZoneCount == this.zoneSettings.currentZoneCount - 1) { //Copy values - this.nextZone = {center: [...this.zoneSettings.min.center],...this.zoneSettings.min} + this.nextZone =JSON.parse(JSON.stringify(this.zoneSettings.min)) + this.currentStartZone =JSON.parse(JSON.stringify(this.zoneSettings.min)) }else { //TODO : compute next zone this.nextZone.center = this.getRandomNextCenter(this.nextZone.radius * this.shrinkFactor) this.nextZone.radius *= this.shrinkFactor; - this.timeoutId = setTimeout(() => this.startShrinking(), 1000 * 60 * this.zoneSettings.reductionIntervalMinutes) + this.currentStartZone = JSON.parse(JSON.stringify(this.currentZone)) + this.onNextZoneUpdate({ + begin: JSON.parse(JSON.stringify(this.currentStartZone)), + end: JSON.parse(JOSN.stringify()) + }) + this.nextZoneTimeoutId = setTimeout(() => this.startShrinking(), 1000 * 60 * this.zoneSettings.reductionIntervalMinutes) } } + /* + * Start a task that will run periodically updatinng the zone size, and calling the onZoneUpdate callback + * This will also periodically check if the reduction is over or not + * If the reduction is over this function will call setNextZone + */ startShrinking() { - + const startTime = new Date(); + 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.onZoneUpdate(JSON.parse(JSON.stringify(this.currentZone))) + //Zone shrinking is over + if(completed >= 1) { + clearInterval(this.updateIntervalId); + this.updateIntervalId = null; + this.setNextZone(); + return; + } + }, this.zoneSettings.updateIntervalSeconds * 1000); } } \ No newline at end of file