premier test implémentation d'un composant calculant la zone

This commit is contained in:
2024-04-16 21:35:56 +00:00
parent 0ad4032a9b
commit 40241311ee
4 changed files with 122 additions and 24 deletions

View File

@@ -1,4 +1,5 @@
import { isInCircle } from "./map_utils.js"; import { isInCircle } from "./map_utils.js";
import { ZoneManager } from "./zoneManager.js";
const GameState = { const GameState = {
SETUP: "setup", SETUP: "setup",
@@ -8,10 +9,10 @@ const GameState = {
} }
export default class Game { export default class Game {
constructor() { constructor(onUpdateZone,onUpdateNewZone) {
this.teams = []; this.teams = [];
this.state = GameState.SETUP; this.state = GameState.SETUP;
this.zone = new ZoneManager(null, null) this.zone = new ZoneManager(onUpdateZone, onUpdateNewZone)
} }
setState(newState) { setState(newState) {
@@ -21,6 +22,11 @@ export default class Game {
//The game has started //The game has started
if(newState == GameState.PLAYING) { if(newState == GameState.PLAYING) {
this.initLastSentLocations(); this.initLastSentLocations();
this.zone.reset()
this.zone.start()
}
if(newState != GameState.PLAYING) {
this.zone.reset();
} }
this.state = newState; this.state = newState;
return true; return true;
@@ -152,9 +158,15 @@ export default class Game {
return false; 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 //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; return false;
} }
this.zone.udpateSettings(newSettings) this.zone.udpateSettings(newSettings)

View File

@@ -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} event The event name
* @param {String} data The data to send * @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) { function teamBroadcast(teamId, event, data) {
for (let socketId of game.getTeam(teamId).sockets) { for (let socketId of game.getTeam(teamId).sockets) {
io.of("player").to(socketId).emit(event, data) 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) { function playersBroadcast(event, data) {
for (let team of game.teams) { for (let team of game.teams) {
teamBroadcast(team.id, event, data); 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) { function logoutPlayer(id) {
for (let team of game.teams) { for (let team of game.teams) {
team.sockets = team.sockets.filter((sid) => sid != id); 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 //Array of logged in sockets
let loggedInSockets = []; 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) { if (!loggedIn) {
socket.emit("error", "Not logged in"); socket.emit("error", "Not logged in");
return; return;
} }
if(!game.setZone(zone)) { if(!game.setZoneSettings(zone)) {
socket.emit("error", "Error changing zone"); socket.emit("error", "Error changing zone");
} }
}) })
//User is attempting to add a new team //User is attempting to add a new team

13
traque-back/util.js Normal file
View File

@@ -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;
}

View File

@@ -1,7 +1,8 @@
import randomLocation, { randomCirclePoint } from 'random-location' import randomLocation, { randomCirclePoint } from 'random-location'
import { isInCircle } from './map_utils'; import { isInCircle } from './map_utils';
import { map } from './util';
class ZoneManager { export class ZoneManager {
constructor(onZoneUpdate, onNextZoneUpdate) { constructor(onZoneUpdate, onNextZoneUpdate) {
//Setings storing where the zone will start, end and how it should evolve //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 //The zone will start by staying at its mzx value for reductionInterval minutes
@@ -19,17 +20,20 @@ class ZoneManager {
} }
this.shrinkFactor = null; this.shrinkFactor = null;
//Live location of the zone //Live location of the zone
this.currentZone = null; this.currentZone = {center: null, radius: null};
this.currentDecrement = null;
//If the zone is shrinking, this is the target of the current shrinking //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 //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.startDate = null;
this.zoneSettings = zoneSettings; this.zoneSettings = zoneSettings;
this.started = false; this.started = false;
this.timeoutId = null; this.updateIntervalId = null;
this.nextZoneTimeoutId = null;
this.onZoneUpdate = onZoneUpdate; this.onZoneUpdate = onZoneUpdate;
this.onNextZoneUpdate = onNextZoneUpdate 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) 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() { reset() {
this.currentZoneCount = 0; 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() { start() {
this.started = true; this.started = true;
this.startDate = new Date(); 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) { getRandomNextCenter(newRadius) {
let ok = false; let ok = false;
let res = null let res = null
//take a random point satisfying both conditions
while(!ok) { while(!ok) {
res = randomCirclePoint({latitude: this.currentZone.lat, longitude: this.currentZone.long}, this.currentZone.radius - newRadius); res = randomCirclePoint([ this.currentZone.lat, this.currentZone.long], this.currentZone.radius - newRadius);
ok = (isInCircle({lat: res.latitude, long: res.longitude}, this.zoneSettings.min.center, newRadius - this.zoneSettings.min.radius)) ok = (isInCircle([res.latitude, res.longitude], this.zoneSettings.min.center, newRadius - this.zoneSettings.min.radius))
}
return {
lat: res.latitude,
long: res.longitude
} }
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() { setNextZone() {
//At this point, nextZone == currentZone, we need to update the next zone, the raidus decrement, and start a timer before the next shrink //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 //last zone
if(this.currentZoneCount == this.zoneSettings.currentZoneCount - 1) { if(this.currentZoneCount == this.zoneSettings.currentZoneCount - 1) {
//Copy values //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 { }else {
//TODO : compute next zone //TODO : compute next zone
this.nextZone.center = this.getRandomNextCenter(this.nextZone.radius * this.shrinkFactor) this.nextZone.center = this.getRandomNextCenter(this.nextZone.radius * this.shrinkFactor)
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() { 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);
} }
} }