10namespace ComusParty\Controllers;
12use ComusParty\App\EloCalculator;
13use ComusParty\App\Exceptions\GameSettingsException;
14use ComusParty\App\Exceptions\GameUnavailableException;
15use ComusParty\App\Exceptions\MalformedRequestException;
16use ComusParty\App\Exceptions\NotFoundException;
17use ComusParty\App\Exceptions\UnauthorizedAccessException;
18use ComusParty\App\MessageHandler;
19use ComusParty\App\Validator;
20use ComusParty\Models\GameDAO;
21use ComusParty\Models\GameRecord;
22use ComusParty\Models\GameRecordDAO;
23use ComusParty\Models\GameRecordState;
24use ComusParty\Models\GameState;
25use ComusParty\Models\PenaltyDAO;
26use ComusParty\Models\PlayerDAO;
27use DateMalformedStringException;
31use Random\RandomException;
33use Twig\Error\LoaderError;
34use Twig\Error\RuntimeError;
35use Twig\Error\SyntaxError;
36use Twig\Loader\FilesystemLoader;
64 $player = $playerManager->findByUuid($_SESSION[
'uuid']);
65 $_SESSION[
"elo"] = $player->getElo();
66 $_SESSION[
"xp"] = $player->getXp();
67 $_SESSION[
"comusCoin"] = $player->getComusCoin();
68 $_SESSION[
"username"] = $player->getUsername();
69 $this->
getTwig()->addGlobal(
'auth', [
70 'pfpPath' => $_SESSION[
'pfpPath'] ??
null,
71 'loggedIn' => isset($_SESSION[
'uuid']),
72 'loggedUuid' => $_SESSION[
'uuid'] ??
null,
73 'loggedUsername' => $_SESSION[
'username'] ??
null,
74 'loggedComusCoin' => $_SESSION[
'comusCoin'] ??
null,
75 'loggedElo' => $_SESSION[
'elo'] ??
null,
76 'loggedXp' => $_SESSION[
'xp'] ??
null,
77 'role' => $_SESSION[
'role'] ??
null,
78 'firstName' => $_SESSION[
'firstName'] ??
null,
79 'lastName' => $_SESSION[
'lastName'] ??
null,
82 $games = $gameManager->findAllWithTags();
83 $games = array_filter($games, fn($game) => $game->getState() == GameState::AVAILABLE);
84 $template = $this->
getTwig()->load(
'player/home.twig');
85 echo $template->render(array(
101 public function initGame(
string $code, ?array $settings): void
106 if ($gameRecord ==
null) {
110 if ($gameRecord->getState() != GameRecordState::WAITING) {
114 if ($gameRecord->getHostedBy()->getUuid() != $_SESSION[
'uuid']) {
118 $game = $gameRecord->getGame();
120 if ($game->getState() != GameState::AVAILABLE) {
125 if (
sizeof($gameSettings) == 0) {
129 $nbPlayers =
sizeof($gameRecord->getPlayers());
130 if ($nbPlayers < $gameSettings[
"settings"][
"minPlayers"] || $nbPlayers > $gameSettings[
"settings"][
"maxPlayers"]) {
131 throw new GameSettingsException(
"Le nombre de joueurs est de " . $nbPlayers .
" alors que le jeu nécessite entre " . $gameSettings[
"settings"][
"minPlayers"] .
" et " . $gameSettings[
"settings"][
"maxPlayers"] .
" joueurs");
134 if (in_array(
"MODIFIED_SETTING_DATA", $gameSettings[
"neededParametersFromComus"])) {
135 if (
sizeof($settings) !=
sizeof($gameSettings[
"modifiableSettings"])) {
140 foreach ($settings as $key => $value) {
141 if (!array_key_exists($key, $gameSettings[
"modifiableSettings"])) {
145 $neededSetting = $gameSettings[
"modifiableSettings"][$key];
147 if ($neededSetting[
"type"] ==
"select" && !in_array($value, array_map(fn($option) => $option[
"value"], $neededSetting[
"options"]))) {
148 throw new GameSettingsException(
"Le paramètre $key doit être une des valeurs suivantes : " . implode(
", ", $neededSetting[
"values"]));
149 } elseif ($neededSetting[
"type"] ==
"select") {
153 if ($neededSetting[
"type"] ==
"checkbox" && !is_bool($value)) {
155 } elseif ($neededSetting[
"type"] ==
"checkbox") {
161 "type" => $neededSetting[
"type"] ==
"number" ?
"numeric" :
"string",
162 ...(array_key_exists(
"min", $neededSetting) ? [
"min-value" => $neededSetting[
"min"]] : []),
163 ...(array_key_exists(
"max", $neededSetting) ? [
"max-value" => $neededSetting[
"max"]] : []),
164 ...(array_key_exists(
"pattern", $neededSetting) ? [
"format" => $neededSetting[
"pattern"]] : [])
169 if (!$validator->validate($settings)) {
170 throw new GameSettingsException(
"Les paramètres du jeu ne sont pas valides.\r\n" . implode(
"\r\n", array_map(fn(
string $key, array $value) =>
"[$key] " . implode(
", ", $value), array_keys($validator->getErrors()), $validator->getErrors())));
178 $players = $gameRecord->getPlayers();
179 foreach ($players as &$player) {
180 $player[
"token"] = bin2hex(random_bytes(8));
182 $gameRecord->setPlayers($players);
183 (
new GameRecordDAO($this->
getPdo()))->updatePlayers($gameRecord->getCode(), $gameRecord->getPlayers());
185 $token = $gameRecord->generateToken();
189 "code" => $gameRecord->getCode(),
192 if (in_array(
"MODIFIED_SETTING_DATA", $gameSettings[
"neededParametersFromComus"])) {
193 $data[
"settings"] = $settings;
196 if (in_array(
"PLAYER_UUID", $gameSettings[
"neededParametersFromComus"])) {
197 $data[
"players"] = array_map(
function ($player) use ($gameSettings) {
199 'uuid' => $player[
"player"]->getUuid(),
200 ...(in_array(
"PLAYER_NAME", $gameSettings[
"neededParametersFromComus"]) ? [
'username' => $player[
"player"]->getUsername()] : []),
201 ...(in_array(
'PLAYER_STYLE', $gameSettings[
"neededParametersFromComus"]) ? [
'style' => [
202 "profilePicture" => $player[
"player"]->getActivePfp(),
203 "banner" => $player[
"player"]->getActiveBanner(),
205 'token' => $player[
"token"]
207 }, $gameRecord->getPlayers());
210 $ch = curl_init($baseUrl .
"/" . $gameRecord->getCode() .
"/init");
211 curl_setopt($ch, CURLOPT_POST,
true);
212 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
213 curl_setopt($ch, CURLOPT_RETURNTRANSFER,
true);
214 curl_setopt($ch, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
215 curl_setopt($ch, CURLOPT_HTTPHEADER, [
216 "Content-Type: application/json",
220 $response = curl_exec($ch);
223 if (curl_errno($ch)) {
224 MessageHandler::sendJsonCustomException(500, curl_error($ch));
226 if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200) {
227 MessageHandler::sendJsonCustomException(curl_getinfo($ch, CURLINFO_HTTP_CODE),
"Erreur lors de l'initialisation du jeu");
230 $response = json_decode($response,
true);
231 if (!$response[
"success"]) {
232 MessageHandler::sendJsonCustomException(
233 array_key_exists(
"code", $response) ? $response[
"code"] : 500,
234 "Erreur lors de l'initialisation du jeu." . (array_key_exists(
"message", $response) ?
" " . $response[
"message"] :
"")
242 $gameRecord->setState(GameRecordState::STARTED);
243 $gameRecord->setUpdatedAt(
new DateTime());
246 echo MessageHandler::sendJsonMessage(
"La partie a bien été initialisée", [
249 "gameId" => $game->getId(),
253 }
catch (Exception|Error $e) {
254 MessageHandler::sendJsonException($e);
268 $settingsFile =
"$gameFolder/settings.json";
269 if (!file_exists($settingsFile)) {
270 throw new GameUnavailableException(
"Le fichier $settingsFile n'existe pas. Impossible de récupérer les informations du jeu.");
273 return json_decode(file_get_contents($settingsFile),
true);
284 return realpath(__DIR__ .
"/../..") .
"/games/game$id";
290 if ($gameSettings[
"settings"][
"serveByComus"]) {
291 return "https://games.comus-party.com/game" . $id;
293 return $gameSettings[
"settings"][
"serverAddress"] .
":" . $gameSettings[
"settings"][
"serverPort"];
305 $game = $gameManager->findWithDetailsById($id);
306 echo MessageHandler::sendJsonMessage(
"Informations du jeu récupérées", [
308 "id" => $game->getId(),
309 "name" => $game->getName(),
310 "img" => $game->getPathImg(),
311 "description" => $game->getDescription(),
312 "tags" => $game->getTags(),
331 if ($gameRecord ==
null || $gameRecord->getGame()->getState() != GameState::AVAILABLE) {
335 if (is_null($gameRecord->getPlayers())) {
340 if ($gameRecord->getState() == GameRecordState::WAITING) {
342 }
else if ($gameRecord->getState() == GameRecordState::STARTED) {
345 throw new Exception(
"Cette partie est terminée", 404);
363 if (!in_array((
new PlayerDAO($this->
getPdo()))->findByUuid($_SESSION[
'uuid']), array_map(fn($player) => $player[
'player'], $gameRecord->
getPlayers()))) {
369 if (in_array(
"MODIFIED_SETTING_DATA", $gameSettings[
"neededParametersFromComus"])) {
375 $template = $this->
getTwig()->load(
'player/game-settings.twig');
376 echo $template->render([
377 "code" => $gameRecord->
getCode(),
378 "isHost" => $gameRecord->
getHostedBy()->getUuid() == $_SESSION[
'uuid'],
379 "players" => array_map(fn($player) => $player[
'player'], $gameRecord->
getPlayers()),
380 "game" => $gameRecord->
getGame(),
381 "chat" => $gameSettings[
"settings"][
"allowChat"],
382 "gameFileInfos" => $gameSettings[
"game"],
383 "settings" => $settings,
397 if ($method ==
'POST') {
399 echo $this->
joinGame($code, $_SESSION[
'uuid']);
400 }
catch (Exception $e) {
401 MessageHandler::sendJsonException($e);
403 } elseif ($method ==
'GET') {
404 $this->
joinGame($code, $_SESSION[
'uuid']);
417 private function joinGame(
string $code, ?
string $playerUuid =
null): string
420 $gameRecord = $gameRecordManager->findByCode($code);
422 if ($gameRecord ==
null) {
426 if ($this->
getGameSettings($gameRecord->getGame()->getId())[
'settings'][
'maxPlayers'] <=
sizeof($gameRecord->getPlayers())) {
434 if ($gameRecord->getState() != GameRecordState::WAITING) {
438 if (!in_array($playerUuid, array_map(fn($player) => $player[
'player']->getUuid(), $gameRecord->getPlayers()))) {
439 $gameRecordManager->addPlayer($gameRecord, $player);
446 "gameId" => $gameRecord->getGame()->getId(),
459 return $allSettings[
"modifiableSettings"];
474 if (!in_array($_SESSION[
'uuid'], array_map(fn($player) => $player[
"player"]->getUuid(), $players))) {
480 foreach ($players as $player) {
481 if ($player[
"player"]->getUuid() == $_SESSION[
'uuid']) {
482 $token = $player[
"token"];
486 $template = $this->
getTwig()->load(
'player/in-game.twig');
487 echo $template->render([
488 "code" => $gameRecord->
getCode(),
489 "isHost" => $gameRecord->
getHostedBy()->getUuid() == $_SESSION[
'uuid'],
491 "game" => $gameRecord->
getGame(),
492 "chat" => $this->getGameSettings($gameRecord->
getGame()->getId())[
"settings"][
"allowChat"],
493 "iframe" => $baseUrl .
"/" . $gameRecord->
getCode() .
"/" . $token
507 $gameRecord = $gameRecordManager->findByCode($code);
509 if ($gameRecord ==
null) {
513 if ($gameRecord->getHostedBy()->getUuid() != $_SESSION[
'uuid']) {
517 $gameRecord->setPrivate($isPrivate);
518 $gameRecordManager->update($gameRecord);
520 echo MessageHandler::sendJsonMessage(
"La visibilité de la partie a bien été modifiée");
522 }
catch (Exception|Error $e) {
523 MessageHandler::sendJsonException($e);
538 if ($game->getState() != GameState::AVAILABLE) {
543 $gameRecords = $gameRecordManager->findByGameIdAndState($gameId, GameRecordState::WAITING);
546 foreach ($gameRecords as $gameRecord) {
547 if ($gameRecord->getState() == GameRecordState::WAITING && !$gameRecord->isPrivate()) {
548 $players = $gameRecord->getPlayers();
553 if (is_null($players)) {
557 foreach ($players as $player) {
558 $totalElo += $player[
'player']->getElo();
562 $eloForGame[$gameRecord->getCode()] = $totalElo / $nbPlayers;
566 if (
sizeof($eloForGame) == 0) {
570 $playerElo = (
new PlayerDAO($this->
getPdo()))->findByUuid($_SESSION[
'uuid'])->getElo();
573 foreach ($eloForGame as $gameCode => $gameElo) {
574 if ($bestGame ==
null || abs($gameElo - $playerElo) < abs($eloForGame[$bestGame] - $playerElo)) {
575 $bestGame = $gameCode;
579 echo $this->
joinGame($bestGame, $_SESSION[
'uuid']);
581 }
catch (Exception $e) {
582 MessageHandler::sendJsonException($e);
594 public function quitGame(
string $code,
string $playerUuid): void
597 $gameRecord = $gameRecordManager->findByCode($code);
599 if ($gameRecord ==
null) {
603 $gameRecordManager->removePlayer($code, $playerUuid);
605 if ($gameRecord->getHostedBy()->getUuid() == $playerUuid) {
606 if (
sizeof($gameRecord->getPlayers()) > 1) {
607 $gameRecord->setHostedBy($gameRecord->getPlayers()[0][
"player"]);
608 $gameRecordManager->update($gameRecord);
610 $gameRecordManager->delete($code);
614 echo MessageHandler::sendJsonMessage(
"Vous avez bien quitté la partie");
630 if ($game->getState() != GameState::AVAILABLE) {
634 $host = (
new PlayerDAO($this->
getPdo()))->findByUuid($_SESSION[
'uuid']);
635 $generatedCode = bin2hex(random_bytes(8));
642 GameRecordState::WAITING,
651 $gameRecordManager->insert($gameRecord);
652 $gameRecordManager->addPlayer($gameRecord, $host);
654 echo MessageHandler::sendJsonMessage(
"La partie a bien été créée", [
656 "code" => $generatedCode,
670 public function endGame(
string $code,
string $token, ?array $results =
null): void
675 $gameRecord = $gameRecordManager->findByCode($code);
677 if ($gameRecord ==
null) {
681 if ($gameRecord->getState() != GameRecordState::STARTED) {
685 if ($gameRecord->getToken() != hash(
"sha256", $token)) {
689 if (!empty($results)) {
690 $actualPlayerTokenInRecord = [];
691 foreach ($gameRecord->getPlayers() as $player) {
692 $actualPlayerTokenInRecord[$player[
"player"]->getUuid()] = $player[
"token"];
695 $gameSettings = $this->
getGameSettings($gameRecord->getGame()->getId());
697 foreach ($results as $playerUuid => $playerData) {
698 if (!isset($actualPlayerTokenInRecord[$playerUuid]) || $actualPlayerTokenInRecord[$playerUuid] !== $playerData[
"token"]) {
706 foreach ($results as $playerUuid => $playerData) {
707 if (in_array(
"SCORES", $gameSettings[
"returnParametersToComus"])) {
711 if (in_array(
"WINNERS", $gameSettings[
"returnParametersToComus"])) {
712 if (!isset($playerData[
"winner"])) {
717 $player = $playerManager->findByUuid($playerUuid);
718 if ($playerData[
"winner"]) {
719 $allWinner[] = $player;
720 $gameRecordManager->addWinner($code, $playerUuid);
722 $allLooser[] = $player;
724 $allPlayers[] = $player;
728 if (!$gameRecord->isPrivate() && in_array(
"WINNERS", $gameSettings[
"returnParametersToComus"])) {
733 $gameRecord->setState(GameRecordState::FINISHED);
734 $gameRecord->setFinishedAt(
new DateTime());
735 $gameRecordManager->update($gameRecord);
737 echo MessageHandler::sendJsonMessage(
"La partie a bien été terminée");
739 }
catch (Exception|Error $e) {
740 MessageHandler::sendJsonException($e);
753 $averageEloLooser = $this->
averageElo($looser);
754 $averageEloWinner = $this->
averageElo($winners);
757 foreach ($allPlayers as $player) {
758 $elo = $player->getElo();
759 if (
sizeof($winners) == 0) {
761 }
else if (in_array($player, $winners)) {
768 $player->setElo(round($newElo));
769 $playerManager->update($player);
780 foreach ($players as $player) {
781 $averageElo += $player->getElo();
783 return $averageElo /
sizeof($players);
795 $player = $playerManager->findByUsername($playerUsername);
798 $penalty = $penaltyManager->findLastMutedByPlayerUuid($player->getUuid());
805 if (isset($penalty)) {
806 $endDate = $penalty->getCreatedAt()->modify(
"+" . $penalty->getDuration() .
"hour");
807 if ($endDate >
new DateTime()) {
808 $response[
'muted'] =
true;
809 echo MessageHandler::sendJsonMessage(
"Le joueur est encore mute", $response);
814 echo MessageHandler::sendJsonMessage(
"Le joueur n'est pas mute", $response);
static calculateNewElo(int $eloPlayer, float $averageElo, float $result)
Calcule le nouvel Elo du joueur en fonction de son Elo actuel, de l'Elo moyen des joueurs de la parti...
Classe GameSettingsException.
Classe GameUnavailableException.
Classe NotFoundException.
Classe UnauthorizedAccessException.
showHomePage()
Affiche la page d'accueil avec la liste des jeux.
joinGameWithCode(string $method, string $code)
Rejoint une partie avec un code (Méthode GET ou POST autorisée)
averageElo(array $players)
initGame(string $code, ?array $settings)
Permet d'initialiser le jeu après validation des paramètres et que les joueurs soient prêts.
showGameSettings(GameRecord $gameRecord)
Affiche la page des paramètres de la partie.
getGameSettings(int $id)
Récupère les paramètres du jeu dont l'ID est passé en paramètre.
endGame(string $code, string $token, ?array $results=null)
Termine une partie et met à jour les scores et les gagnants.
showInGame(GameRecord $gameRecord)
Affiche la page de la partie en cours.
showGame(string $code)
Affiche la page de la partie dont le code est passé en paramètre.
joinGameFromSearch(int $gameId)
Rejoint une partie suite à une recherche de partie.
__construct(FilesystemLoader $loader, Environment $twig)
Constructeur de la classe ControllerGame.
createGame(int $gameId)
Crée une partie en base de données pour un jeu donné
isPlayerMuted(string $playerUsername)
Vérifie si un joueur est mute.
getGameInformations(?int $id)
Récupère les informations d'un jeu et le retourne au format JSON.
joinGame(string $code, ?string $playerUuid=null)
Rejoint une partie avec un code.
changeVisibility(string $code, bool $isPrivate)
Permet de changer la visibilité d'une partie.
getGameModifiableSettings(int $id)
Récupère les paramètres modifiables du jeu dont l'ID est passé en paramètre.
calculateAndUpdateElo(array $allPlayers, array $winners, array $looser)
Calcule et met à jour les scores Elo des joueurs.
quitGame(string $code, string $playerUuid)
Quitte une partie.
getGameFolder(int $id)
Récupère le dossier du jeu dont l'ID est passé en paramètre.
getTwig()
Retourne l'attribut twig, correspondant à l'environnement de Twig.
getPdo()
Retourne l'attribut PDO, correspondant à la connexion à la base de données.
isPrivate()
Getter de l'attribut isPrivate.
getGame()
Getter de l'attribut game.
getPlayers()
Getter de l'attribut players.
getCode()
Getter de l'attribut uuid.
getHostedBy()
Getter de l'attribut hostedBy.