Refactored code to remove useless classes, fix #17

This commit is contained in:
2024-06-10 18:06:00 +00:00
parent 4f9608d25d
commit 21e6f31c15
7 changed files with 137 additions and 146 deletions

View File

@@ -1,4 +1,7 @@
import { io, game, penaltyController } from "./index.js";
import { io } from "./index.js";
import game from "./game.js"
import zone from "./zone_manager.js"
import penaltyController from "./penalty_controller.js";
import { playersBroadcast, sendUpdatedTeamInformations } from "./team_socket.js";
import { config } from "dotenv";
@@ -47,11 +50,11 @@ export function initAdminSocketHandler() {
//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("zone_settings", zone.zoneSettings)
socket.emit("zone", zone.currentZone)
socket.emit("new_zone", {
begin: game.zone.currentStartZone,
end: game.zone.nextZone
begin: zone.currentStartZone,
end: zone.nextZone
})
} else {
@@ -79,9 +82,9 @@ export function initAdminSocketHandler() {
}
if (!game.setZoneSettings(settings)) {
socket.emit("error", "Error changing zone");
socket.emit("zone_settings", game.zone.zoneSettings) //Still broadcast the old config to the client who submited an incorrect config to keep the client up to date
socket.emit("zone_settings", zone.zoneSettings) //Still broadcast the old config to the client who submited an incorrect config to keep the client up to date
} else {
secureAdminBroadcast("zone_settings", game.zone.zoneSettings)
secureAdminBroadcast("zone_settings", zone.zoneSettings)
}
})

View File

@@ -1,8 +1,9 @@
import { secureAdminBroadcast } from "./admin_socket.js";
import { penaltyController } from "./index.js";
import { isInCircle } from "./map_utils.js";
import { playersBroadcast, sendUpdatedTeamInformations } from "./team_socket.js";
import { ZoneManager } from "./zone_manager.js";
import penaltyController from "./penalty_controller.js";
import zoneManager from "./zone_manager.js";
export const GameState = {
SETUP: "setup",
@@ -11,23 +12,20 @@ export const GameState = {
FINISHED: "finished"
}
export default class Game {
constructor(onUpdateZone, onUpdateNewZone) {
this.teams = [];
this.state = GameState.SETUP;
this.zone = new ZoneManager(onUpdateZone, onUpdateNewZone)
this.settings = {
loserEndGameMessage: "",
winnerEndGameMessage: "",
capturedMessage: "",
waitingMessage: "Jeu en préparation, veuillez patienter."
}
}
export default {
teams : [],
state : GameState.SETUP,
settings : {
loserEndGameMessage: "",
winnerEndGameMessage: "",
capturedMessage: "",
waitingMessage: "Jeu en préparation, veuillez patienter."
},
changeSettings(newSettings) {
this.settings = {...this.settings, ...newSettings};
return true;
}
},
setState(newState) {
if (Object.values(GameState).indexOf(newState) == -1) {
@@ -36,19 +34,19 @@ export default class Game {
//The game has started
if (newState == GameState.PLAYING) {
penaltyController.start();
if (!this.zone.ready()) {
if (!zoneManager.ready()) {
return false;
}
this.initLastSentLocations();
this.zone.reset()
zoneManager.reset()
//If the zone cannot be setup, reset everything
if (!this.zone.start()) {
if (!zoneManager.start()) {
this.setState(GameState.SETUP);
return;
}
}
if (newState != GameState.PLAYING) {
this.zone.reset();
zoneManager.reset();
penaltyController.stop();
}
//Game reset
@@ -65,7 +63,7 @@ export default class Game {
}
this.state = newState;
return true;
}
},
getNewTeamId() {
let id = null;
@@ -73,11 +71,11 @@ export default class Game {
id = Math.floor(Math.random() * 1_000_000);
}
return id;
}
},
createCaptureCode() {
return Math.floor(Math.random() * 10000)
}
},
addTeam(teamName) {
let id = this.getNewTeamId();
@@ -100,7 +98,7 @@ export default class Game {
});
this.updateTeamChasing();
return true;
}
},
playingTeamCount() {
let res = 0;
@@ -110,7 +108,7 @@ export default class Game {
}
})
return res;
}
},
updateTeamChasing() {
if (this.playingTeamCount() <= 2) {
@@ -138,16 +136,16 @@ export default class Game {
this.getTeam(previousTeam).enemyName = this.getTeam(firstTeam).name;
secureAdminBroadcast("teams", this.teams)
return true;
}
},
reorderTeams(newOrder) {
this.teams = newOrder;
return this.updateTeamChasing();
}
},
getTeam(teamId) {
return this.teams.find(t => t.id === teamId);
}
},
updateTeam(teamId, newTeam) {
this.teams = this.teams.map((t) => {
@@ -160,7 +158,7 @@ export default class Game {
this.updateTeamChasing();
penaltyController.checkPenalties();
return true;
}
},
updateLocation(teamId, location) {
let team = this.getTeam(teamId);
@@ -176,7 +174,7 @@ export default class Game {
team.ready = isInCircle({ lat: location[0], lng: location[1] }, team.startingArea.center, team.startingArea.radius)
}
return true;
}
},
//Make it so that when a team requests the location of a team that has never sent their locaiton
//Their position at the begining of the game is sent
@@ -186,7 +184,7 @@ export default class Game {
team.locationSendDeadline = Number(new Date()) + penaltyController.settings.allowedTimeBetweenPositionUpdate * 60 * 1000;
sendUpdatedTeamInformations(team.id);
}
}
},
sendLocation(teamId) {
let team = this.getTeam(teamId);
@@ -203,7 +201,7 @@ export default class Game {
team.enemyLocation = this.getTeam(team.chasing).lastSentLocation;
}
return team;
}
},
removeTeam(teamId) {
if (this.getTeam(teamId) == undefined) {
@@ -213,7 +211,7 @@ export default class Game {
this.teams = this.teams.filter(t => t.id !== teamId);
this.updateTeamChasing();
return true;
}
},
/**
* Request a capture initiated by the team with id teamId (the one trying to capture)
@@ -231,7 +229,7 @@ export default class Game {
return true;
}
return false;
}
},
/**
* Set a team to captured and update the chase chain
@@ -240,7 +238,7 @@ export default class Game {
capture(teamId) {
this.getTeam(teamId).captured = true
this.updateTeamChasing();
}
},
/**
* Change the settings of the Zone manager
@@ -253,12 +251,12 @@ export default class Game {
if (this.state == GameState.PLAYING || this.state == GameState.FINISHED) {
return false;
}
return this.zone.udpateSettings(newSettings)
}
return zoneManager.udpateSettings(newSettings)
},
finishGame() {
this.setState(GameState.FINISHED);
this.zone.reset();
zoneManager.reset();
playersBroadcast("game_state", this.state);
}
},
}

View File

@@ -2,12 +2,10 @@ import { createServer } from "https";
import express from "express";
import { Server } from "socket.io";
import Game from "./game.js";
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";
import { initAdminSocketHandler } from "./admin_socket.js";
import { initTeamSocket } from "./team_socket.js";
import { initPhotoUpload } from "./photo.js";
//extract admin password from .env file
config();
@@ -35,22 +33,6 @@ export const io = new Server(httpsServer, {
}
});
//Zone update broadcast function, called by the game object
function onUpdateNewZone(newZone) {
playersBroadcast("new_zone", newZone)
secureAdminBroadcast("new_zone", newZone)
}
function onUpdateZone(zone) {
playersBroadcast("zone", zone)
secureAdminBroadcast("zone", zone)
}
export const game = new Game(onUpdateZone, onUpdateNewZone);
export const penaltyController = new PenaltyController();
initAdminSocketHandler();
initTeamSocket();
initPhotoUpload();

View File

@@ -1,21 +1,18 @@
import { getDistanceFromLatLon, isInCircle } from "./map_utils.js";
import { 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";
import game, {GameState} from "./game.js";
import zone from "./zone_manager.js";
export class PenaltyController {
constructor() {
export default {
outOfBoundsSince: {},
checkIntervalId: null,
settings: {
allowedTimeOutOfZone: 10,
allowedTimeBetweenPositionUpdate: 10,
//Number of penalties needed to be eliminated
this.game = game;
this.outOfBoundsSince = {};
this.checkIntervalId = null;
this.settings = {
allowedTimeOutOfZone: 10,
allowedTimeBetweenPositionUpdate: 10,
maxPenalties: 3
}
}
maxPenalties: 3
},
start() {
this.outOfBoundsSince = {};
@@ -24,12 +21,12 @@ export class PenaltyController {
}
//Watch periodically if all teams need are following the rules
this.checkIntervalId = setInterval(() => {
if (this.game.state == GameState.PLAYING) {
if (game.state == GameState.PLAYING) {
this.watchPositionUpdate();
this.watchZone();
}
}, 100);
}
},
stop() {
this.outOfBoundsSince = {};
@@ -37,7 +34,7 @@ export class PenaltyController {
clearInterval(this.checkIntervalId)
this.checkIntervalId = null;
}
}
},
updateSettings(newSettings) {
//Sanitize input
@@ -47,14 +44,14 @@ export class PenaltyController {
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);
let team = game.getTeam(teamId);
if (!team) {
return;
}
@@ -63,7 +60,7 @@ export class PenaltyController {
}
team.penalties++;
if (team.penalties >= this.settings.maxPenalties) {
this.game.capture(team.id);
game.capture(team.id);
sendUpdatedTeamInformations(teamId);
sendUpdatedTeamInformations(team.chased);
teamBroadcast(teamId, "warning", "You have been eliminated (reason: too many penalties)")
@@ -72,29 +69,29 @@ export class PenaltyController {
teamBroadcast(teamId, "warning", `You recieved a penalty (${team.penalties}/${this.settings.maxPenalties})`)
sendUpdatedTeamInformations(teamId);
}
secureAdminBroadcast("teams", this.game.teams)
}
secureAdminBroadcast("teams", game.teams)
},
checkPenalties() {
for (let team of game.teams) {
if (team.penalties >= this.settings.maxPenalties) {
this.game.capture(team.id);
game.capture(team.id);
sendUpdatedTeamInformations(team.id);
sendUpdatedTeamInformations(team.chased);
teamBroadcast(team.id, "warning", "You have been eliminated (reason: too many penalties)")
teamBroadcast(team.chased, "success", "The team you were chasing has been eliminated")
}
}
}
},
watchZone() {
this.game.teams.forEach((team) => {
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 || zone.currentZone == null) {
return;
}
if (!isInCircle({ lat: team.currentLocation[0], lng: team.currentLocation[1] }, this.game.zone.currentZone.center, this.game.zone.currentZone.radius)) {
if (!isInCircle({ lat: team.currentLocation[0], lng: team.currentLocation[1] }, zone.currentZone.center, zone.currentZone.radius)) {
//The team was not previously out of the zone
if (!this.outOfBoundsSince[team.id]) {
this.outOfBoundsSince[team.id] = new Date();
@@ -111,11 +108,11 @@ export class PenaltyController {
}
}
})
}
},
watchPositionUpdate() {
this.game.teams.forEach((team) => {
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) {
@@ -124,12 +121,12 @@ export class PenaltyController {
}
if (new Date() > team.locationSendDeadline) {
this.addPenalty(team.id);
this.game.sendLocation(team.id);
game.sendLocation(team.id);
sendUpdatedTeamInformations(team.id);
secureAdminBroadcast("teams", this.game.teams)
}else if(Math.abs(new Date() - team.locationSendDeadline - 60 * 1000) < 100) {
secureAdminBroadcast("teams", 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.`)
}
})
}
},
}

View File

@@ -1,7 +1,8 @@
import { app, game } from "./index.js";
import { app } from "./index.js";
import multer from "multer";
import fs from "fs";
import path from "path";
import game from "./game.js";
const UPLOAD_DIR = "uploads/"
const ALLOWED_MIME = [
"image/png",

View File

@@ -1,5 +1,7 @@
import { secureAdminBroadcast } from "./admin_socket.js";
import { io, game } from "./index.js";
import { io} from "./index.js";
import game from "./game.js";
import zone from "./zone_manager.js";
/**
* Send a socket message to all the players of a team
@@ -79,10 +81,10 @@ export function initTeamSocket() {
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("zone", zone.currentZone)
socket.emit("new_zone", {
begin: game.zone.currentStartZone,
end: game.zone.nextZone
begin: zone.currentStartZone,
end: zone.nextZone
})
});

View File

@@ -1,42 +1,40 @@
import { randomCirclePoint } from 'random-location'
import { isInCircle } from './map_utils.js';
import { map } from './util.js';
import { playersBroadcast } from './team_socket.js';
import { secureAdminBroadcast } from './admin_socket.js';
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
//and then reduce during reductionDuration minutes, then wait again...
//The reduction factor is such that after reductionCount the zone will be the min zone
//a call to onZoneUpdate will be made every updateIntervalSeconds when the zone is changing
//a call to onNextZoneUpdate will be made when the zone reduction ends and a new next zone is announced
this.zoneSettings = {
min: { center: null, radius: null },
max: { center: null, radius: null },
reductionCount: 2,
reductionDuration: 1,
reductionInterval: 1,
updateIntervalSeconds: 1,
}
this.nextZoneDecrement = null;
//Live location of the zone
this.currentZone = { center: null, radius: null };
export default {
//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
//and then reduce during reductionDuration minutes, then wait again...
//The reduction factor is such that after reductionCount the zone will be the min zone
//a call to onZoneUpdate will be made every updateIntervalSeconds when the zone is changing
//a call to onNextZoneUpdate will be made when the zone reduction ends and a new next zone is announced
zoneSettings: {
min: { center: null, radius: null },
max: { center: null, radius: null },
reductionCount: 2,
reductionDuration: 1,
reductionInterval: 1,
updateIntervalSeconds: 1,
},
//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 = { center: null, radius: null };
nextZoneDecrement: null,
//Live location of the zone
currentZone: { center: null, radius: null },
//Zone at the begining of the shrinking
this.currentStartZone = { 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
nextZone: { center: null, radius: null },
this.startDate = null;
this.started = false;
this.updateIntervalId = null;
this.nextZoneTimeoutId = null;
//Zone at the begining of the shrinking
currentStartZone: { center: null, radius: null },
this.onZoneUpdate = onZoneUpdate;
this.onNextZoneUpdate = onNextZoneUpdate
}
startDate: null,
started: false,
updateIntervalId: null,
nextZoneTimeoutId: null,
/**
* Test if a given configuration object is valid, i.e if all needed values are well defined
@@ -51,14 +49,14 @@ export class ZoneManager {
if (settings.max && (typeof settings.max.radius != "number" || typeof settings.max.center.lat != "number" || typeof settings.max.center.lng != "number")) { return false }
if (settings.min && (typeof settings.min.radius != "number" || typeof settings.min.center.lat != "number" || typeof settings.min.center.lng != "number")) { return false }
return true;
}
},
/**
* Test if the zone manager is ready to start
* @returns true if the zone manager is ready to be started, false otherwise
*/
ready() {
return this.validateSettings(this.zoneSettings);
}
},
/**
* Update the settings of the zone, this can be done by passing an object containing the settings to change.
@@ -80,7 +78,7 @@ export class ZoneManager {
this.zoneSettings = { ...this.zoneSettings, ...newSettings };
this.nextZoneDecrement = (this.zoneSettings.max.radius - this.zoneSettings.min.radius) / this.zoneSettings.reductionCount;
return true;
}
},
/**
* Reinitialize the object and stop all the tasks
@@ -96,7 +94,7 @@ export class ZoneManager {
clearTimeout(this.nextZoneTimeoutId);
this.nextZoneTimeoutId = null;
}
}
},
/**
* Start the zone reduction sequence
@@ -110,7 +108,7 @@ export class ZoneManager {
this.currentZone = JSON.parse(JSON.stringify(this.zoneSettings.max));
return this.setNextZone();
}
},
/**
* Get the center of the next zone, this center need to satisfy two properties
@@ -129,14 +127,14 @@ export class ZoneManager {
res = randomCirclePoint({ latitude: this.currentZone.center.lat, longitude: this.currentZone.center.lng }, this.currentZone.radius - newRadius);
ok = (isInCircle({ lat: res.latitude, lng: res.longitude }, this.zoneSettings.min.center, newRadius - this.zoneSettings.min.radius))
}
if(tries>=MAX_TRIES) {
if (tries >= MAX_TRIES) {
return false;
}
return {
lat: res.latitude,
lng: res.longitude
}
}
},
/**
* Compute the next zone satifying the given settings, update the nextZone and currentStartZone
@@ -156,7 +154,7 @@ export class ZoneManager {
} else if (this.currentZoneCount < this.zoneSettings.reductionCount) {
this.nextZone.center = this.getRandomNextCenter(this.nextZone.radius - this.nextZoneDecrement)
//Next center cannot be found
if(this.nextZone.center === false) {
if (this.nextZone.center === false) {
console.log("no center")
return false;
}
@@ -171,7 +169,7 @@ export class ZoneManager {
end: JSON.parse(JSON.stringify(this.nextZone))
})
return true;
}
},
/*
* Start a task that will run periodically updatinng the zone size, and calling the onZoneUpdate callback
@@ -183,8 +181,8 @@ export class ZoneManager {
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.currentZone.center.lat = map(completed,0,1, this.currentStartZone.center.lat, this.nextZone.center.lat)
this.currentZone.center.lng = map(completed,0,1, this.currentStartZone.center.lng, this.nextZone.center.lng)
this.currentZone.center.lat = map(completed, 0, 1, this.currentStartZone.center.lat, this.nextZone.center.lat)
this.currentZone.center.lng = map(completed, 0, 1, this.currentStartZone.center.lng, this.nextZone.center.lng)
this.onZoneUpdate(JSON.parse(JSON.stringify(this.currentZone)))
//Zone shrinking is over
if (completed >= 1) {
@@ -194,6 +192,16 @@ export class ZoneManager {
return;
}
}, this.zoneSettings.updateIntervalSeconds * 1000);
}
},
onNextZoneUpdate(newZone) {
playersBroadcast("new_zone", newZone)
secureAdminBroadcast("new_zone", newZone)
},
onZoneUpdate(zone) {
playersBroadcast("zone", zone)
secureAdminBroadcast("zone", zone)
},
}