From 313a3568aa67e37df2be0f9db42ef1175121044a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Mon, 10 May 2021 21:22:07 +0200 Subject: [PATCH 01/41] MAPG-235 extended database structure for challenges --- .../structure/20210510_2000_challenge.sql | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 database/migrations/structure/20210510_2000_challenge.sql diff --git a/database/migrations/structure/20210510_2000_challenge.sql b/database/migrations/structure/20210510_2000_challenge.sql new file mode 100644 index 0000000..ebaa3f4 --- /dev/null +++ b/database/migrations/structure/20210510_2000_challenge.sql @@ -0,0 +1,50 @@ +CREATE TABLE `challenges` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `token` int(10) unsigned NOT NULL, + `timer_sec` int(10) unsigned, + `no_move` tinyint(1) NOT NULL DEFAULT 0, + `no_pan` tinyint(1) NOT NULL DEFAULT 0, + `no_zoom` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +CREATE TABLE `user_in_challenge` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL, + `challenge_id` int(10) unsigned NOT NULL, + `round` smallint(5) signed NOT NULL DEFAULT -1, + `score` int(10) unsigned NOT NULL DEFAULT 0, + `time_left` int(10) unsigned, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `challenge_id` (`challenge_id`), + CONSTRAINT `user_in_challenge_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`), + CONSTRAINT `user_in_challenge_challenge_id` FOREIGN KEY (`challenge_id`) REFERENCES `challenges` (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +CREATE TABLE `place_in_challenge` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `place_id` int(10) unsigned NOT NULL, + `challenge_id` int(10) unsigned NOT NULL, + `order` smallint(5) unsigned NOT NULL, + PRIMARY KEY (`id`), + KEY `place_id` (`place_id`), + KEY `challenge_id` (`challenge_id`), + CONSTRAINT `place_in_challenge_place_id` FOREIGN KEY (`place_id`) REFERENCES `places` (`id`), + CONSTRAINT `place_in_challenge_challenge_id` FOREIGN KEY (`challenge_id`) REFERENCES `challenges` (`id`), + CONSTRAINT `unique_order_in_challenge` UNIQUE (`order`, `challenge_id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +CREATE TABLE `guesses` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL, + `place_in_challenge_id` int(10) unsigned NOT NULL, + `lat` decimal(8,6) NOT NULL, + `lng` decimal(9,6) NOT NULL, + `time_spent` int(10), + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `place_in_challenge_id` (`place_in_challenge_id`), + CONSTRAINT `guesses_user_id` FOREIGN KEY (`user_id`) REFERENCES `places` (`id`), + CONSTRAINT `guesses_place_in_challenge_id` FOREIGN KEY (`place_in_challenge_id`) REFERENCES `place_in_challenge` (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; \ No newline at end of file -- 2.45.2 From 99244e93d2c87d02d3c28294f7c802474030f3a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Tue, 11 May 2021 09:07:05 +0200 Subject: [PATCH 02/41] MAPG-235 updated structure and created models --- .../structure/20210510_2000_challenge.sql | 2 + src/PersistentData/Model/Challenge.php | 94 ++++++++++++++++ src/PersistentData/Model/Guess.php | 104 +++++++++++++++++ src/PersistentData/Model/PlaceInChallenge.php | 70 ++++++++++++ src/PersistentData/Model/UserInChallenge.php | 106 ++++++++++++++++++ src/Repository/ChallengeRepository.php | 51 +++++++++ src/Repository/GuessRepository.php | 41 +++++++ src/Repository/PlaceInChallengeRepository.php | 34 ++++++ src/Repository/UserInChallengeRepository.php | 34 ++++++ 9 files changed, 536 insertions(+) create mode 100644 src/PersistentData/Model/Challenge.php create mode 100644 src/PersistentData/Model/Guess.php create mode 100644 src/PersistentData/Model/PlaceInChallenge.php create mode 100644 src/PersistentData/Model/UserInChallenge.php create mode 100644 src/Repository/ChallengeRepository.php create mode 100644 src/Repository/GuessRepository.php create mode 100644 src/Repository/PlaceInChallengeRepository.php create mode 100644 src/Repository/UserInChallengeRepository.php diff --git a/database/migrations/structure/20210510_2000_challenge.sql b/database/migrations/structure/20210510_2000_challenge.sql index ebaa3f4..a108729 100644 --- a/database/migrations/structure/20210510_2000_challenge.sql +++ b/database/migrations/structure/20210510_2000_challenge.sql @@ -5,6 +5,7 @@ CREATE TABLE `challenges` ( `no_move` tinyint(1) NOT NULL DEFAULT 0, `no_pan` tinyint(1) NOT NULL DEFAULT 0, `no_zoom` tinyint(1) NOT NULL DEFAULT 0, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; @@ -15,6 +16,7 @@ CREATE TABLE `user_in_challenge` ( `round` smallint(5) signed NOT NULL DEFAULT -1, `score` int(10) unsigned NOT NULL DEFAULT 0, `time_left` int(10) unsigned, + `owner` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `challenge_id` (`challenge_id`), diff --git a/src/PersistentData/Model/Challenge.php b/src/PersistentData/Model/Challenge.php new file mode 100644 index 0000000..dea916b --- /dev/null +++ b/src/PersistentData/Model/Challenge.php @@ -0,0 +1,94 @@ +token = $token; + } + + public function setTimerSec(int $timerSec): void + { + $this->timerSec = $timerSec; + } + + public function setNoMove(bool $noMove): void + { + $this->$noMove = $noMove; + } + + public function setNoPan(bool $noPan): void + { + $this->$noPan = $noPan; + } + + public function setNoZoom(bool $noZoom): void + { + $this->$noZoom = $noZoom; + } + + public function setCreatedDate(DateTime $created): void + { + $this->created = $created; + } + + public function setCreated(string $created): void + { + $this->created = new DateTime($created); + } + + public function getToken(): int + { + return $this->token; + } + + public function getTimerSec(): int + { + return $this->timerSec; + } + + public function getNoMove(): bool + { + return $this->noMove; + } + + public function getNoPan(): bool + { + return $this->noPan; + } + + public function getNoZoom(): bool + { + return $this->noZoom; + } + + public function getCreatedDate(): DateTime + { + return $this->created; + } + + public function getCreated(): string + { + return $this->created->format('Y-m-d H:i:s'); + } +} diff --git a/src/PersistentData/Model/Guess.php b/src/PersistentData/Model/Guess.php new file mode 100644 index 0000000..01726df --- /dev/null +++ b/src/PersistentData/Model/Guess.php @@ -0,0 +1,104 @@ + User::class, 'place_in_challenge' => PlaceInChallenge::class]; + + private ?User $user = null; + + private ?int $userId = null; + + private ?PlaceInChallenge $placeInChallenge = null; + + private ?int $placeInChallengeId = null; + + private Position $position; + + private int $timeSpent; + + public function setUser(User $user): void + { + $this->user = $user; + } + + public function setUserId(int $userId): void + { + $this->userId = $userId; + } + + public function setPlaceInChallenge(PlaceInChallenge $placeInChallenge): void + { + $this->placeInChallenge = $placeInChallenge; + } + + public function setPlaceInChallengeId(int $placeInChallengeId): void + { + $this->placeInChallengeId = $placeInChallengeId; + } + + public function setPosition(Position $position): void + { + $this->position = $position; + } + + public function setLat(float $lat): void + { + $this->position->setLat($lat); + } + + public function setLng(float $lng): void + { + $this->position->setLng($lng); + } + + public function setTimeSpent(int $timeSpent): void + { + $this->timeSpent = $timeSpent; + } + + public function getUser(): ?User + { + return $this->user; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function getPlaceInChallenge(): ?PlaceInChallenge + { + return $this->placeInChallenge; + } + + public function getPlaceInChallengeId(): ?int + { + return $this->placeInChallengeId; + } + + public function getPosition(): Position + { + return $this->position; + } + + public function getLat(): float + { + return $this->position->getLat(); + } + + public function getLng(): float + { + return $this->position->getLng(); + } + + public function getTimeSpent(): ?int + { + return $this->timeSpent; + } +} diff --git a/src/PersistentData/Model/PlaceInChallenge.php b/src/PersistentData/Model/PlaceInChallenge.php new file mode 100644 index 0000000..b42fd9b --- /dev/null +++ b/src/PersistentData/Model/PlaceInChallenge.php @@ -0,0 +1,70 @@ + Place::class, 'challenge' => Challenge::class]; + + private ?Place $place = null; + + private ?int $placeId = null; + + private ?Challenge $challenge = null; + + private ?int $challengeId = null; + + private int $order; + + public function setPlace(Place $place): void + { + $this->place = $place; + } + + public function setPlaceId(int $placeId): void + { + $this->placeId = $placeId; + } + + public function setChallenge(Challenge $challenge): void + { + $this->challenge = $challenge; + } + + public function setChallengeId(int $challengeId): void + { + $this->challengeId = $challengeId; + } + + public function setOrder(int $order): void + { + $this->order = $order; + } + + public function getPlace(): ?Place + { + return $this->place; + } + + public function getPlaceId(): ?int + { + return $this->placeId; + } + + public function getChallenge(): ?Challenge + { + return $this->challenge; + } + + public function getChallengeId(): ?int + { + return $this->challengeId; + } + + public function getOrder(): int + { + return $this->order; + } +} diff --git a/src/PersistentData/Model/UserInChallenge.php b/src/PersistentData/Model/UserInChallenge.php new file mode 100644 index 0000000..6547551 --- /dev/null +++ b/src/PersistentData/Model/UserInChallenge.php @@ -0,0 +1,106 @@ + User::class, 'challenge' => Challenge::class]; + + private ?User $user = null; + + private ?int $userId = null; + + private ?Challenge $challenge = null; + + private ?int $challengeId = null; + + private int $round; + + private int $score; + + private int $timeLeft; + + private bool $isOwner; + + public function setUser(User $user): void + { + $this->user = $user; + } + + public function setUserId(int $userId): void + { + $this->userId = $userId; + } + + public function setChallenge(Challenge $challenge): void + { + $this->challenge = $challenge; + } + + public function setChallengeId(int $challengeId): void + { + $this->challengeId = $challengeId; + } + + public function setRound(int $round): void + { + $this->round = $round; + } + + public function setScore(int $score): void + { + $this->score = $score; + } + + public function setTimeLeft(int $timeLeft): void + { + $this->timeLeft = $timeLeft; + } + + public function setIsOwner(bool $isOwner): void + { + $this->isOwner = $isOwner; + } + + public function getUser(): ?User + { + return $this->user; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function getChallenge(): ?Challenge + { + return $this->challenge; + } + + public function getChallengeId(): ?int + { + return $this->challengeId; + } + + public function getRound(): int + { + return $this->round; + } + + public function getScore(): int + { + return $this->score; + } + + public function getTimeLeft(): ?int + { + return $this->timeLeft; + } + + public function getIsOwner(): bool + { + return $this->isOwner; + } +} diff --git a/src/Repository/ChallengeRepository.php b/src/Repository/ChallengeRepository.php new file mode 100644 index 0000000..1b66266 --- /dev/null +++ b/src/Repository/ChallengeRepository.php @@ -0,0 +1,51 @@ +pdm = new PersistentDataManager(); + } + + public function getById(int $challengeId): ?Challenge + { + return $this->pdm->selectFromDbById($challengeId, Challenge::class); + } + + public function getByToken(int $token): ?Challenge + { + $select = new Select(\Container::$dbConnection); + $select->where('token', '=', $token); + + return $this->pdm->selectFromDb($select, Challenge::class); + } + + public function getAllByParticipant(User $user): Generator + { + $select = new Select(\Container::$dbConnection); + $select->innerJoin('user_in_challenge', ['challenge', 'id'], '=', ['user_in_challenge', 'challenge_id']); + $select->innerJoin('users', ['users', 'id'], '=', ['user_in_challenge', 'user_id']); + $select->where('user_id', '=', $user->getId()); + + yield from $this->pdm->selectMultipleFromDb($select, Challenge::class); + } + + public function getAllByOwner(User $user): Generator + { + $select = new Select(\Container::$dbConnection); + $select->innerJoin('user_in_challenge', ['challenge', 'id'], '=', ['user_in_challenge', 'challenge_id']); + $select->innerJoin('users', ['users', 'id'], '=', ['user_in_challenge', 'user_id']); + $select->where('user_id', '=', $user->getId()); + $select->where('is_owner', '=', true); + + yield from $this->pdm->selectMultipleFromDb($select, Challenge::class); + } +} \ No newline at end of file diff --git a/src/Repository/GuessRepository.php b/src/Repository/GuessRepository.php new file mode 100644 index 0000000..1739e1a --- /dev/null +++ b/src/Repository/GuessRepository.php @@ -0,0 +1,41 @@ +pdm = new PersistentDataManager(); + } + + public function getAllByUserAndChallenge(User $user, Challenge $challenge) : Generator + { + $select = new Select(\Container::$dbConnection); + $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guess', 'place_in_challenge_id']); + $select->where('user_id', '=', $user->getId()); + $select->where('challenge_id', '=', $challenge->getId()); + + yield from $this->pdm->selectMultipleFromDb($select, Guess::class); + } + + public function getByUserAndPlaceInChallenge(User $user, Challenge $challenge, Place $place) : ?Guess + { + $select = new Select(\Container::$dbConnection); + $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guess', 'place_in_challenge_id']); + $select->where('user_id', '=', $user->getId()); + $select->where('challenge_id', '=', $challenge->getId()); + $select->where('place_id', '=', $place->getId()); + + return $this->pdm->selectFromDb($select, Guess::class); + } +} diff --git a/src/Repository/PlaceInChallengeRepository.php b/src/Repository/PlaceInChallengeRepository.php new file mode 100644 index 0000000..b15e755 --- /dev/null +++ b/src/Repository/PlaceInChallengeRepository.php @@ -0,0 +1,34 @@ +pdm = new PersistentDataManager(); + } + + public function getAllByPlace(Place $place) : Generator + { + $select = new Select(\Container::$dbConnection); + $select->where('place_id', '=', $place->getId()); + + yield from $this->pdm->selectMultipleFromDb($select, PlaceInChallenge::class); + } + + public function getAllByChallenge(Challenge $challenge) : Generator + { + $select = new Select(\Container::$dbConnection); + $select->where('challenge_id', '=', $challenge->getId()); + + yield from $this->pdm->selectMultipleFromDb($select, PlaceInChallenge::class); + } +} diff --git a/src/Repository/UserInChallengeRepository.php b/src/Repository/UserInChallengeRepository.php new file mode 100644 index 0000000..74029e8 --- /dev/null +++ b/src/Repository/UserInChallengeRepository.php @@ -0,0 +1,34 @@ +pdm = new PersistentDataManager(); + } + + public function getAllByUser(User $user) : Generator + { + $select = new Select(\Container::$dbConnection); + $select->where('user_id', '=', $user->getId()); + + yield from $this->pdm->selectMultipleFromDb($select, UserInChallenge::class); + } + + public function getAllByChallenge(Challenge $challenge) : Generator + { + $select = new Select(\Container::$dbConnection); + $select->where('challenge_id', '=', $challenge->getId()); + + yield from $this->pdm->selectMultipleFromDb($select, UserInChallenge::class); + } +} -- 2.45.2 From d7147b30d63070efda8d299968251ec8cfc3d2cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Wed, 12 May 2021 13:51:06 +0200 Subject: [PATCH 03/41] MAPG-235 new challenge can be created and is prepared --- .../structure/20210510_2000_challenge.sql | 2 +- public/static/css/maps.css | 19 ++++ public/static/js/game.js | 26 ++++- public/static/js/maps.js | 41 +++++++ src/Controller/GameController.php | 102 ++++++++++++++++++ src/Database/Query/Select.php | 4 + src/PersistentData/Model/Challenge.php | 18 ++-- src/PersistentData/Model/UserInChallenge.php | 14 +-- src/Repository/ChallengeRepository.php | 7 ++ src/Repository/MapRepository.php | 13 +++ src/Repository/UserInChallengeRepository.php | 9 ++ views/game.php | 1 + views/maps.php | 43 ++++++++ web.php | 7 ++ 14 files changed, 289 insertions(+), 17 deletions(-) diff --git a/database/migrations/structure/20210510_2000_challenge.sql b/database/migrations/structure/20210510_2000_challenge.sql index a108729..5827d5d 100644 --- a/database/migrations/structure/20210510_2000_challenge.sql +++ b/database/migrations/structure/20210510_2000_challenge.sql @@ -16,7 +16,7 @@ CREATE TABLE `user_in_challenge` ( `round` smallint(5) signed NOT NULL DEFAULT -1, `score` int(10) unsigned NOT NULL DEFAULT 0, `time_left` int(10) unsigned, - `owner` tinyint(1) NOT NULL DEFAULT 0, + `is_owner` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `challenge_id` (`challenge_id`), diff --git a/public/static/css/maps.css b/public/static/css/maps.css index c8a58fe..6678c09 100644 --- a/public/static/css/maps.css +++ b/public/static/css/maps.css @@ -75,6 +75,25 @@ div.mapItem>div.buttonContainer { grid-auto-flow: column; } +#restrictions { + margin-top: 1em; + font-family: 'Roboto', sans-serif; +} + +#restrictions h3 { + font-weight: 500; +} + +#restrictions input[type=checkbox] { + height: auto; + margin: 0.5em; +} + +#restrictions input[type=range] { + height: 1.5em; + margin-left: 2em; +} + @media screen and (min-width: 1504px) { #mapContainer { grid-template-columns: auto auto auto auto; diff --git a/public/static/js/game.js b/public/static/js/game.js index b52292d..827a307 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -1,10 +1,13 @@ 'use strict'; +const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); + (function () { var Game = { NUMBER_OF_ROUNDS: 5, MAX_SCORE: 1000, + type: GameType.SINGLE, mapBounds: null, multi: { token: null, owner: false }, rounds: [], @@ -211,6 +214,19 @@ } }, + getGameIdentifier: function() { + switch(Game.type) { + case GameType.SINGLE: + return '/game/' + mapId; + case GameType.MULTI: + return '/multiGame/' + roomId; + case GameType.CHALLENGE: + return '/challenge/' + challengeToken; + default: + return '/game/' + mapId; + } + }, + prepare: function () { var data = new FormData(); var userNames; @@ -226,7 +242,7 @@ } document.getElementById('loading').style.visibility = 'visible'; - var url = roomId ? '/multiGame/' + roomId + '/prepare.json' : '/game/' + mapId + '/prepare.json'; + var url = Game.getGameIdentifier() + '/prepare.json'; MapGuesser.httpRequest('POST', url, function () { document.getElementById('loading').style.visibility = 'hidden'; @@ -269,7 +285,7 @@ } document.getElementById('loading').style.visibility = 'visible'; - MapGuesser.httpRequest('POST', '/game/' + mapId + '/initialData.json', function () { + MapGuesser.httpRequest('POST', Game.getGameIdentifier() + '/initialData.json', function () { document.getElementById('loading').style.visibility = 'hidden'; document.getElementById('panoCover').style.visibility = 'hidden'; @@ -876,6 +892,12 @@ document.getElementById("compass").style.transform = "translateY(-50%) rotate(" + heading + "deg)"; }); + if(roomId !== null) { + Game.type = GameType.MULTI; + } else if(challengeToken !== null) { + Game.type = GameType.CHALLENGE; + } + if (COOKIES_CONSENT) { Game.prepare(); } diff --git a/public/static/js/maps.js b/public/static/js/maps.js index 6d4199f..46efcaf 100644 --- a/public/static/js/maps.js +++ b/public/static/js/maps.js @@ -85,10 +85,40 @@ window.location.href = '/multiGame/' + this.elements.roomId.value; }; + document.getElementById('challengeForm').onsubmit = function (e) { + e.preventDefault(); + + var url = '/challenge/create.json'; + var formData = new FormData(this); + + document.getElementById('loading').style.visibility = 'visible'; + MapGuesser.httpRequest('POST', url, function() { + document.getElementById('loading').style.visibility = 'hidden'; + + if (this.response.error) { + Game.handleErrorResponse(this.response.error); + return; + } + + window.location.href = '/challenge/' + this.response.challengeToken; + + }, formData); + }; + document.getElementById('multiButton').onclick = function () { MapGuesser.showModal('multi'); document.getElementById('createNewRoomButton').href = '/multiGame/new/' + this.dataset.mapId; document.getElementById('multiForm').elements.roomId.select(); + document.getElementById('playMode').style.visibility = 'hidden'; + } + + document.getElementById('challengeButton').onclick = function () { + MapGuesser.showModal('challenge'); + document.getElementById('createNewChallengeButton').href = '/challenge/new/' + this.dataset.mapId; + document.getElementById('playMode').style.visibility = 'hidden'; + + var timeLimit = document.getElementById('timeLimit').value; + document.getElementById('timeLimitLabel').innerText = 'Time limit of ' + timeLimit + ' seconds per round'; } document.getElementById('closePlayModeButton').onclick = function () { @@ -99,6 +129,10 @@ MapGuesser.hideModal(); }; + document.getElementById('closeChallengeButton').onclick = function () { + MapGuesser.hideModal(); + } + var buttons = document.getElementById('mapContainer').getElementsByClassName('playButton'); for (var i = 0; i < buttons.length; i++) { var button = buttons[i]; @@ -107,6 +141,13 @@ MapGuesser.showModal('playMode'); document.getElementById('singleButton').href = '/game/' + this.dataset.mapId; document.getElementById('multiButton').dataset.mapId = this.dataset.mapId; + document.getElementById('challengeMapId').value = this.dataset.mapId; }; } + + document.getElementById('timeLimit').oninput = function () { + var timeLimit = document.getElementById('timeLimit').value; + document.getElementById('timeLimitLabel').innerText = 'Time limit of ' + timeLimit + ' seconds per round'; + document.getElementById('timerEnabled').checked = true; + } })(); diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index a487644..727d4a5 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -8,14 +8,22 @@ use MapGuesser\Response\JsonContent; use MapGuesser\Interfaces\Response\IContent; use MapGuesser\Interfaces\Response\IRedirect; use MapGuesser\Multi\MultiConnector; +use MapGuesser\PersistentData\Model\Challenge; use MapGuesser\PersistentData\Model\MultiRoom; +use MapGuesser\PersistentData\Model\PlaceInChallenge; +use MapGuesser\PersistentData\Model\UserInChallenge; use MapGuesser\PersistentData\PersistentDataManager; +use MapGuesser\Repository\ChallengeRepository; use MapGuesser\Repository\MapRepository; use MapGuesser\Repository\MultiRoomRepository; +use MapGuesser\Repository\PlaceRepository; +use MapGuesser\Repository\UserInChallengeRepository; use MapGuesser\Response\Redirect; class GameController { + const NUMBER_OF_ROUNDS = 5; + private IRequest $request; private PersistentDataManager $pdm; @@ -26,6 +34,12 @@ class GameController private MapRepository $mapRepository; + private PlaceRepository $placeRepository; + + private ChallengeRepository $challengeRepository; + + private UserInChallengeRepository $userInChallengeRepository; + public function __construct(IRequest $request) { $this->request = $request; @@ -33,6 +47,9 @@ class GameController $this->multiConnector = new MultiConnector(); $this->multiRoomRepository = new MultiRoomRepository(); $this->mapRepository = new MapRepository(); + $this->placeRepository = new PlaceRepository(); + $this->challengeRepository = new ChallengeRepository(); + $this->userInChallengeRepository = new UserInChallengeRepository(); } public function getGame(): IContent @@ -79,6 +96,69 @@ class GameController return new HtmlContent('game', ['roomId' => $roomId]); } + public function getChallenge(): IContent + { + $challengeToken = $this->request->query('challengeToken'); + + return new HtmlContent('game', ['challengeToken' => $challengeToken]); + } + + public function createNewChallenge(): IContent + { + // create Challenge + $challengeToken = rand(); + + $challenge = new Challenge(); + $challenge->setToken($challengeToken); + $challenge->setCreatedDate(new DateTime()); + + if($this->request->post('timerEnabled') !== null && $this->request->post('timeLimit') !== null) { + $challenge->setTimerSec($this->request->post('timeLimit')); + } + if($this->request->post('noMove') !== null) { + $challenge->setNoMove(true); + } + if($this->request->post('noPan') !== null) { + $challenge->setNoPan(true); + } + if($this->request->post('noZoom') !== null) { + $challenge->setNoZoom(true); + } + + $this->pdm->saveToDb($challenge); + + // save owner/creator + + $session = $this->request->session(); + $userId = $session->get('userId'); + + $userInChallenge = new UserInChallenge(); + $userInChallenge->setUserId($userId); + $userInChallenge->setChallenge($challenge); + $userInChallenge->setTimeLeft($challenge->getTimerSec()); + $userInChallenge->setIsOwner(true); + + $this->pdm->saveToDb($userInChallenge); + + // select places + + $mapId = (int) $this->request->post('mapId'); + $map = $this->mapRepository->getById($mapId); + + $places = $this->placeRepository->getRandomNPlaces($mapId, static::NUMBER_OF_ROUNDS, $userId); + + $order = 1; + foreach ($places as $place) { + $placeInChallenge = new PlaceInChallenge(); + $placeInChallenge->setPlace($place); + $placeInChallenge->setChallenge($challenge); + $placeInChallenge->setOrder($order++); + $this->pdm->saveToDb($placeInChallenge); + } + + return new JsonContent(['challengeToken' => dechex($challengeToken)]); + } + public function prepareGame(): IContent { $mapId = (int) $this->request->query('mapId'); @@ -154,6 +234,28 @@ class GameController ]); } + public function prepareChallenge(): IContent + { + $challengeToken_str = $this->request->query('challengeToken'); + $session = $this->request->session(); + $userId = $session->get('userId'); + $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str); + + if(!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) { + $userInChallenge = new UserInChallenge(); + $userInChallenge->setUserId($userId); + $userInChallenge->setTimeLeft($challenge->getTimerSec()); + } + + $map = $this->mapRepository->getByChallenge($challenge); + + return new JsonContent([ + 'mapId' => $map->getId(), + 'mapName' => $map->getName(), + 'bounds' => $map->getBounds()->toArray() + ]); + } + private function getMultiToken(string $roomId): string { $session = $this->request->session(); diff --git a/src/Database/Query/Select.php b/src/Database/Query/Select.php index d62bd84..67f3fae 100644 --- a/src/Database/Query/Select.php +++ b/src/Database/Query/Select.php @@ -323,6 +323,10 @@ class Select $value = $this->generateColumn($value); }); + if(count($columns) > 0 && $columns[0] == Utils::backtick('id')) { + $columns[0] = Utils::backtick($this->table) . '.' . Utils::backtick('id'); + } + return implode(',', $columns); } diff --git a/src/PersistentData/Model/Challenge.php b/src/PersistentData/Model/Challenge.php index dea916b..11152f5 100644 --- a/src/PersistentData/Model/Challenge.php +++ b/src/PersistentData/Model/Challenge.php @@ -4,7 +4,7 @@ use DateTime; class Challenge extends Model { - protected static string $table = 'challenge'; + protected static string $table = 'challenges'; protected static array $fields = ['token', 'timer_sec', 'no_move', 'no_pan', 'no_zoom', 'created']; @@ -12,13 +12,13 @@ class Challenge extends Model private int $token; - private int $timerSec; + private ?int $timerSec = null; - private bool $noMove; + private bool $noMove = false; - private bool $noPan; + private bool $noPan = false; - private bool $noZoom; + private bool $noZoom = false; private DateTime $created; @@ -27,9 +27,11 @@ class Challenge extends Model $this->token = $token; } - public function setTimerSec(int $timerSec): void + public function setTimerSec(?int $timerSec): void { - $this->timerSec = $timerSec; + if(isset($timerSec)) { + $this->timerSec = $timerSec; + } } public function setNoMove(bool $noMove): void @@ -62,7 +64,7 @@ class Challenge extends Model return $this->token; } - public function getTimerSec(): int + public function getTimerSec(): ?int { return $this->timerSec; } diff --git a/src/PersistentData/Model/UserInChallenge.php b/src/PersistentData/Model/UserInChallenge.php index 6547551..829a4d5 100644 --- a/src/PersistentData/Model/UserInChallenge.php +++ b/src/PersistentData/Model/UserInChallenge.php @@ -16,13 +16,13 @@ class UserInChallenge extends Model private ?int $challengeId = null; - private int $round; + private int $round = -1; - private int $score; + private int $score = 0; - private int $timeLeft; + private ?int $timeLeft = null; - private bool $isOwner; + private bool $isOwner = false; public function setUser(User $user): void { @@ -54,9 +54,11 @@ class UserInChallenge extends Model $this->score = $score; } - public function setTimeLeft(int $timeLeft): void + public function setTimeLeft(?int $timeLeft): void { - $this->timeLeft = $timeLeft; + if(isset($timeLeft)) { + $this->timeLeft = $timeLeft; + } } public function setIsOwner(bool $isOwner): void diff --git a/src/Repository/ChallengeRepository.php b/src/Repository/ChallengeRepository.php index 1b66266..dc0344e 100644 --- a/src/Repository/ChallengeRepository.php +++ b/src/Repository/ChallengeRepository.php @@ -28,6 +28,13 @@ class ChallengeRepository return $this->pdm->selectFromDb($select, Challenge::class); } + public function getByTokenStr(string $token_str): ?Challenge + { + $token = hexdec($token_str); + + return $this->getByToken($token); + } + public function getAllByParticipant(User $user): Generator { $select = new Select(\Container::$dbConnection); diff --git a/src/Repository/MapRepository.php b/src/Repository/MapRepository.php index cfb9885..7854091 100644 --- a/src/Repository/MapRepository.php +++ b/src/Repository/MapRepository.php @@ -1,5 +1,7 @@ pdm->selectFromDbById($mapId, Map::class); } + + public function getByChallenge(Challenge $challenge): ?Map + { + $select = new Select(\Container::$dbConnection); + $select->innerJoin('places', ['maps', 'id'], '=', ['places', 'map_id']); + $select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']); + $select->where('challenge_id', '=', $challenge->getId()); + $select->limit(1); + + return $this->pdm->selectFromDb($select, Map::class); + } } diff --git a/src/Repository/UserInChallengeRepository.php b/src/Repository/UserInChallengeRepository.php index 74029e8..a03915d 100644 --- a/src/Repository/UserInChallengeRepository.php +++ b/src/Repository/UserInChallengeRepository.php @@ -31,4 +31,13 @@ class UserInChallengeRepository yield from $this->pdm->selectMultipleFromDb($select, UserInChallenge::class); } + + public function isUserParticipatingInChallenge(int $userId, Challenge $challenge): bool + { + $select = new Select(\Container::$dbConnection, 'user_in_challenge'); + $select->where('user_id', '=', $userId); + $select->where('challenge_id', '=', $challenge->getId()); + + return $select->count(); + } } diff --git a/views/game.php b/views/game.php index 218b937..8495112 100644 --- a/views/game.php +++ b/views/game.php @@ -84,5 +84,6 @@ var multiUrl = ''; var roomId = ; var mapId = ; + var challengeToken = ; @endsection diff --git a/views/maps.php b/views/maps.php index 52822ab..d210759 100644 --- a/views/maps.php +++ b/views/maps.php @@ -11,6 +11,8 @@ TODO: condition! Single player

OR

+

OR

+
@@ -29,6 +31,47 @@ TODO: condition! + @endsection @section(main) diff --git a/web.php b/web.php index 4d52673..8e72033 100644 --- a/web.php +++ b/web.php @@ -62,6 +62,13 @@ Container::$routeCollection->group('multiGame', function (MapGuesser\Routing\Rou $routeCollection->post('multiGame.nextRound-json', '{roomId}/nextRound.json', [MapGuesser\Controller\GameFlowController::class, 'multiNextRound']); $routeCollection->post('multiGame.guess-json', '{roomId}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'multiGuess']); }); +Container::$routeCollection->group('challenge', function (MapGuesser\Routing\RouteCollection $routeCollection) { + $routeCollection->post('challenge.create', 'create.json', [\MapGuesser\Controller\GameController::class, 'createNewChallenge']); + $routeCollection->get('challenge', '{challengeToken}', [MapGuesser\Controller\GameController::class, 'getChallenge']); + $routeCollection->post('challenge.prepare-json', '{challengeToken}/prepare.json', [MapGuesser\Controller\GameController::class, 'prepareChallenge']); + $routeCollection->post('challenge.initialData-json', '{challengeToken}/initialData.json', [MapGuesser\Controller\GameFlowController::class, 'challengeInitialData']); + $routeCollection->post('challenge.guess-json', '{challengeToken}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'challengeGuess']); +}); Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) { $routeCollection->get('admin.mapEditor', 'mapEditor/{mapId?}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']); $routeCollection->get('admin.place', 'place.json/{placeId}', [MapGuesser\Controller\MapAdminController::class, 'getPlace']); -- 2.45.2 From 5daed100360789aa0f9c7f0a2f14d116c8819a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Wed, 12 May 2021 15:34:15 +0200 Subject: [PATCH 04/41] MAPG-235 challengeInitialData implemented and first round is loaded --- .../structure/20210510_2000_challenge.sql | 2 +- src/Controller/GameFlowController.php | 55 +++++++++++++++++++ src/PersistentData/Model/UserInChallenge.php | 2 +- src/Repository/PlaceRepository.php | 11 ++++ src/Repository/UserInChallengeRepository.php | 9 +++ 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/database/migrations/structure/20210510_2000_challenge.sql b/database/migrations/structure/20210510_2000_challenge.sql index 5827d5d..5f21f0e 100644 --- a/database/migrations/structure/20210510_2000_challenge.sql +++ b/database/migrations/structure/20210510_2000_challenge.sql @@ -13,7 +13,7 @@ CREATE TABLE `user_in_challenge` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(10) unsigned NOT NULL, `challenge_id` int(10) unsigned NOT NULL, - `round` smallint(5) signed NOT NULL DEFAULT -1, + `round` smallint(5) signed NOT NULL DEFAULT 0, `score` int(10) unsigned NOT NULL DEFAULT 0, `time_left` int(10) unsigned, `is_owner` tinyint(1) NOT NULL DEFAULT 0, diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index eba2a48..dc42994 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -6,10 +6,13 @@ use MapGuesser\Util\Geo\Position; use MapGuesser\Response\JsonContent; use MapGuesser\Interfaces\Response\IContent; use MapGuesser\Multi\MultiConnector; +use MapGuesser\PersistentData\Model\UserInChallenge; use MapGuesser\PersistentData\PersistentDataManager; use MapGuesser\PersistentData\Model\UserPlayedPlace; +use MapGuesser\Repository\ChallengeRepository; use MapGuesser\Repository\MultiRoomRepository; use MapGuesser\Repository\PlaceRepository; +use MapGuesser\Repository\UserInChallengeRepository; use MapGuesser\Repository\UserPlayedPlaceRepository; class GameFlowController @@ -29,6 +32,10 @@ class GameFlowController private UserPlayedPlaceRepository $userPlayedPlaceRepository; + private ChallengeRepository $challengeRepository; + + private UserInChallengeRepository $userInChallengeRepository; + public function __construct(IRequest $request) { $this->request = $request; @@ -37,6 +44,8 @@ class GameFlowController $this->multiRoomRepository = new MultiRoomRepository(); $this->placeRepository = new PlaceRepository(); $this->userPlayedPlaceRepository = new UserPlayedPlaceRepository(); + $this->challengeRepository = new ChallengeRepository(); + $this->userInChallengeRepository = new UserInChallengeRepository(); } public function initialData(): IContent @@ -115,6 +124,52 @@ class GameFlowController return new JsonContent(['ok' => true]); } + public function challengeInitialData(): IContent + { + // TODO + $session = $this->request->session(); + $userId = $session->get('userId'); + $challengeToken_str = $this->request->query('challengeToken'); + $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str); + + if (!isset($challenge)) { + return new JsonContent(['error' => 'game_not_found']); + } + + $userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge); + $currentRound = $userInChallenge->getRound(); + + $currentPlace = $this->placeRepository->getCurrentInChallenge($challenge, $currentRound); + + $response = []; + + // $last = $state['rounds'][$state['currentRound']]; + // $response['place'] = [ + // 'panoId' => $last['panoId'], + // 'pov' => $last['pov']->toArray() + // ]; + + $response['place'] = [ + 'panoId' => $currentPlace->getPanoIdCached(), + 'pov' => [$currentPlace->getPov()->toArray()] + ]; + + $response['history'] = []; + // for ($i = 0; $i < $state['currentRound']; ++$i) { + // $round = $state['rounds'][$i]; + // $response['history'][] = [ + // 'position' => $round['position']->toArray(), + // 'result' => [ + // 'guessPosition' => $round['guessPosition']->toArray(), + // 'distance' => $round['distance'], + // 'score' => $round['score'] + // ] + // ]; + // } + + return new JsonContent($response); + } + public function guess(): IContent { $mapId = (int) $this->request->query('mapId'); diff --git a/src/PersistentData/Model/UserInChallenge.php b/src/PersistentData/Model/UserInChallenge.php index 829a4d5..4037691 100644 --- a/src/PersistentData/Model/UserInChallenge.php +++ b/src/PersistentData/Model/UserInChallenge.php @@ -16,7 +16,7 @@ class UserInChallenge extends Model private ?int $challengeId = null; - private int $round = -1; + private int $round = 0; private int $score = 0; diff --git a/src/Repository/PlaceRepository.php b/src/Repository/PlaceRepository.php index 4c36edc..3d69d3b 100644 --- a/src/Repository/PlaceRepository.php +++ b/src/Repository/PlaceRepository.php @@ -2,6 +2,7 @@ use Generator; use MapGuesser\Database\Query\Select; +use MapGuesser\PersistentData\Model\Challenge; use MapGuesser\PersistentData\Model\Map; use MapGuesser\PersistentData\Model\Place; use MapGuesser\PersistentData\PersistentDataManager; @@ -176,5 +177,15 @@ class PlaceRepository return $places; } + + public function getCurrentInChallenge(Challenge $challenge, int $round): ?Place + { + $select = new Select(\Container::$dbConnection); + $select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']); + $select->orderBy('order'); + $select->limit(1, $round); + + return $this->pdm->selectFromDb($select, Place::class); + } } diff --git a/src/Repository/UserInChallengeRepository.php b/src/Repository/UserInChallengeRepository.php index a03915d..20ae0d5 100644 --- a/src/Repository/UserInChallengeRepository.php +++ b/src/Repository/UserInChallengeRepository.php @@ -32,6 +32,15 @@ class UserInChallengeRepository yield from $this->pdm->selectMultipleFromDb($select, UserInChallenge::class); } + public function getByUserIdAndChallenge(int $userId, Challenge $challenge): ?UserInChallenge + { + $select = new Select(\Container::$dbConnection); + $select->where('user_id', '=', $userId); + $select->where('challenge_id', '=', $challenge->getId()); + + return $this->pdm->selectFromDb($select, UserInChallenge::class); + } + public function isUserParticipatingInChallenge(int $userId, Challenge $challenge): bool { $select = new Select(\Container::$dbConnection, 'user_in_challenge'); -- 2.45.2 From 93f8fc3f34b0d9989ce35a544cd3d7ab067028a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Wed, 12 May 2021 21:01:26 +0200 Subject: [PATCH 05/41] MAPG-235 basic gameflow works --- .../structure/20210510_2000_challenge.sql | 3 +- public/static/js/game.js | 34 ++++- src/Controller/GameController.php | 2 + src/Controller/GameFlowController.php | 130 +++++++++++++----- src/PersistentData/Model/Guess.php | 35 ++++- src/PersistentData/Model/UserInChallenge.php | 14 +- src/Repository/GuessRepository.php | 15 +- src/Repository/MapRepository.php | 9 ++ src/Repository/PlaceInChallengeRepository.php | 9 ++ src/Repository/PlaceRepository.php | 13 +- 10 files changed, 209 insertions(+), 55 deletions(-) diff --git a/database/migrations/structure/20210510_2000_challenge.sql b/database/migrations/structure/20210510_2000_challenge.sql index 5f21f0e..626e6d5 100644 --- a/database/migrations/structure/20210510_2000_challenge.sql +++ b/database/migrations/structure/20210510_2000_challenge.sql @@ -14,7 +14,6 @@ CREATE TABLE `user_in_challenge` ( `user_id` int(10) unsigned NOT NULL, `challenge_id` int(10) unsigned NOT NULL, `round` smallint(5) signed NOT NULL DEFAULT 0, - `score` int(10) unsigned NOT NULL DEFAULT 0, `time_left` int(10) unsigned, `is_owner` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), @@ -43,6 +42,8 @@ CREATE TABLE `guesses` ( `place_in_challenge_id` int(10) unsigned NOT NULL, `lat` decimal(8,6) NOT NULL, `lng` decimal(9,6) NOT NULL, + `score` int(10) NOT NULL, + `distance` int(10) NOT NULL, `time_spent` int(10), PRIMARY KEY (`id`), KEY `user_id` (`user_id`), diff --git a/public/static/js/game.js b/public/static/js/game.js index 827a307..b1d101d 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -294,9 +294,6 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); return; } - Game.panoId = this.response.place.panoId; - Game.pov = this.response.place.pov; - for (var i = 0; i < this.response.history.length; ++i) { var round = this.response.history[i]; Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); @@ -305,6 +302,35 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); Game.scoreSum += round.result.score; } + if (this.response.finished) { + + // TODO: refactor - it is necessary for mobile + if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') { + document.getElementById('showGuessButton').click(); + } + + if (Game.adaptGuess) { + document.getElementById('guess').classList.remove('adapt'); + } + + if (Game.guessMarker) { + Game.guessMarker.setMap(null); + Game.guessMarker = null; + } + + document.getElementById('guess').classList.add('result'); + + Game.map.setOptions({ + draggableCursor: 'grab' + }); + + Game.showSummary(); + return; + } + + Game.panoId = this.response.place.panoId; + Game.pov = this.response.place.pov; + document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS); document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE); @@ -561,7 +587,7 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); data.append('lng', String(guessPosition.lng)); document.getElementById('loading').style.visibility = 'visible'; - var url = roomId ? '/multiGame/' + roomId + '/guess.json' : '/game/' + mapId + '/guess.json'; + var url = Game.getGameIdentifier() + '/guess.json'; MapGuesser.httpRequest('POST', url, function () { document.getElementById('loading').style.visibility = 'hidden'; diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index 727d4a5..b17aec2 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -244,7 +244,9 @@ class GameController if(!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) { $userInChallenge = new UserInChallenge(); $userInChallenge->setUserId($userId); + $userInChallenge->setChallenge($challenge); $userInChallenge->setTimeLeft($challenge->getTimerSec()); + $this->pdm->saveToDb($userInChallenge); } $map = $this->mapRepository->getByChallenge($challenge); diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index dc42994..dc0f01e 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -8,9 +8,13 @@ use MapGuesser\Interfaces\Response\IContent; use MapGuesser\Multi\MultiConnector; use MapGuesser\PersistentData\Model\UserInChallenge; use MapGuesser\PersistentData\PersistentDataManager; +use MapGuesser\PersistentData\Model\Guess; use MapGuesser\PersistentData\Model\UserPlayedPlace; use MapGuesser\Repository\ChallengeRepository; +use MapGuesser\Repository\GuessRepository; +use MapGuesser\Repository\MapRepository; use MapGuesser\Repository\MultiRoomRepository; +use MapGuesser\Repository\PlaceInChallengeRepository; use MapGuesser\Repository\PlaceRepository; use MapGuesser\Repository\UserInChallengeRepository; use MapGuesser\Repository\UserPlayedPlaceRepository; @@ -30,12 +34,18 @@ class GameFlowController private PlaceRepository $placeRepository; + private MapRepository $mapRepository; + private UserPlayedPlaceRepository $userPlayedPlaceRepository; private ChallengeRepository $challengeRepository; private UserInChallengeRepository $userInChallengeRepository; + private PlaceInChallengeRepository $placeInChallengeRepository; + + private GuessRepository $guessRepository; + public function __construct(IRequest $request) { $this->request = $request; @@ -43,9 +53,12 @@ class GameFlowController $this->multiConnector = new MultiConnector(); $this->multiRoomRepository = new MultiRoomRepository(); $this->placeRepository = new PlaceRepository(); + $this->mapRepository = new MapRepository(); $this->userPlayedPlaceRepository = new UserPlayedPlaceRepository(); $this->challengeRepository = new ChallengeRepository(); $this->userInChallengeRepository = new UserInChallengeRepository(); + $this->placeInChallengeRepository = new PlaceInChallengeRepository(); + $this->guessRepository = new GuessRepository(); } public function initialData(): IContent @@ -126,7 +139,6 @@ class GameFlowController public function challengeInitialData(): IContent { - // TODO $session = $this->request->session(); $userId = $session->get('userId'); $challengeToken_str = $this->request->query('challengeToken'); @@ -138,34 +150,36 @@ class GameFlowController $userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge); $currentRound = $userInChallenge->getRound(); - - $currentPlace = $this->placeRepository->getCurrentInChallenge($challenge, $currentRound); + $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); $response = []; - // $last = $state['rounds'][$state['currentRound']]; - // $response['place'] = [ - // 'panoId' => $last['panoId'], - // 'pov' => $last['pov']->toArray() - // ]; - - $response['place'] = [ - 'panoId' => $currentPlace->getPanoIdCached(), - 'pov' => [$currentPlace->getPov()->toArray()] - ]; - $response['history'] = []; - // for ($i = 0; $i < $state['currentRound']; ++$i) { - // $round = $state['rounds'][$i]; - // $response['history'][] = [ - // 'position' => $round['position']->toArray(), - // 'result' => [ - // 'guessPosition' => $round['guessPosition']->toArray(), - // 'distance' => $round['distance'], - // 'score' => $round['score'] - // ] - // ]; - // } + + $guesses = iterator_to_array($this->guessRepository->getAllInChallenge($userId, $challenge)); + $places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge)); + + for($i = 0; $i < $currentRound; ++$i) + { + $response['history'][] = [ + 'position' => $places[$i]->getPosition()->toArray(), + 'result' => [ + 'guessPosition' => $guesses[$i]->getPosition()->toArray(), + 'distance' => $guesses[$i]->getDistance(), + 'score' => $guesses[$i]->getScore() + ] + ]; + } + + if(!isset($currentPlace)) { // game finished + $response['finished'] = true; + + } else { // continue game + $response['place'] = [ + 'panoId' => $currentPlace->getPanoIdCached(), + 'pov' => [$currentPlace->getPov()->toArray()] + ]; + } return new JsonContent($response); } @@ -181,7 +195,7 @@ class GameFlowController $last = $state['rounds'][$state['currentRound']]; $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); - $result = $this->evalueteGuess($last['position'], $guessPosition, $state['area']); + $result = $this->evaluateGuess($last['position'], $guessPosition, $state['area']); $last['guessPosition'] = $guessPosition; $last['distance'] = $result['distance']; @@ -205,19 +219,18 @@ class GameFlowController $session->set('state', $state); - $this->saveVisit($last); + $this->saveVisit($last['placeId']); return new JsonContent($response); } // save the selected place for the round in UserPlayedPlace - private function saveVisit($last): void + private function saveVisit($placeId): void { $session = $this->request->session(); $userId = $session->get('userId'); if(isset($userId)) { - $placeId = $last['placeId']; $userPlayedPlace = $this->userPlayedPlaceRepository->getByUserIdAndPlaceId($userId, $placeId); if(!$userPlayedPlace) { $userPlayedPlace = new UserPlayedPlace(); @@ -245,7 +258,7 @@ class GameFlowController $last = $state['rounds'][$state['currentRound']]; $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); - $result = $this->evalueteGuess($last['position'], $guessPosition, $state['area']); + $result = $this->evaluateGuess($last['position'], $guessPosition, $state['area']); $responseFromMulti = $this->multiConnector->sendMessage('guess', [ 'roomId' => $roomId, @@ -268,6 +281,61 @@ class GameFlowController return new JsonContent($response); } + public function challengeGuess(): IContent + { + $session = $this->request->session(); + $userId = $session->get('userId'); + $challengeToken_str = $this->request->query('challengeToken'); + $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str); + + if (!isset($challenge)) { + return new JsonContent(['error' => 'game_not_found']); + } + + $userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge); + $currentRound = $userInChallenge->getRound(); + $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); + $map = $this->mapRepository->getByPlace($currentPlace); + $placeInChallenge = $this->placeInChallengeRepository->getByPlaceAndChallenge($currentPlace, $challenge); + + $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); + $result = $this->evaluateGuess($currentPlace->getPosition(), $guessPosition, $map->getArea()); + + // save guess + $guess = new Guess(); + $guess->setUserId($userId); + $guess->setPlaceInChallenge($placeInChallenge); + $guess->setPosition($guessPosition); + $guess->setDistance($result['distance']); + $guess->setScore($result['score']); + $this->pdm->saveToDb($guess); + + $response = [ + 'position' => $currentPlace->getPosition()->toArray(), + 'result' => $result + ]; + + // update round + $nextRound = $currentRound + 1; + + $userInChallenge->setRound($nextRound); + $this->pdm->saveToDb($userInChallenge); + + if ($nextRound < static::NUMBER_OF_ROUNDS) { + + $nextPlace = $this->placeRepository->getByRoundInChallenge($challenge, $nextRound); + + $response['place'] = [ + 'panoId' => $nextPlace->getPanoIdCached(), + 'pov' => $nextPlace->getPov()->toArray() + ]; + } + + $this->saveVisit($currentPlace->getId()); + + return new JsonContent($response); + } + public function multiNextRound(): IContent { $roomId = $this->request->query('roomId'); @@ -297,7 +365,7 @@ class GameFlowController return new JsonContent(['ok' => true]); } - private function evalueteGuess(Position $realPosition, Position $guessPosition, float $area) + private function evaluateGuess(Position $realPosition, Position $guessPosition, float $area) { $distance = $this->calculateDistance($realPosition, $guessPosition); $score = $this->calculateScore($distance, $area); diff --git a/src/PersistentData/Model/Guess.php b/src/PersistentData/Model/Guess.php index 01726df..913f65f 100644 --- a/src/PersistentData/Model/Guess.php +++ b/src/PersistentData/Model/Guess.php @@ -4,9 +4,9 @@ use MapGuesser\Util\Geo\Position; class Guess extends Model { - protected static string $table = 'guess'; + protected static string $table = 'guesses'; - protected static array $fields = ['user_id', 'place_in_challenge_id', 'lat', 'lng', 'time_spent']; + protected static array $fields = ['user_id', 'place_in_challenge_id', 'lat', 'lng', 'score', 'distance', 'time_spent']; protected static array $relations = ['user' => User::class, 'place_in_challenge' => PlaceInChallenge::class]; @@ -20,7 +20,16 @@ class Guess extends Model private Position $position; - private int $timeSpent; + private int $score = 0; + + private int $distance = 0; + + private int $timeSpent = 0; + + public function __construct() + { + $this->position = new Position(0.0, 0.0); + } public function setUser(User $user): void { @@ -57,6 +66,16 @@ class Guess extends Model $this->position->setLng($lng); } + public function setScore(int $score): void + { + $this->score = $score; + } + + public function setDistance(int $distance): void + { + $this->distance = $distance; + } + public function setTimeSpent(int $timeSpent): void { $this->timeSpent = $timeSpent; @@ -97,6 +116,16 @@ class Guess extends Model return $this->position->getLng(); } + public function getScore(): int + { + return $this->score; + } + + public function getDistance(): int + { + return $this->distance; + } + public function getTimeSpent(): ?int { return $this->timeSpent; diff --git a/src/PersistentData/Model/UserInChallenge.php b/src/PersistentData/Model/UserInChallenge.php index 4037691..dfb85b1 100644 --- a/src/PersistentData/Model/UserInChallenge.php +++ b/src/PersistentData/Model/UserInChallenge.php @@ -4,7 +4,7 @@ class UserInChallenge extends Model { protected static string $table = 'user_in_challenge'; - protected static array $fields = ['user_id', 'challenge_id', 'round', 'score', 'time_left', 'is_owner']; + protected static array $fields = ['user_id', 'challenge_id', 'round', 'time_left', 'is_owner']; protected static array $relations = ['user' => User::class, 'challenge' => Challenge::class]; @@ -18,8 +18,6 @@ class UserInChallenge extends Model private int $round = 0; - private int $score = 0; - private ?int $timeLeft = null; private bool $isOwner = false; @@ -49,11 +47,6 @@ class UserInChallenge extends Model $this->round = $round; } - public function setScore(int $score): void - { - $this->score = $score; - } - public function setTimeLeft(?int $timeLeft): void { if(isset($timeLeft)) { @@ -91,11 +84,6 @@ class UserInChallenge extends Model return $this->round; } - public function getScore(): int - { - return $this->score; - } - public function getTimeLeft(): ?int { return $this->timeLeft; diff --git a/src/Repository/GuessRepository.php b/src/Repository/GuessRepository.php index 1739e1a..7035c7b 100644 --- a/src/Repository/GuessRepository.php +++ b/src/Repository/GuessRepository.php @@ -21,7 +21,7 @@ class GuessRepository public function getAllByUserAndChallenge(User $user, Challenge $challenge) : Generator { $select = new Select(\Container::$dbConnection); - $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guess', 'place_in_challenge_id']); + $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']); $select->where('user_id', '=', $user->getId()); $select->where('challenge_id', '=', $challenge->getId()); @@ -31,11 +31,22 @@ class GuessRepository public function getByUserAndPlaceInChallenge(User $user, Challenge $challenge, Place $place) : ?Guess { $select = new Select(\Container::$dbConnection); - $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guess', 'place_in_challenge_id']); + $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']); $select->where('user_id', '=', $user->getId()); $select->where('challenge_id', '=', $challenge->getId()); $select->where('place_id', '=', $place->getId()); return $this->pdm->selectFromDb($select, Guess::class); } + + public function getAllInChallenge(int $userId, Challenge $challenge): Generator + { + $select = new Select(\Container::$dbConnection); + $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']); + $select->where('user_id', '=', $userId); + $select->where('challenge_id', '=', $challenge->getId()); + $select->orderBy('order'); + + yield from $this->pdm->selectMultipleFromDb($select, Guess::class); + } } diff --git a/src/Repository/MapRepository.php b/src/Repository/MapRepository.php index 7854091..5514ee5 100644 --- a/src/Repository/MapRepository.php +++ b/src/Repository/MapRepository.php @@ -3,6 +3,7 @@ use MapGuesser\Database\Query\Select; use MapGuesser\PersistentData\Model\Challenge; use MapGuesser\PersistentData\Model\Map; +use MapGuesser\PersistentData\Model\Place; use MapGuesser\PersistentData\PersistentDataManager; class MapRepository @@ -19,6 +20,14 @@ class MapRepository return $this->pdm->selectFromDbById($mapId, Map::class); } + public function getByPlace(Place $place): ?Map + { + $select = new Select(\Container::$dbConnection); + $select->where('id', '=', $place->getMapId()); + + return $this->pdm->selectFromDb($select, Map::class); + } + public function getByChallenge(Challenge $challenge): ?Map { $select = new Select(\Container::$dbConnection); diff --git a/src/Repository/PlaceInChallengeRepository.php b/src/Repository/PlaceInChallengeRepository.php index b15e755..d9865e6 100644 --- a/src/Repository/PlaceInChallengeRepository.php +++ b/src/Repository/PlaceInChallengeRepository.php @@ -31,4 +31,13 @@ class PlaceInChallengeRepository yield from $this->pdm->selectMultipleFromDb($select, PlaceInChallenge::class); } + + public function getByPlaceAndChallenge(Place $place, Challenge $challenge) : ?PlaceInChallenge + { + $select = new Select(\Container::$dbConnection); + $select->where('place_id', '=', $place->getId()); + $select->where('challenge_id', '=', $challenge->getId()); + + return $this->pdm->selectFromDb($select, PlaceInChallenge::class); + } } diff --git a/src/Repository/PlaceRepository.php b/src/Repository/PlaceRepository.php index 3d69d3b..7c7c548 100644 --- a/src/Repository/PlaceRepository.php +++ b/src/Repository/PlaceRepository.php @@ -178,14 +178,25 @@ class PlaceRepository return $places; } - public function getCurrentInChallenge(Challenge $challenge, int $round): ?Place + public function getByRoundInChallenge(Challenge $challenge, int $round): ?Place { $select = new Select(\Container::$dbConnection); $select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']); + $select->where('challenge_id', '=', $challenge->getId()); $select->orderBy('order'); $select->limit(1, $round); return $this->pdm->selectFromDb($select, Place::class); } + + public function getAllInChallenge(Challenge $challenge): Generator + { + $select = new Select(\Container::$dbConnection); + $select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']); + $select->where('challenge_id', '=', $challenge->getId()); + $select->orderBy('order'); + + yield from $this->pdm->selectMultipleFromDb($select, Place::class); + } } -- 2.45.2 From e7869d67f7d5f2c300bb4144fa9f9e1eaa6c2dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Thu, 13 May 2021 13:06:24 +0200 Subject: [PATCH 06/41] MAPG-235 guesses of other players in the round are sent after guess --- public/static/js/game.js | 2 ++ src/Controller/GameFlowController.php | 31 ++++++++++++++++---- src/Database/Query/Select.php | 4 --- src/PersistentData/PersistentDataManager.php | 4 +++ src/Repository/GuessRepository.php | 23 ++++++++++++++- src/Repository/MapRepository.php | 5 +--- src/Repository/UserRepository.php | 6 ++++ 7 files changed, 61 insertions(+), 14 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index b1d101d..13f4e6b 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -325,6 +325,8 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); }); Game.showSummary(); + + document.getElementById('continueButton').style.display = 'none'; return; } diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index dc0f01e..b8dda00 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -18,6 +18,7 @@ use MapGuesser\Repository\PlaceInChallengeRepository; use MapGuesser\Repository\PlaceRepository; use MapGuesser\Repository\UserInChallengeRepository; use MapGuesser\Repository\UserPlayedPlaceRepository; +use MapGuesser\Repository\UserRepository; class GameFlowController { @@ -36,6 +37,8 @@ class GameFlowController private MapRepository $mapRepository; + private UserRepository $userRepository; + private UserPlayedPlaceRepository $userPlayedPlaceRepository; private ChallengeRepository $challengeRepository; @@ -54,6 +57,7 @@ class GameFlowController $this->multiRoomRepository = new MultiRoomRepository(); $this->placeRepository = new PlaceRepository(); $this->mapRepository = new MapRepository(); + $this->userRepository = new UserRepository(); $this->userPlayedPlaceRepository = new UserPlayedPlaceRepository(); $this->challengeRepository = new ChallengeRepository(); $this->userInChallengeRepository = new UserInChallengeRepository(); @@ -156,7 +160,7 @@ class GameFlowController $response['history'] = []; - $guesses = iterator_to_array($this->guessRepository->getAllInChallenge($userId, $challenge)); + $guesses = iterator_to_array($this->guessRepository->getAllInChallengeByUser($userId, $challenge)); $places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge)); for($i = 0; $i < $currentRound; ++$i) @@ -298,6 +302,18 @@ class GameFlowController $map = $this->mapRepository->getByPlace($currentPlace); $placeInChallenge = $this->placeInChallengeRepository->getByPlaceAndChallenge($currentPlace, $challenge); + // fill in all other results for the round + $response['allResults'] = []; + foreach($this->guessRepository->getAllInChallengeByRound($currentRound, $challenge) as $guess) { + $user = $this->userRepository->getByGuess($guess); + $response['allResults'][] = [ + 'userName' => $user->getDisplayName(), + 'guessPosition' => $guess->getPosition()->toArray(), + 'distance' => $guess->getDistance(), + 'score' => $guess->getScore() + ]; + } + $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); $result = $this->evaluateGuess($currentPlace->getPosition(), $guessPosition, $map->getArea()); @@ -310,10 +326,12 @@ class GameFlowController $guess->setScore($result['score']); $this->pdm->saveToDb($guess); - $response = [ - 'position' => $currentPlace->getPosition()->toArray(), - 'result' => $result - ]; + // $response = [ + // 'position' => $currentPlace->getPosition()->toArray(), + // 'result' => $result + // ]; + $response['position'] = $currentPlace->getPosition()->toArray(); + $response['result'] = $result; // update round $nextRound = $currentRound + 1; @@ -331,6 +349,9 @@ class GameFlowController ]; } + + + $this->saveVisit($currentPlace->getId()); return new JsonContent($response); diff --git a/src/Database/Query/Select.php b/src/Database/Query/Select.php index 67f3fae..d62bd84 100644 --- a/src/Database/Query/Select.php +++ b/src/Database/Query/Select.php @@ -323,10 +323,6 @@ class Select $value = $this->generateColumn($value); }); - if(count($columns) > 0 && $columns[0] == Utils::backtick('id')) { - $columns[0] = Utils::backtick($this->table) . '.' . Utils::backtick('id'); - } - return implode(',', $columns); } diff --git a/src/PersistentData/PersistentDataManager.php b/src/PersistentData/PersistentDataManager.php index 126f695..8aa8bc1 100644 --- a/src/PersistentData/PersistentDataManager.php +++ b/src/PersistentData/PersistentDataManager.php @@ -133,6 +133,10 @@ class PersistentDataManager $table = call_user_func([$type, 'getTable']); $fields = call_user_func([$type, 'getFields']); + array_walk($fields, function (&$value, $key, $table) { + $value = [$table, $value]; + }, $table); + $select->from($table); //TODO: only with some relations? diff --git a/src/Repository/GuessRepository.php b/src/Repository/GuessRepository.php index 7035c7b..f04c67f 100644 --- a/src/Repository/GuessRepository.php +++ b/src/Repository/GuessRepository.php @@ -39,7 +39,7 @@ class GuessRepository return $this->pdm->selectFromDb($select, Guess::class); } - public function getAllInChallenge(int $userId, Challenge $challenge): Generator + public function getAllInChallengeByUser(int $userId, Challenge $challenge): Generator { $select = new Select(\Container::$dbConnection); $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']); @@ -49,4 +49,25 @@ class GuessRepository yield from $this->pdm->selectMultipleFromDb($select, Guess::class); } + + public function getAllInChallenge(Challenge $challenge): Generator + { + $select = new Select(\Container::$dbConnection); + $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); + $select->where('challenge_id', '=', $challenge->getId()); + + yield from $this->pdm->selectMultipleFromDb($select, Guess::class); + } + + public function getAllInChallengeByRound(int $round, Challenge $challenge): Generator + { + $order = $round + 1; + + $select = new Select(\Container::$dbConnection); + $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); + $select->where('challenge_id', '=', $challenge->getId()); + $select->where('order', '=', $order); + + yield from $this->pdm->selectMultipleFromDb($select, Guess::class); + } } diff --git a/src/Repository/MapRepository.php b/src/Repository/MapRepository.php index 5514ee5..6b41dc0 100644 --- a/src/Repository/MapRepository.php +++ b/src/Repository/MapRepository.php @@ -22,10 +22,7 @@ class MapRepository public function getByPlace(Place $place): ?Map { - $select = new Select(\Container::$dbConnection); - $select->where('id', '=', $place->getMapId()); - - return $this->pdm->selectFromDb($select, Map::class); + return $this->getById($place->getMapId()); } public function getByChallenge(Challenge $challenge): ?Map diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index be96c79..d9ed9bd 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -3,6 +3,7 @@ use DateTime; use Generator; use MapGuesser\Database\Query\Select; +use MapGuesser\PersistentData\Model\Guess; use MapGuesser\PersistentData\Model\User; use MapGuesser\PersistentData\PersistentDataManager; @@ -44,4 +45,9 @@ class UserRepository yield from $this->pdm->selectMultipleFromDb($select, User::class); } + + public function getByGuess(Guess $guess): ?User + { + return $this->getById($guess->getUserId()); + } } -- 2.45.2 From 69964acfb28f2f42290294fd9c0a759824123f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Thu, 13 May 2021 19:48:25 +0200 Subject: [PATCH 07/41] MAPG-235 results of other players shown --- public/static/js/game.js | 94 ++++++++++++++++++++++++++- src/Controller/GameFlowController.php | 64 +++++++++++++++--- src/Repository/GuessRepository.php | 1 + 3 files changed, 148 insertions(+), 11 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index 13f4e6b..43918be 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -19,6 +19,7 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); guessMarker: null, adaptGuess: false, googleLink: null, + history: [], readyToContinue: true, timeoutEnd: null, @@ -294,12 +295,23 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); return; } + Game.history = this.response.history; + for (var i = 0; i < this.response.history.length; ++i) { var round = this.response.history[i]; Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); Game.addPositionToResultMap(true); - Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); + if (round.result.guessPosition) { + Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); + } Game.scoreSum += round.result.score; + + for (var j = 0; j < round.allResults.length; ++j) { + var result = round.allResults[j]; + if (result.guessPosition) { + Game.addGuessPositionToResultMap(result.guessPosition, result, true); + } + } } if (this.response.finished) { @@ -598,12 +610,57 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); return; } + Game.history = this.response.history; + Game.receiveResult(this.response.position, guessPosition, this.response.result, this.response.allResults); if (this.response.place) { Game.panoId = this.response.place.panoId; Game.pov = this.response.place.pov; } + + // if(this.response.history) { + + // // game finished + // // delete everything but the last round + // for (var i = 0; i < Game.rounds.length -1; ++i) { + // var round = Game.rounds[i]; + + // if (round.realMarker) { + // round.realMarker.setMap(null); + // } + // for (var j = 0; j < round.guessMarkers.length; ++j) { + // var guessMarker = round.guessMarkers[j]; + // guessMarker.marker.setMap(null); + // guessMarker.line.setMap(null); + // if (guessMarker.info) { + // guessMarker.info.close(); + // } + // } + // } + + // // Game.rounds = []; + + // for (var i = 0; i < this.response.history.length; ++i) { + // var round = this.response.history[i]; + // Game.rounds[i] = { position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }; + // Game.addPositionToResultMap(true); + // if (round.result.guessPosition) { + // Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); + // } + // Game.scoreSum += round.result.score; + + // for (var j = 0; j < round.allResults.length; ++j) { + // var result = round.allResults[j]; + // if (result.guessPosition) { + // Game.addGuessPositionToResultMap(result.guessPosition, result, true); + // } + // } + // } + + // } + + }, data); }, @@ -717,6 +774,41 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); }, showSummary: function () { + for (var i = 0; i < Game.rounds.length; ++i) { + var round = Game.rounds[i]; + + if (round.realMarker) { + round.realMarker.setMap(null); + } + for (var j = 0; j < round.guessMarkers.length; ++j) { + var guessMarker = round.guessMarkers[j]; + guessMarker.marker.setMap(null); + guessMarker.line.setMap(null); + if (guessMarker.info) { + guessMarker.info.close(); + } + } + } + Game.rounds = []; + + for (var i = 0; i < Game.history.length; ++i) { + var round = Game.history[i]; + Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); + Game.addPositionToResultMap(true); + if (round.result.guessPosition) { + Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); + } + Game.scoreSum += round.result.score; + + for (var j = 0; j < round.allResults.length; ++j) { + var result = round.allResults[j]; + if (result.guessPosition) { + Game.addGuessPositionToResultMap(result.guessPosition, result, true); + } + } + } + + var distanceInfo = document.getElementById('distanceInfo'); distanceInfo.children[0].style.display = 'none'; distanceInfo.children[1].style.display = 'none'; diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index b8dda00..4b3c648 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -160,19 +160,34 @@ class GameFlowController $response['history'] = []; - $guesses = iterator_to_array($this->guessRepository->getAllInChallengeByUser($userId, $challenge)); + $guessesByUser = iterator_to_array($this->guessRepository->getAllInChallengeByUser($userId, $challenge)); $places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge)); + + + for($i = 0; $i < $currentRound; ++$i) { - for($i = 0; $i < $currentRound; ++$i) - { $response['history'][] = [ 'position' => $places[$i]->getPosition()->toArray(), 'result' => [ - 'guessPosition' => $guesses[$i]->getPosition()->toArray(), - 'distance' => $guesses[$i]->getDistance(), - 'score' => $guesses[$i]->getScore() - ] + 'guessPosition' => $guessesByUser[$i]->getPosition()->toArray(), + 'distance' => $guessesByUser[$i]->getDistance(), + 'score' => $guessesByUser[$i]->getScore() + ], + 'allResults' => [] ]; + + foreach($this->guessRepository->getAllInChallengeByRound($i, $challenge) as $guess) { + if($guess->getUserId() != $userId) { + $user = $this->userRepository->getByGuess($guess); + + $response['history'][$i]['allResults'][] = [ + 'userName' => $user->getDisplayName(), + 'guessPosition' => $guess->getPosition()->toArray(), + 'distance' => $guess->getDistance(), + 'score' => $guess->getScore() + ]; + } + } } if(!isset($currentPlace)) { // game finished @@ -347,11 +362,40 @@ class GameFlowController 'panoId' => $nextPlace->getPanoIdCached(), 'pov' => $nextPlace->getPov()->toArray() ]; + + } else { + + $guessesByUser = iterator_to_array($this->guessRepository->getAllInChallengeByUser($userId, $challenge)); + $places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge)); + + for($i = 0; $i < $nextRound; ++$i) { + + $response['history'][] = [ + 'position' => $places[$i]->getPosition()->toArray(), + 'result' => [ + 'guessPosition' => $guessesByUser[$i]->getPosition()->toArray(), + 'distance' => $guessesByUser[$i]->getDistance(), + 'score' => $guessesByUser[$i]->getScore() + ], + 'allResults' => [] + ]; + + foreach($this->guessRepository->getAllInChallengeByRound($i, $challenge) as $guess) { + // if($guess->getUserId() != $userId) { + $user = $this->userRepository->getByGuess($guess); + + $response['history'][$i]['allResults'][] = [ + 'userName' => $user->getDisplayName(), + 'guessPosition' => $guess->getPosition()->toArray(), + 'distance' => $guess->getDistance(), + 'score' => $guess->getScore() + ]; + // } + } + } + } - - - $this->saveVisit($currentPlace->getId()); return new JsonContent($response); diff --git a/src/Repository/GuessRepository.php b/src/Repository/GuessRepository.php index f04c67f..71c467b 100644 --- a/src/Repository/GuessRepository.php +++ b/src/Repository/GuessRepository.php @@ -55,6 +55,7 @@ class GuessRepository $select = new Select(\Container::$dbConnection); $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); $select->where('challenge_id', '=', $challenge->getId()); + $select->orderBy('order'); yield from $this->pdm->selectMultipleFromDb($select, Guess::class); } -- 2.45.2 From 21e41b7c362e64d6c65edb0f69d9f409e19c42f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Thu, 13 May 2021 19:55:32 +0200 Subject: [PATCH 08/41] MAP-235 fixed overlapping markers of history and user's guesses --- src/Controller/GameFlowController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index 4b3c648..aa0de1b 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -381,7 +381,7 @@ class GameFlowController ]; foreach($this->guessRepository->getAllInChallengeByRound($i, $challenge) as $guess) { - // if($guess->getUserId() != $userId) { + if($guess->getUserId() != $userId) { $user = $this->userRepository->getByGuess($guess); $response['history'][$i]['allResults'][] = [ @@ -390,7 +390,7 @@ class GameFlowController 'distance' => $guess->getDistance(), 'score' => $guess->getScore() ]; - // } + } } } -- 2.45.2 From 00afd65d75383a94661283c18b062e9f6cf156de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Sat, 15 May 2021 11:32:41 +0200 Subject: [PATCH 09/41] MAP-235 queries with relations on any recursion level implemented --- src/Controller/GameController.php | 3 +- src/Controller/GameFlowController.php | 16 ++-- src/PersistentData/PersistentDataManager.php | 85 ++++++++++++++++---- src/Repository/GuessRepository.php | 4 +- src/Repository/PlaceRepository.php | 9 +++ src/Repository/UserInChallengeRepository.php | 11 +++ 6 files changed, 102 insertions(+), 26 deletions(-) diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index b17aec2..788bfe0 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -143,7 +143,7 @@ class GameController // select places $mapId = (int) $this->request->post('mapId'); - $map = $this->mapRepository->getById($mapId); + // $map = $this->mapRepository->getById($mapId); $places = $this->placeRepository->getRandomNPlaces($mapId, static::NUMBER_OF_ROUNDS, $userId); @@ -242,6 +242,7 @@ class GameController $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str); if(!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) { + // new player is joining $userInChallenge = new UserInChallenge(); $userInChallenge->setUserId($userId); $userInChallenge->setChallenge($challenge); diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index aa0de1b..7043afd 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -146,13 +146,13 @@ class GameFlowController $session = $this->request->session(); $userId = $session->get('userId'); $challengeToken_str = $this->request->query('challengeToken'); - $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str); + $userInChallenge = $this->userInChallengeRepository->getByUserIdAndToken($userId, $challengeToken_str); - if (!isset($challenge)) { + if (!isset($userInChallenge)) { return new JsonContent(['error' => 'game_not_found']); } - $userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge); + $challenge = $userInChallenge->getChallenge(); $currentRound = $userInChallenge->getRound(); $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); @@ -160,6 +160,7 @@ class GameFlowController $response['history'] = []; + $allGuessesInChallenge = iterator_to_array($this->guessRepository->getAllInChallenge($challenge)); $guessesByUser = iterator_to_array($this->guessRepository->getAllInChallengeByUser($userId, $challenge)); $places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge)); @@ -205,6 +206,9 @@ class GameFlowController public function guess(): IContent { + // testing + $testPlace = $this->placeRepository->getByIdWithMap(5); + $mapId = (int) $this->request->query('mapId'); $session = $this->request->session(); @@ -305,13 +309,13 @@ class GameFlowController $session = $this->request->session(); $userId = $session->get('userId'); $challengeToken_str = $this->request->query('challengeToken'); - $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str); + $userInChallenge = $this->userInChallengeRepository->getByUserIdAndToken($userId, $challengeToken_str); - if (!isset($challenge)) { + if (!isset($userInChallenge)) { return new JsonContent(['error' => 'game_not_found']); } - $userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge); + $challenge = $userInChallenge->getChallenge(); $currentRound = $userInChallenge->getRound(); $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); $map = $this->mapRepository->getByPlace($currentPlace); diff --git a/src/PersistentData/PersistentDataManager.php b/src/PersistentData/PersistentDataManager.php index 8aa8bc1..d89ce54 100644 --- a/src/PersistentData/PersistentDataManager.php +++ b/src/PersistentData/PersistentDataManager.php @@ -45,24 +45,62 @@ class PersistentDataManager return $this->selectFromDb($select, $type, $withRelations); } - public function fillWithData(array $data, Model $model): void + public function fillWithData(array &$data, Model $model, ?string $modelKey = null): void { $relations = $model::getRelations(); $relationData = []; - foreach ($data as $key => $value) { - if ($this->extractRelationData($key, $value, $relationData, $relations)) { - continue; - } + while (key($data)) { + $key = key($data); + $value = current($data); + $relation = key($relations); - $method = 'set' . str_replace('_', '', ucwords($key, '_')); + if (strpos($key, '__') == false) { + $method = 'set' . str_replace('_', '', ucwords($key, '_')); - if (method_exists($model, $method)) { - $model->$method($value); + if (method_exists($model, $method)) { + $model->$method($value); + } + + next($data); + } else if (isset($modelKey) && substr($key, 0, strlen($modelKey . '__')) === $modelKey . '__') { + $key = substr($key, strlen($modelKey) + 2); + + $method = 'set' . str_replace('_', '', ucwords($key, '_')); + + if (method_exists($model, $method)) { + $model->$method($value); + } + + next($data); + } else if (substr($key, 0, strlen($relation . '__')) === $relation . '__') { + // $relation = current($relations); + $relationType = current($relations); + $relationModel = new $relationType(); + $this->fillWithData($data, $relationModel, $relation); + + $method = 'set' . str_replace('_', '', ucwords($relation, '_')); + $model->$method($relationModel); + + next($relations); + } else { + return; } } - $this->setRelations($model, $relationData); + // foreach ($data as $key => $value) { + // if ($this->extractRelationData($key, $value, $relationData, $relations)) { + // continue; + // } + + // $method = 'set' . str_replace('_', '', ucwords($key, '_')); + + // if (method_exists($model, $method)) { + // $model->$method($value); + // } + // } + + // $this->setRelations($model, $relationData); $model->saveSnapshot(); } @@ -133,9 +171,15 @@ class PersistentDataManager $table = call_user_func([$type, 'getTable']); $fields = call_user_func([$type, 'getFields']); - array_walk($fields, function (&$value, $key, $table) { - $value = [$table, $value]; - }, $table); + // array_walk($fields, function (&$value, $key, $table) { + // $value = [$table, $value]; + // }, $table); + + $columns = []; + + foreach ($fields as $field) { + $columns[] = [$table, $field]; + } $select->from($table); @@ -143,18 +187,19 @@ class PersistentDataManager if ($withRelations) { $relations = call_user_func([$type, 'getRelations']); - $columns = []; + // $columns = []; - foreach ($fields as $field) { - $columns[] = [$table, $field]; - } + // foreach ($fields as $field) { + // $columns[] = [$table, $field]; + // } $columns = array_merge($columns, $this->getRelationColumns($relations)); $this->leftJoinRelations($select, $table, $relations); $select->columns($columns); } else { - $select->columns($fields); + // $select->columns($fields); + $select->columns($columns); } return $select; @@ -169,6 +214,9 @@ class PersistentDataManager foreach (call_user_func([$relationType, 'getFields']) as $relationField) { $columns[] = [$relationTable, $relationField, $relation . '__' . $relationField]; } + + $nextOrderRelations = call_user_func([$relationType, 'getRelations']); + $columns = array_merge($columns, $this->getRelationColumns($nextOrderRelations)); } return $columns; @@ -179,6 +227,9 @@ class PersistentDataManager foreach ($relations as $relation => $relationType) { $relationTable = call_user_func([$relationType, 'getTable']); $select->leftJoin($relationTable, [$relationTable, 'id'], '=', [$table, $relation . '_id']); + + $nextOrderRelations = call_user_func([$relationType, 'getRelations']); + $this->leftJoinRelations($select, $relationTable, $nextOrderRelations); } } diff --git a/src/Repository/GuessRepository.php b/src/Repository/GuessRepository.php index 71c467b..b99fa90 100644 --- a/src/Repository/GuessRepository.php +++ b/src/Repository/GuessRepository.php @@ -53,11 +53,11 @@ class GuessRepository public function getAllInChallenge(Challenge $challenge): Generator { $select = new Select(\Container::$dbConnection); - $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); + // $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); $select->where('challenge_id', '=', $challenge->getId()); $select->orderBy('order'); - yield from $this->pdm->selectMultipleFromDb($select, Guess::class); + yield from $this->pdm->selectMultipleFromDb($select, Guess::class, true); } public function getAllInChallengeByRound(int $round, Challenge $challenge): Generator diff --git a/src/Repository/PlaceRepository.php b/src/Repository/PlaceRepository.php index 7c7c548..9d072be 100644 --- a/src/Repository/PlaceRepository.php +++ b/src/Repository/PlaceRepository.php @@ -198,5 +198,14 @@ class PlaceRepository yield from $this->pdm->selectMultipleFromDb($select, Place::class); } + + public function getByIdWithMap(int $placeId): ?Place + { + $select = new Select(\Container::$dbConnection); + // $select->innerJoin('maps', ['maps', 'id'], '=', ['places', 'map_id']); + $select->where(['places', 'id'], '=', $placeId); + + return $this->pdm->selectFromDb($select, Place::class, true); + } } diff --git a/src/Repository/UserInChallengeRepository.php b/src/Repository/UserInChallengeRepository.php index 20ae0d5..3ebdd23 100644 --- a/src/Repository/UserInChallengeRepository.php +++ b/src/Repository/UserInChallengeRepository.php @@ -41,6 +41,17 @@ class UserInChallengeRepository return $this->pdm->selectFromDb($select, UserInChallenge::class); } + public function getByUserIdAndToken(int $userId, string $token_str): ?UserInChallenge + { + $token = hexdec($token_str); + + $select = new Select(\Container::$dbConnection); + $select->where('user_id', '=', $userId); + $select->where('token', '=', $token); + + return $this->pdm->selectFromDb($select, UserInChallenge::class, true); + } + public function isUserParticipatingInChallenge(int $userId, Challenge $challenge): bool { $select = new Select(\Container::$dbConnection, 'user_in_challenge'); -- 2.45.2 From 44df94eb9832b0f40e7d3ab8ddf5869222631f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Sat, 15 May 2021 11:32:41 +0200 Subject: [PATCH 10/41] MAPG-235 queries with relations on any recursion level implemented --- src/Controller/GameController.php | 3 +- src/Controller/GameFlowController.php | 16 ++-- src/PersistentData/PersistentDataManager.php | 85 ++++++++++++++++---- src/Repository/GuessRepository.php | 4 +- src/Repository/PlaceRepository.php | 9 +++ src/Repository/UserInChallengeRepository.php | 11 +++ 6 files changed, 102 insertions(+), 26 deletions(-) diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index b17aec2..788bfe0 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -143,7 +143,7 @@ class GameController // select places $mapId = (int) $this->request->post('mapId'); - $map = $this->mapRepository->getById($mapId); + // $map = $this->mapRepository->getById($mapId); $places = $this->placeRepository->getRandomNPlaces($mapId, static::NUMBER_OF_ROUNDS, $userId); @@ -242,6 +242,7 @@ class GameController $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str); if(!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) { + // new player is joining $userInChallenge = new UserInChallenge(); $userInChallenge->setUserId($userId); $userInChallenge->setChallenge($challenge); diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index aa0de1b..7043afd 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -146,13 +146,13 @@ class GameFlowController $session = $this->request->session(); $userId = $session->get('userId'); $challengeToken_str = $this->request->query('challengeToken'); - $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str); + $userInChallenge = $this->userInChallengeRepository->getByUserIdAndToken($userId, $challengeToken_str); - if (!isset($challenge)) { + if (!isset($userInChallenge)) { return new JsonContent(['error' => 'game_not_found']); } - $userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge); + $challenge = $userInChallenge->getChallenge(); $currentRound = $userInChallenge->getRound(); $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); @@ -160,6 +160,7 @@ class GameFlowController $response['history'] = []; + $allGuessesInChallenge = iterator_to_array($this->guessRepository->getAllInChallenge($challenge)); $guessesByUser = iterator_to_array($this->guessRepository->getAllInChallengeByUser($userId, $challenge)); $places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge)); @@ -205,6 +206,9 @@ class GameFlowController public function guess(): IContent { + // testing + $testPlace = $this->placeRepository->getByIdWithMap(5); + $mapId = (int) $this->request->query('mapId'); $session = $this->request->session(); @@ -305,13 +309,13 @@ class GameFlowController $session = $this->request->session(); $userId = $session->get('userId'); $challengeToken_str = $this->request->query('challengeToken'); - $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str); + $userInChallenge = $this->userInChallengeRepository->getByUserIdAndToken($userId, $challengeToken_str); - if (!isset($challenge)) { + if (!isset($userInChallenge)) { return new JsonContent(['error' => 'game_not_found']); } - $userInChallenge = $this->userInChallengeRepository->getByUserIdAndChallenge($userId, $challenge); + $challenge = $userInChallenge->getChallenge(); $currentRound = $userInChallenge->getRound(); $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); $map = $this->mapRepository->getByPlace($currentPlace); diff --git a/src/PersistentData/PersistentDataManager.php b/src/PersistentData/PersistentDataManager.php index 8aa8bc1..d89ce54 100644 --- a/src/PersistentData/PersistentDataManager.php +++ b/src/PersistentData/PersistentDataManager.php @@ -45,24 +45,62 @@ class PersistentDataManager return $this->selectFromDb($select, $type, $withRelations); } - public function fillWithData(array $data, Model $model): void + public function fillWithData(array &$data, Model $model, ?string $modelKey = null): void { $relations = $model::getRelations(); $relationData = []; - foreach ($data as $key => $value) { - if ($this->extractRelationData($key, $value, $relationData, $relations)) { - continue; - } + while (key($data)) { + $key = key($data); + $value = current($data); + $relation = key($relations); - $method = 'set' . str_replace('_', '', ucwords($key, '_')); + if (strpos($key, '__') == false) { + $method = 'set' . str_replace('_', '', ucwords($key, '_')); - if (method_exists($model, $method)) { - $model->$method($value); + if (method_exists($model, $method)) { + $model->$method($value); + } + + next($data); + } else if (isset($modelKey) && substr($key, 0, strlen($modelKey . '__')) === $modelKey . '__') { + $key = substr($key, strlen($modelKey) + 2); + + $method = 'set' . str_replace('_', '', ucwords($key, '_')); + + if (method_exists($model, $method)) { + $model->$method($value); + } + + next($data); + } else if (substr($key, 0, strlen($relation . '__')) === $relation . '__') { + // $relation = current($relations); + $relationType = current($relations); + $relationModel = new $relationType(); + $this->fillWithData($data, $relationModel, $relation); + + $method = 'set' . str_replace('_', '', ucwords($relation, '_')); + $model->$method($relationModel); + + next($relations); + } else { + return; } } - $this->setRelations($model, $relationData); + // foreach ($data as $key => $value) { + // if ($this->extractRelationData($key, $value, $relationData, $relations)) { + // continue; + // } + + // $method = 'set' . str_replace('_', '', ucwords($key, '_')); + + // if (method_exists($model, $method)) { + // $model->$method($value); + // } + // } + + // $this->setRelations($model, $relationData); $model->saveSnapshot(); } @@ -133,9 +171,15 @@ class PersistentDataManager $table = call_user_func([$type, 'getTable']); $fields = call_user_func([$type, 'getFields']); - array_walk($fields, function (&$value, $key, $table) { - $value = [$table, $value]; - }, $table); + // array_walk($fields, function (&$value, $key, $table) { + // $value = [$table, $value]; + // }, $table); + + $columns = []; + + foreach ($fields as $field) { + $columns[] = [$table, $field]; + } $select->from($table); @@ -143,18 +187,19 @@ class PersistentDataManager if ($withRelations) { $relations = call_user_func([$type, 'getRelations']); - $columns = []; + // $columns = []; - foreach ($fields as $field) { - $columns[] = [$table, $field]; - } + // foreach ($fields as $field) { + // $columns[] = [$table, $field]; + // } $columns = array_merge($columns, $this->getRelationColumns($relations)); $this->leftJoinRelations($select, $table, $relations); $select->columns($columns); } else { - $select->columns($fields); + // $select->columns($fields); + $select->columns($columns); } return $select; @@ -169,6 +214,9 @@ class PersistentDataManager foreach (call_user_func([$relationType, 'getFields']) as $relationField) { $columns[] = [$relationTable, $relationField, $relation . '__' . $relationField]; } + + $nextOrderRelations = call_user_func([$relationType, 'getRelations']); + $columns = array_merge($columns, $this->getRelationColumns($nextOrderRelations)); } return $columns; @@ -179,6 +227,9 @@ class PersistentDataManager foreach ($relations as $relation => $relationType) { $relationTable = call_user_func([$relationType, 'getTable']); $select->leftJoin($relationTable, [$relationTable, 'id'], '=', [$table, $relation . '_id']); + + $nextOrderRelations = call_user_func([$relationType, 'getRelations']); + $this->leftJoinRelations($select, $relationTable, $nextOrderRelations); } } diff --git a/src/Repository/GuessRepository.php b/src/Repository/GuessRepository.php index 71c467b..b99fa90 100644 --- a/src/Repository/GuessRepository.php +++ b/src/Repository/GuessRepository.php @@ -53,11 +53,11 @@ class GuessRepository public function getAllInChallenge(Challenge $challenge): Generator { $select = new Select(\Container::$dbConnection); - $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); + // $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); $select->where('challenge_id', '=', $challenge->getId()); $select->orderBy('order'); - yield from $this->pdm->selectMultipleFromDb($select, Guess::class); + yield from $this->pdm->selectMultipleFromDb($select, Guess::class, true); } public function getAllInChallengeByRound(int $round, Challenge $challenge): Generator diff --git a/src/Repository/PlaceRepository.php b/src/Repository/PlaceRepository.php index 7c7c548..9d072be 100644 --- a/src/Repository/PlaceRepository.php +++ b/src/Repository/PlaceRepository.php @@ -198,5 +198,14 @@ class PlaceRepository yield from $this->pdm->selectMultipleFromDb($select, Place::class); } + + public function getByIdWithMap(int $placeId): ?Place + { + $select = new Select(\Container::$dbConnection); + // $select->innerJoin('maps', ['maps', 'id'], '=', ['places', 'map_id']); + $select->where(['places', 'id'], '=', $placeId); + + return $this->pdm->selectFromDb($select, Place::class, true); + } } diff --git a/src/Repository/UserInChallengeRepository.php b/src/Repository/UserInChallengeRepository.php index 20ae0d5..3ebdd23 100644 --- a/src/Repository/UserInChallengeRepository.php +++ b/src/Repository/UserInChallengeRepository.php @@ -41,6 +41,17 @@ class UserInChallengeRepository return $this->pdm->selectFromDb($select, UserInChallenge::class); } + public function getByUserIdAndToken(int $userId, string $token_str): ?UserInChallenge + { + $token = hexdec($token_str); + + $select = new Select(\Container::$dbConnection); + $select->where('user_id', '=', $userId); + $select->where('token', '=', $token); + + return $this->pdm->selectFromDb($select, UserInChallenge::class, true); + } + public function isUserParticipatingInChallenge(int $userId, Challenge $challenge): bool { $select = new Select(\Container::$dbConnection, 'user_in_challenge'); -- 2.45.2 From 7792f1c3ff1bafdc63ba49f0bc358f83dc1e60b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Sun, 16 May 2021 13:14:00 +0200 Subject: [PATCH 11/41] MAPG-235 refactor: renamed fields in challenge related tables --- .../structure/20210510_2000_challenge.sql | 8 ++++---- src/Controller/GameController.php | 10 +++++----- src/Controller/GameFlowController.php | 6 +++--- src/PersistentData/Model/Challenge.php | 14 +++++++------- src/PersistentData/Model/PlaceInChallenge.php | 12 ++++++------ src/PersistentData/Model/UserInChallenge.php | 12 ++++++------ src/Repository/GuessRepository.php | 8 +++----- src/Repository/PlaceRepository.php | 4 ++-- 8 files changed, 36 insertions(+), 38 deletions(-) diff --git a/database/migrations/structure/20210510_2000_challenge.sql b/database/migrations/structure/20210510_2000_challenge.sql index 626e6d5..0b9a657 100644 --- a/database/migrations/structure/20210510_2000_challenge.sql +++ b/database/migrations/structure/20210510_2000_challenge.sql @@ -1,7 +1,7 @@ CREATE TABLE `challenges` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `token` int(10) unsigned NOT NULL, - `timer_sec` int(10) unsigned, + `time_limit` int(10) unsigned, `no_move` tinyint(1) NOT NULL DEFAULT 0, `no_pan` tinyint(1) NOT NULL DEFAULT 0, `no_zoom` tinyint(1) NOT NULL DEFAULT 0, @@ -13,7 +13,7 @@ CREATE TABLE `user_in_challenge` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(10) unsigned NOT NULL, `challenge_id` int(10) unsigned NOT NULL, - `round` smallint(5) signed NOT NULL DEFAULT 0, + `current_round` smallint(5) signed NOT NULL DEFAULT 0, `time_left` int(10) unsigned, `is_owner` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), @@ -27,13 +27,13 @@ CREATE TABLE `place_in_challenge` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `place_id` int(10) unsigned NOT NULL, `challenge_id` int(10) unsigned NOT NULL, - `order` smallint(5) unsigned NOT NULL, + `round` smallint(5) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `place_id` (`place_id`), KEY `challenge_id` (`challenge_id`), CONSTRAINT `place_in_challenge_place_id` FOREIGN KEY (`place_id`) REFERENCES `places` (`id`), CONSTRAINT `place_in_challenge_challenge_id` FOREIGN KEY (`challenge_id`) REFERENCES `challenges` (`id`), - CONSTRAINT `unique_order_in_challenge` UNIQUE (`order`, `challenge_id`) + CONSTRAINT `unique_order_in_challenge` UNIQUE (`round`, `challenge_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; CREATE TABLE `guesses` ( diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index 788bfe0..9585622 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -113,7 +113,7 @@ class GameController $challenge->setCreatedDate(new DateTime()); if($this->request->post('timerEnabled') !== null && $this->request->post('timeLimit') !== null) { - $challenge->setTimerSec($this->request->post('timeLimit')); + $challenge->setTimeLimit($this->request->post('timeLimit')); } if($this->request->post('noMove') !== null) { $challenge->setNoMove(true); @@ -135,7 +135,7 @@ class GameController $userInChallenge = new UserInChallenge(); $userInChallenge->setUserId($userId); $userInChallenge->setChallenge($challenge); - $userInChallenge->setTimeLeft($challenge->getTimerSec()); + $userInChallenge->setTimeLeft($challenge->getTimeLimit()); $userInChallenge->setIsOwner(true); $this->pdm->saveToDb($userInChallenge); @@ -147,12 +147,12 @@ class GameController $places = $this->placeRepository->getRandomNPlaces($mapId, static::NUMBER_OF_ROUNDS, $userId); - $order = 1; + $round = 0; foreach ($places as $place) { $placeInChallenge = new PlaceInChallenge(); $placeInChallenge->setPlace($place); $placeInChallenge->setChallenge($challenge); - $placeInChallenge->setOrder($order++); + $placeInChallenge->setRound($round++); $this->pdm->saveToDb($placeInChallenge); } @@ -246,7 +246,7 @@ class GameController $userInChallenge = new UserInChallenge(); $userInChallenge->setUserId($userId); $userInChallenge->setChallenge($challenge); - $userInChallenge->setTimeLeft($challenge->getTimerSec()); + $userInChallenge->setTimeLeft($challenge->getTimeLimit()); $this->pdm->saveToDb($userInChallenge); } diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index 7043afd..f95ac11 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -153,7 +153,7 @@ class GameFlowController } $challenge = $userInChallenge->getChallenge(); - $currentRound = $userInChallenge->getRound(); + $currentRound = $userInChallenge->getCurrentRound(); $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); $response = []; @@ -316,7 +316,7 @@ class GameFlowController } $challenge = $userInChallenge->getChallenge(); - $currentRound = $userInChallenge->getRound(); + $currentRound = $userInChallenge->getCurrentRound(); $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); $map = $this->mapRepository->getByPlace($currentPlace); $placeInChallenge = $this->placeInChallengeRepository->getByPlaceAndChallenge($currentPlace, $challenge); @@ -355,7 +355,7 @@ class GameFlowController // update round $nextRound = $currentRound + 1; - $userInChallenge->setRound($nextRound); + $userInChallenge->setCurrentRound($nextRound); $this->pdm->saveToDb($userInChallenge); if ($nextRound < static::NUMBER_OF_ROUNDS) { diff --git a/src/PersistentData/Model/Challenge.php b/src/PersistentData/Model/Challenge.php index 11152f5..d947ae8 100644 --- a/src/PersistentData/Model/Challenge.php +++ b/src/PersistentData/Model/Challenge.php @@ -6,13 +6,13 @@ class Challenge extends Model { protected static string $table = 'challenges'; - protected static array $fields = ['token', 'timer_sec', 'no_move', 'no_pan', 'no_zoom', 'created']; + protected static array $fields = ['token', 'time_limit', 'no_move', 'no_pan', 'no_zoom', 'created']; protected static array $relations = []; private int $token; - private ?int $timerSec = null; + private ?int $timeLimit = null; private bool $noMove = false; @@ -27,10 +27,10 @@ class Challenge extends Model $this->token = $token; } - public function setTimerSec(?int $timerSec): void + public function setTimeLimit(?int $timeLimit): void { - if(isset($timerSec)) { - $this->timerSec = $timerSec; + if(isset($timeLimit)) { + $this->timeLimit = $timeLimit; } } @@ -64,9 +64,9 @@ class Challenge extends Model return $this->token; } - public function getTimerSec(): ?int + public function getTimeLimit(): ?int { - return $this->timerSec; + return $this->timeLimit; } public function getNoMove(): bool diff --git a/src/PersistentData/Model/PlaceInChallenge.php b/src/PersistentData/Model/PlaceInChallenge.php index b42fd9b..6bbe504 100644 --- a/src/PersistentData/Model/PlaceInChallenge.php +++ b/src/PersistentData/Model/PlaceInChallenge.php @@ -4,7 +4,7 @@ class PlaceInChallenge extends Model { protected static string $table = 'place_in_challenge'; - protected static array $fields = ['place_id', 'challenge_id', 'order']; + protected static array $fields = ['place_id', 'challenge_id', 'round']; protected static array $relations = ['place' => Place::class, 'challenge' => Challenge::class]; @@ -16,7 +16,7 @@ class PlaceInChallenge extends Model private ?int $challengeId = null; - private int $order; + private int $round; public function setPlace(Place $place): void { @@ -38,9 +38,9 @@ class PlaceInChallenge extends Model $this->challengeId = $challengeId; } - public function setOrder(int $order): void + public function setRound(int $round): void { - $this->order = $order; + $this->round = $round; } public function getPlace(): ?Place @@ -63,8 +63,8 @@ class PlaceInChallenge extends Model return $this->challengeId; } - public function getOrder(): int + public function getRound(): int { - return $this->order; + return $this->round; } } diff --git a/src/PersistentData/Model/UserInChallenge.php b/src/PersistentData/Model/UserInChallenge.php index dfb85b1..680e04c 100644 --- a/src/PersistentData/Model/UserInChallenge.php +++ b/src/PersistentData/Model/UserInChallenge.php @@ -4,7 +4,7 @@ class UserInChallenge extends Model { protected static string $table = 'user_in_challenge'; - protected static array $fields = ['user_id', 'challenge_id', 'round', 'time_left', 'is_owner']; + protected static array $fields = ['user_id', 'challenge_id', 'current_round', 'time_left', 'is_owner']; protected static array $relations = ['user' => User::class, 'challenge' => Challenge::class]; @@ -16,7 +16,7 @@ class UserInChallenge extends Model private ?int $challengeId = null; - private int $round = 0; + private int $currentRound = 0; private ?int $timeLeft = null; @@ -42,9 +42,9 @@ class UserInChallenge extends Model $this->challengeId = $challengeId; } - public function setRound(int $round): void + public function setCurrentRound(int $currentRound): void { - $this->round = $round; + $this->currentRound = $currentRound; } public function setTimeLeft(?int $timeLeft): void @@ -79,9 +79,9 @@ class UserInChallenge extends Model return $this->challengeId; } - public function getRound(): int + public function getCurrentRound(): int { - return $this->round; + return $this->currentRound; } public function getTimeLeft(): ?int diff --git a/src/Repository/GuessRepository.php b/src/Repository/GuessRepository.php index b99fa90..e3b1cb3 100644 --- a/src/Repository/GuessRepository.php +++ b/src/Repository/GuessRepository.php @@ -45,7 +45,7 @@ class GuessRepository $select->innerJoin('place_in_challenge', ['place_in_challenge', 'id'], '=', ['guesses', 'place_in_challenge_id']); $select->where('user_id', '=', $userId); $select->where('challenge_id', '=', $challenge->getId()); - $select->orderBy('order'); + $select->orderBy('round'); yield from $this->pdm->selectMultipleFromDb($select, Guess::class); } @@ -55,19 +55,17 @@ class GuessRepository $select = new Select(\Container::$dbConnection); // $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); $select->where('challenge_id', '=', $challenge->getId()); - $select->orderBy('order'); + $select->orderBy('round'); yield from $this->pdm->selectMultipleFromDb($select, Guess::class, true); } public function getAllInChallengeByRound(int $round, Challenge $challenge): Generator { - $order = $round + 1; - $select = new Select(\Container::$dbConnection); $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); $select->where('challenge_id', '=', $challenge->getId()); - $select->where('order', '=', $order); + $select->where('round', '=', $round); yield from $this->pdm->selectMultipleFromDb($select, Guess::class); } diff --git a/src/Repository/PlaceRepository.php b/src/Repository/PlaceRepository.php index 9d072be..aad8242 100644 --- a/src/Repository/PlaceRepository.php +++ b/src/Repository/PlaceRepository.php @@ -183,7 +183,7 @@ class PlaceRepository $select = new Select(\Container::$dbConnection); $select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']); $select->where('challenge_id', '=', $challenge->getId()); - $select->orderBy('order'); + $select->orderBy('round'); $select->limit(1, $round); return $this->pdm->selectFromDb($select, Place::class); @@ -194,7 +194,7 @@ class PlaceRepository $select = new Select(\Container::$dbConnection); $select->innerJoin('place_in_challenge', ['places', 'id'], '=', ['place_in_challenge', 'place_id']); $select->where('challenge_id', '=', $challenge->getId()); - $select->orderBy('order'); + $select->orderBy('round'); yield from $this->pdm->selectMultipleFromDb($select, Place::class); } -- 2.45.2 From 45ddb7f56a727d28ae0897f048337478e26e0b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Sun, 16 May 2021 20:46:53 +0200 Subject: [PATCH 12/41] MAPG-235 reduced number of queries for init and guess in challenge --- public/static/js/game.js | 43 +++-- src/Controller/GameFlowController.php | 160 ++++++------------ src/Repository/GuessRepository.php | 4 +- src/Repository/PlaceInChallengeRepository.php | 10 ++ 4 files changed, 95 insertions(+), 122 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index 43918be..3596486 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -297,19 +297,28 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); Game.history = this.response.history; - for (var i = 0; i < this.response.history.length; ++i) { - var round = this.response.history[i]; - Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); - Game.addPositionToResultMap(true); - if (round.result.guessPosition) { - Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); - } - Game.scoreSum += round.result.score; + if (this.response.history !== undefined) { + for (var i = 0; i < this.response.history.length; ++i) { + var round = this.response.history[i]; + + if (round.result) { + Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); + Game.addPositionToResultMap(true); + if (round.result.guessPosition) { + Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); + } + Game.scoreSum += round.result.score; + + + if (round.allResults !== undefined) { + for (var j = 0; j < round.allResults.length; ++j) { + var result = round.allResults[j]; + if (result.guessPosition) { + Game.addGuessPositionToResultMap(result.guessPosition, result, true); + } + } + } - for (var j = 0; j < round.allResults.length; ++j) { - var result = round.allResults[j]; - if (result.guessPosition) { - Game.addGuessPositionToResultMap(result.guessPosition, result, true); } } } @@ -800,10 +809,12 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); } Game.scoreSum += round.result.score; - for (var j = 0; j < round.allResults.length; ++j) { - var result = round.allResults[j]; - if (result.guessPosition) { - Game.addGuessPositionToResultMap(result.guessPosition, result, true); + if(round.allResults !== undefined) { + for (var j = 0; j < round.allResults.length; ++j) { + var result = round.allResults[j]; + if (result.guessPosition) { + Game.addGuessPositionToResultMap(result.guessPosition, result, true); + } } } } diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index f95ac11..e379f81 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -6,7 +6,7 @@ use MapGuesser\Util\Geo\Position; use MapGuesser\Response\JsonContent; use MapGuesser\Interfaces\Response\IContent; use MapGuesser\Multi\MultiConnector; -use MapGuesser\PersistentData\Model\UserInChallenge; +use MapGuesser\PersistentData\Model\Challenge; use MapGuesser\PersistentData\PersistentDataManager; use MapGuesser\PersistentData\Model\Guess; use MapGuesser\PersistentData\Model\UserPlayedPlace; @@ -141,6 +141,47 @@ class GameFlowController return new JsonContent(['ok' => true]); } + private function prepareChallengeResponse(int $userId, Challenge $challenge, int $currentRound): array + { + $allGuessesInChallenge = iterator_to_array($this->guessRepository->getAllInChallenge($challenge)); + + foreach ($allGuessesInChallenge as $guess) { + $round = $guess->getPlaceInChallenge()->getRound(); + + if ($guess->getUser()->getId() === $userId) { + $response['history'][$round]['position'] = + $guess->getPlaceInChallenge()->getPlace()->getPosition()->toArray(); + $response['history'][$round]['result'] = [ + 'guessPosition' => $guess->getPosition()->toArray(), + 'distance' => $guess->getDistance(), + 'score' => $guess->getScore() + ]; + } else { + $response['history'][$round]['allResults'][] = [ + 'userName' => $guess->getUser()->getDisplayName(), + 'guessPosition' => $guess->getPosition()->toArray(), + 'distance' => $guess->getDistance(), + 'score' => $guess->getScore() + ]; + } + } + + + $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); + + if(!isset($currentPlace)) { // game finished + $response['finished'] = true; + + } else { // continue game + $response['place'] = [ + 'panoId' => $currentPlace->getPanoIdCached(), + 'pov' => [$currentPlace->getPov()->toArray()] + ]; + } + + return $response; + } + public function challengeInitialData(): IContent { $session = $this->request->session(); @@ -154,52 +195,8 @@ class GameFlowController $challenge = $userInChallenge->getChallenge(); $currentRound = $userInChallenge->getCurrentRound(); - $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); - $response = []; - - $response['history'] = []; - - $allGuessesInChallenge = iterator_to_array($this->guessRepository->getAllInChallenge($challenge)); - $guessesByUser = iterator_to_array($this->guessRepository->getAllInChallengeByUser($userId, $challenge)); - $places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge)); - - - for($i = 0; $i < $currentRound; ++$i) { - - $response['history'][] = [ - 'position' => $places[$i]->getPosition()->toArray(), - 'result' => [ - 'guessPosition' => $guessesByUser[$i]->getPosition()->toArray(), - 'distance' => $guessesByUser[$i]->getDistance(), - 'score' => $guessesByUser[$i]->getScore() - ], - 'allResults' => [] - ]; - - foreach($this->guessRepository->getAllInChallengeByRound($i, $challenge) as $guess) { - if($guess->getUserId() != $userId) { - $user = $this->userRepository->getByGuess($guess); - - $response['history'][$i]['allResults'][] = [ - 'userName' => $user->getDisplayName(), - 'guessPosition' => $guess->getPosition()->toArray(), - 'distance' => $guess->getDistance(), - 'score' => $guess->getScore() - ]; - } - } - } - - if(!isset($currentPlace)) { // game finished - $response['finished'] = true; - - } else { // continue game - $response['place'] = [ - 'panoId' => $currentPlace->getPanoIdCached(), - 'pov' => [$currentPlace->getPov()->toArray()] - ]; - } + $response = $this->prepareChallengeResponse($userId, $challenge, $currentRound); return new JsonContent($response); } @@ -317,21 +314,10 @@ class GameFlowController $challenge = $userInChallenge->getChallenge(); $currentRound = $userInChallenge->getCurrentRound(); - $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); - $map = $this->mapRepository->getByPlace($currentPlace); - $placeInChallenge = $this->placeInChallengeRepository->getByPlaceAndChallenge($currentPlace, $challenge); + $currentPlaceInChallenge = $this->placeInChallengeRepository->getByRoundInChallenge($currentRound, $challenge); + $currentPlace = $currentPlaceInChallenge->getPlace(); + $map = $currentPlace->getMap(); - // fill in all other results for the round - $response['allResults'] = []; - foreach($this->guessRepository->getAllInChallengeByRound($currentRound, $challenge) as $guess) { - $user = $this->userRepository->getByGuess($guess); - $response['allResults'][] = [ - 'userName' => $user->getDisplayName(), - 'guessPosition' => $guess->getPosition()->toArray(), - 'distance' => $guess->getDistance(), - 'score' => $guess->getScore() - ]; - } $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); $result = $this->evaluateGuess($currentPlace->getPosition(), $guessPosition, $map->getArea()); @@ -339,18 +325,13 @@ class GameFlowController // save guess $guess = new Guess(); $guess->setUserId($userId); - $guess->setPlaceInChallenge($placeInChallenge); + $guess->setPlaceInChallenge($currentPlaceInChallenge); $guess->setPosition($guessPosition); $guess->setDistance($result['distance']); $guess->setScore($result['score']); $this->pdm->saveToDb($guess); - // $response = [ - // 'position' => $currentPlace->getPosition()->toArray(), - // 'result' => $result - // ]; - $response['position'] = $currentPlace->getPosition()->toArray(); - $response['result'] = $result; + // update round $nextRound = $currentRound + 1; @@ -358,47 +339,18 @@ class GameFlowController $userInChallenge->setCurrentRound($nextRound); $this->pdm->saveToDb($userInChallenge); - if ($nextRound < static::NUMBER_OF_ROUNDS) { - $nextPlace = $this->placeRepository->getByRoundInChallenge($challenge, $nextRound); + // creating response + $response = $this->prepareChallengeResponse($userId, $challenge, $nextRound); - $response['place'] = [ - 'panoId' => $nextPlace->getPanoIdCached(), - 'pov' => $nextPlace->getPov()->toArray() - ]; - - } else { - - $guessesByUser = iterator_to_array($this->guessRepository->getAllInChallengeByUser($userId, $challenge)); - $places = iterator_to_array($this->placeRepository->getAllInChallenge($challenge)); - - for($i = 0; $i < $nextRound; ++$i) { - - $response['history'][] = [ - 'position' => $places[$i]->getPosition()->toArray(), - 'result' => [ - 'guessPosition' => $guessesByUser[$i]->getPosition()->toArray(), - 'distance' => $guessesByUser[$i]->getDistance(), - 'score' => $guessesByUser[$i]->getScore() - ], - 'allResults' => [] - ]; - - foreach($this->guessRepository->getAllInChallengeByRound($i, $challenge) as $guess) { - if($guess->getUserId() != $userId) { - $user = $this->userRepository->getByGuess($guess); - - $response['history'][$i]['allResults'][] = [ - 'userName' => $user->getDisplayName(), - 'guessPosition' => $guess->getPosition()->toArray(), - 'distance' => $guess->getDistance(), - 'score' => $guess->getScore() - ]; - } - } - } + $response['position'] = $currentPlace->getPosition()->toArray(); + $response['result'] = $result; + if(isset($response['history'][$currentRound]['allResults'])) { + $response['allResults'] = $response['history'][$currentRound]['allResults']; } + + $this->saveVisit($currentPlace->getId()); diff --git a/src/Repository/GuessRepository.php b/src/Repository/GuessRepository.php index e3b1cb3..9e9cea8 100644 --- a/src/Repository/GuessRepository.php +++ b/src/Repository/GuessRepository.php @@ -63,10 +63,10 @@ class GuessRepository public function getAllInChallengeByRound(int $round, Challenge $challenge): Generator { $select = new Select(\Container::$dbConnection); - $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); + // $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); $select->where('challenge_id', '=', $challenge->getId()); $select->where('round', '=', $round); - yield from $this->pdm->selectMultipleFromDb($select, Guess::class); + yield from $this->pdm->selectMultipleFromDb($select, Guess::class, true); } } diff --git a/src/Repository/PlaceInChallengeRepository.php b/src/Repository/PlaceInChallengeRepository.php index d9865e6..c32c07e 100644 --- a/src/Repository/PlaceInChallengeRepository.php +++ b/src/Repository/PlaceInChallengeRepository.php @@ -40,4 +40,14 @@ class PlaceInChallengeRepository return $this->pdm->selectFromDb($select, PlaceInChallenge::class); } + + public function getByRoundInChallenge(int $round, Challenge $challenge): ?PlaceInChallenge + { + $select = new Select(\Container::$dbConnection); + $select->where('challenge_id', '=', $challenge->getId()); + $select->orderBy('round'); + $select->limit(1, $round); + + return $this->pdm->selectFromDb($select, PlaceInChallenge::class, true); + } } -- 2.45.2 From 30f4b7ad190eab672e9e3355600384fc458b8466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Mon, 17 May 2021 21:29:55 +0200 Subject: [PATCH 13/41] MAPG-235 fixed sending history to initializer and fixed pov data format --- src/Controller/GameFlowController.php | 62 ++++++++++++-------- src/PersistentData/PersistentDataManager.php | 2 +- src/Repository/GuessRepository.php | 2 - 3 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index e379f81..beab998 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -141,42 +141,54 @@ class GameFlowController return new JsonContent(['ok' => true]); } - private function prepareChallengeResponse(int $userId, Challenge $challenge, int $currentRound): array + private function prepareChallengeResponse(int $userId, Challenge $challenge, int $currentRound, bool $withHistory = false): array { - $allGuessesInChallenge = iterator_to_array($this->guessRepository->getAllInChallenge($challenge)); - - foreach ($allGuessesInChallenge as $guess) { - $round = $guess->getPlaceInChallenge()->getRound(); - - if ($guess->getUser()->getId() === $userId) { - $response['history'][$round]['position'] = - $guess->getPlaceInChallenge()->getPlace()->getPosition()->toArray(); - $response['history'][$round]['result'] = [ + $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); + + if (!isset($currentPlace) || $withHistory) { + foreach ($this->guessRepository->getAllInChallenge($challenge) as $guess) { + $round = $guess->getPlaceInChallenge()->getRound(); + + if ($guess->getUser()->getId() === $userId) { + $response['history'][$round]['position'] = + $guess->getPlaceInChallenge()->getPlace()->getPosition()->toArray(); + $response['history'][$round]['result'] = [ + 'guessPosition' => $guess->getPosition()->toArray(), + 'distance' => $guess->getDistance(), + 'score' => $guess->getScore() + ]; + } else { + $response['history'][$round]['allResults'][] = [ + 'userName' => $guess->getUser()->getDisplayName(), 'guessPosition' => $guess->getPosition()->toArray(), 'distance' => $guess->getDistance(), 'score' => $guess->getScore() - ]; - } else { - $response['history'][$round]['allResults'][] = [ - 'userName' => $guess->getUser()->getDisplayName(), - 'guessPosition' => $guess->getPosition()->toArray(), - 'distance' => $guess->getDistance(), - 'score' => $guess->getScore() - ]; + ]; + } } } - - $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); - - if(!isset($currentPlace)) { // game finished + if (!isset($currentPlace)) { // game finished $response['finished'] = true; - } else { // continue game $response['place'] = [ 'panoId' => $currentPlace->getPanoIdCached(), - 'pov' => [$currentPlace->getPov()->toArray()] + 'pov' => $currentPlace->getPov()->toArray() ]; + + $prevRound = $currentRound - 1; + if ($prevRound >= 0) { + foreach ($this->guessRepository->getAllInChallengeByRound($prevRound, $challenge) as $guess) { + if ($guess->getUser()->getId() != $userId) { + $response['allResults'][] = [ + 'userName' => $guess->getUser()->getDisplayName(), + 'guessPosition' => $guess->getPosition()->toArray(), + 'distance' => $guess->getDistance(), + 'score' => $guess->getScore() + ]; + } + } + } } return $response; @@ -196,7 +208,7 @@ class GameFlowController $challenge = $userInChallenge->getChallenge(); $currentRound = $userInChallenge->getCurrentRound(); - $response = $this->prepareChallengeResponse($userId, $challenge, $currentRound); + $response = $this->prepareChallengeResponse($userId, $challenge, $currentRound, true); return new JsonContent($response); } diff --git a/src/PersistentData/PersistentDataManager.php b/src/PersistentData/PersistentDataManager.php index d89ce54..56206d5 100644 --- a/src/PersistentData/PersistentDataManager.php +++ b/src/PersistentData/PersistentDataManager.php @@ -84,7 +84,7 @@ class PersistentDataManager next($relations); } else { - return; + break; } } diff --git a/src/Repository/GuessRepository.php b/src/Repository/GuessRepository.php index 9e9cea8..6eb0336 100644 --- a/src/Repository/GuessRepository.php +++ b/src/Repository/GuessRepository.php @@ -53,7 +53,6 @@ class GuessRepository public function getAllInChallenge(Challenge $challenge): Generator { $select = new Select(\Container::$dbConnection); - // $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); $select->where('challenge_id', '=', $challenge->getId()); $select->orderBy('round'); @@ -63,7 +62,6 @@ class GuessRepository public function getAllInChallengeByRound(int $round, Challenge $challenge): Generator { $select = new Select(\Container::$dbConnection); - // $select->innerJoin('place_in_challenge', ['guesses', 'place_in_challenge_id'], '=', ['place_in_challenge', 'id']); $select->where('challenge_id', '=', $challenge->getId()); $select->where('round', '=', $round); -- 2.45.2 From 069c6b37c834c7db86a63f77e0d21f021b2ccb6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Tue, 18 May 2021 18:06:02 +0200 Subject: [PATCH 14/41] MAPG-235 fixed bugs with displaying score and round --- public/static/js/game.js | 216 +++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 122 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index 3596486..0bb29de 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -295,72 +295,110 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); return; } - Game.history = this.response.history; - - if (this.response.history !== undefined) { - for (var i = 0; i < this.response.history.length; ++i) { - var round = this.response.history[i]; - - if (round.result) { - Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); - Game.addPositionToResultMap(true); - if (round.result.guessPosition) { - Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); - } - Game.scoreSum += round.result.score; - - - if (round.allResults !== undefined) { - for (var j = 0; j < round.allResults.length; ++j) { - var result = round.allResults[j]; - if (result.guessPosition) { - Game.addGuessPositionToResultMap(result.guessPosition, result, true); - } - } - } - - } - } - } + Game.loadHistory(this.response.history); if (this.response.finished) { - // TODO: refactor - it is necessary for mobile - if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') { - document.getElementById('showGuessButton').click(); - } - - if (Game.adaptGuess) { - document.getElementById('guess').classList.remove('adapt'); - } - - if (Game.guessMarker) { - Game.guessMarker.setMap(null); - Game.guessMarker = null; - } - - document.getElementById('guess').classList.add('result'); - - Game.map.setOptions({ - draggableCursor: 'grab' - }); - + Game.transitToResultMap(); Game.showSummary(); - document.getElementById('continueButton').style.display = 'none'; - return; - } - Game.panoId = this.response.place.panoId; - Game.pov = this.response.place.pov; + + } else { + + Game.panoId = this.response.place.panoId; + Game.pov = this.response.place.pov; + + Game.startNewRound(); + } document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS); document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE); - - Game.startNewRound(); }); }, + transitToResultMap: function() { + // TODO: refactor - it is necessary for mobile + if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') { + document.getElementById('showGuessButton').click(); + } + + if (Game.adaptGuess) { + document.getElementById('guess').classList.remove('adapt'); + } + + if (Game.guessMarker) { + Game.guessMarker.setMap(null); + Game.guessMarker = null; + } + + document.getElementById('guess').classList.add('result'); + + Game.map.setOptions({ + draggableCursor: 'grab' + }); + + if (Game.rounds.length === Game.NUMBER_OF_ROUNDS) { + document.getElementById('continueButton').style.display = 'none'; + document.getElementById('showSummaryButton').style.display = 'block'; + } else if (Game.type == GameType.MULTI) { + if (Game.multi.owner) { + if (!Game.readyToContinue) { + document.getElementById('continueButton').disabled = true; + } + } else { + document.getElementById('continueButton').style.display = 'none'; + } + } + }, + + loadHistory: function (history) { + if(!history) + return; + + Game.history = history; + + for (var i = 0; i < Game.rounds.length; ++i) { + var round = Game.rounds[i]; + + if (round.realMarker) { + round.realMarker.setMap(null); + } + for (var j = 0; j < round.guessMarkers.length; ++j) { + var guessMarker = round.guessMarkers[j]; + guessMarker.marker.setMap(null); + guessMarker.line.setMap(null); + if (guessMarker.info) { + guessMarker.info.close(); + } + } + } + Game.rounds = []; + + for (var i = 0; i < Game.history.length; ++i) { + var round = Game.history[i]; + + if (round.result) { + Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); + Game.addPositionToResultMap(true); + if (round.result.guessPosition) { + Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); + } + Game.scoreSum += round.result.score; + + + if(round.allResults !== undefined) { + for (var j = 0; j < round.allResults.length; ++j) { + var result = round.allResults[j]; + if (result.guessPosition) { + Game.addGuessPositionToResultMap(result.guessPosition, result, true); + } + } + } + } + } + }, + reset: function () { if (Game.guessMarker) { Game.guessMarker.setMap(null); @@ -542,25 +580,9 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); }, showResultMap: function (result, resultBounds) { - // TODO: refactor - it is necessary for mobile - if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') { - document.getElementById('showGuessButton').click(); - } + + Game.transitToResultMap(); - if (Game.adaptGuess) { - document.getElementById('guess').classList.remove('adapt'); - } - - if (Game.guessMarker) { - Game.guessMarker.setMap(null); - Game.guessMarker = null; - } - - document.getElementById('guess').classList.add('result'); - - Game.map.setOptions({ - draggableCursor: 'grab' - }); Game.map.fitBounds(resultBounds); var distanceInfo = document.getElementById('distanceInfo'); @@ -579,19 +601,6 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); var scoreBar = document.getElementById('scoreBar'); scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor; scoreBar.style.width = scoreBarProperties.width; - - if (Game.rounds.length === Game.NUMBER_OF_ROUNDS) { - document.getElementById('continueButton').style.display = 'none'; - document.getElementById('showSummaryButton').style.display = 'block'; - } else if (roomId) { - if (Game.multi.owner) { - if (!Game.readyToContinue) { - document.getElementById('continueButton').disabled = true; - } - } else { - document.getElementById('continueButton').style.display = 'none'; - } - } }, guess: function () { @@ -783,43 +792,6 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); }, showSummary: function () { - for (var i = 0; i < Game.rounds.length; ++i) { - var round = Game.rounds[i]; - - if (round.realMarker) { - round.realMarker.setMap(null); - } - for (var j = 0; j < round.guessMarkers.length; ++j) { - var guessMarker = round.guessMarkers[j]; - guessMarker.marker.setMap(null); - guessMarker.line.setMap(null); - if (guessMarker.info) { - guessMarker.info.close(); - } - } - } - Game.rounds = []; - - for (var i = 0; i < Game.history.length; ++i) { - var round = Game.history[i]; - Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); - Game.addPositionToResultMap(true); - if (round.result.guessPosition) { - Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); - } - Game.scoreSum += round.result.score; - - if(round.allResults !== undefined) { - for (var j = 0; j < round.allResults.length; ++j) { - var result = round.allResults[j]; - if (result.guessPosition) { - Game.addGuessPositionToResultMap(result.guessPosition, result, true); - } - } - } - } - - var distanceInfo = document.getElementById('distanceInfo'); distanceInfo.children[0].style.display = 'none'; distanceInfo.children[1].style.display = 'none'; -- 2.45.2 From 62251b1062f003e2d3ef9882eaafa38bd75077df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Tue, 18 May 2021 19:55:05 +0200 Subject: [PATCH 15/41] MAPG-235 created error handling for challenge access without login --- public/static/js/game.js | 70 +++++++------------------------ src/Controller/GameController.php | 13 +++++- 2 files changed, 28 insertions(+), 55 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index 0bb29de..eba1b40 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -1,6 +1,6 @@ 'use strict'; -const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); +const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); (function () { var Game = { @@ -215,8 +215,8 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); } }, - getGameIdentifier: function() { - switch(Game.type) { + getGameIdentifier: function () { + switch (Game.type) { case GameType.SINGLE: return '/game/' + mapId; case GameType.MULTI: @@ -303,7 +303,7 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); Game.showSummary(); document.getElementById('continueButton').style.display = 'none'; - + } else { Game.panoId = this.response.place.panoId; @@ -317,7 +317,7 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); }); }, - transitToResultMap: function() { + transitToResultMap: function () { // TODO: refactor - it is necessary for mobile if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') { document.getElementById('showGuessButton').click(); @@ -353,7 +353,7 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); }, loadHistory: function (history) { - if(!history) + if (!history) return; Game.history = history; @@ -385,9 +385,9 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); } Game.scoreSum += round.result.score; - - if(round.allResults !== undefined) { + + if (round.allResults !== undefined) { for (var j = 0; j < round.allResults.length; ++j) { var result = round.allResults[j]; if (result.guessPosition) { @@ -514,7 +514,11 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); break; case 'game_not_found': - MapGuesser.showModalWithContent('Error', 'The game room was not found by this ID. Please check the link.'); + MapGuesser.showModalWithContent('Error', 'The game was not found by this ID. Please check the link.'); + break; + + case 'anonymous_user': + MapGuesser.showModalWithContent('Error', 'You have to login to join a challenge!'); break; default: @@ -580,7 +584,7 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); }, showResultMap: function (result, resultBounds) { - + Game.transitToResultMap(); Game.map.fitBounds(resultBounds); @@ -637,48 +641,6 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); Game.pov = this.response.place.pov; } - // if(this.response.history) { - - // // game finished - // // delete everything but the last round - // for (var i = 0; i < Game.rounds.length -1; ++i) { - // var round = Game.rounds[i]; - - // if (round.realMarker) { - // round.realMarker.setMap(null); - // } - // for (var j = 0; j < round.guessMarkers.length; ++j) { - // var guessMarker = round.guessMarkers[j]; - // guessMarker.marker.setMap(null); - // guessMarker.line.setMap(null); - // if (guessMarker.info) { - // guessMarker.info.close(); - // } - // } - // } - - // // Game.rounds = []; - - // for (var i = 0; i < this.response.history.length; ++i) { - // var round = this.response.history[i]; - // Game.rounds[i] = { position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }; - // Game.addPositionToResultMap(true); - // if (round.result.guessPosition) { - // Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); - // } - // Game.scoreSum += round.result.score; - - // for (var j = 0; j < round.allResults.length; ++j) { - // var result = round.allResults[j]; - // if (result.guessPosition) { - // Game.addGuessPositionToResultMap(result.guessPosition, result, true); - // } - // } - // } - - // } - - }, data); }, @@ -995,9 +957,9 @@ const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2}); document.getElementById("compass").style.transform = "translateY(-50%) rotate(" + heading + "deg)"; }); - if(roomId !== null) { + if (roomId !== null) { Game.type = GameType.MULTI; - } else if(challengeToken !== null) { + } else if (challengeToken !== null) { Game.type = GameType.CHALLENGE; } diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index 9585622..a4d0210 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -97,7 +97,7 @@ class GameController } public function getChallenge(): IContent - { + { $challengeToken = $this->request->query('challengeToken'); return new HtmlContent('game', ['challengeToken' => $challengeToken]); @@ -239,8 +239,19 @@ class GameController $challengeToken_str = $this->request->query('challengeToken'); $session = $this->request->session(); $userId = $session->get('userId'); + + if (!isset($userId)) + { + return new JsonContent(['error' => 'anonymous_user']); + } + $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str); + if (!isset($challenge)) + { + return new JsonContent(['error' => 'game_not_found']); + } + if(!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) { // new player is joining $userInChallenge = new UserInChallenge(); -- 2.45.2 From 7898c5232895ae49208963e5c83024b6c3772315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Tue, 18 May 2021 20:22:43 +0200 Subject: [PATCH 16/41] MAPG-235 go back to start button added to the end of the challenge --- public/static/css/game.css | 4 ++++ public/static/js/game.js | 6 ++++-- views/game.php | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/public/static/css/game.css b/public/static/css/game.css index a23d889..b5488ea 100644 --- a/public/static/css/game.css +++ b/public/static/css/game.css @@ -153,6 +153,10 @@ z-index: 2; } +#goToStart { + display: none; +} + @media screen and (max-width: 599px) { #mapName { display: none; diff --git a/public/static/js/game.js b/public/static/js/game.js index eba1b40..abf5f77 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -301,7 +301,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); Game.transitToResultMap(); Game.showSummary(); - document.getElementById('continueButton').style.display = 'none'; + //document.getElementById('continueButton').style.display = 'none'; } else { @@ -763,11 +763,13 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); scoreInfo.children[1].style.display = 'block'; document.getElementById('showSummaryButton').style.display = null; - if (!roomId || Game.multi.owner) { + if (Game.type == GameType.SINGLE || Game.multi.owner) { document.getElementById('startNewGameButton').style.display = 'block'; if (!Game.readyToContinue) { document.getElementById('startNewGameButton').disabled = true; } + } else if (Game.type == GameType.CHALLENGE) { + document.getElementById('goToStart').style.display = 'block'; } var resultBounds = new google.maps.LatLngBounds(); diff --git a/views/game.php b/views/game.php index 8495112..8795d3a 100644 --- a/views/game.php +++ b/views/game.php @@ -57,6 +57,7 @@ +
-- 2.45.2 From 3b98570f6d7c57d0c1c412b607cbd32eae66829e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Wed, 19 May 2021 21:16:59 +0200 Subject: [PATCH 23/41] MAPG-235 noMove, noZoom, noPan restrictions implemented --- public/static/css/game.css | 13 ++++++++++++- public/static/css/maps.css | 1 + public/static/js/game.js | 21 +++++++++++++++++++-- src/Controller/GameFlowController.php | 7 +++++++ views/game.php | 1 + views/maps.php | 8 ++++---- 6 files changed, 44 insertions(+), 7 deletions(-) diff --git a/public/static/css/game.css b/public/static/css/game.css index b5488ea..b3def48 100644 --- a/public/static/css/game.css +++ b/public/static/css/game.css @@ -15,6 +15,17 @@ right: 0; background-color: #000000; opacity: 0.5; + z-index: 4; +} + +#panningBlockerCover { + display: none; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + opacity: 0; z-index: 3; } @@ -22,7 +33,7 @@ position: absolute; bottom: 30px; right: 20px; - z-index: 2; + z-index: 3; } #guess.result { diff --git a/public/static/css/maps.css b/public/static/css/maps.css index 8e32d05..acfb23d 100644 --- a/public/static/css/maps.css +++ b/public/static/css/maps.css @@ -77,6 +77,7 @@ div.mapItem>div.buttonContainer { #restrictions { margin-top: 1em; + margin-bottom: 1em; font-family: 'Roboto', sans-serif; } diff --git a/public/static/js/game.js b/public/static/js/game.js index abf5f77..4fe154d 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -301,11 +301,21 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); Game.transitToResultMap(); Game.showSummary(); - //document.getElementById('continueButton').style.display = 'none'; - } else { + if (this.response.restrictions) { + Game.panorama.setOptions({ + clickToGo: !this.response.restrictions.noMove, + linksControl: !this.response.restrictions.noMove, + scrollwheel: !this.response.restrictions.noZoom + }); + + if (this.response.restrictions.noPan) { + document.getElementById('panningBlockerCover').style.display = 'block'; + } + } + Game.panoId = this.response.place.panoId; Game.pov = this.response.place.pov; @@ -442,6 +452,13 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); // needs to be set visible after the show guess map hid it in mobile view document.getElementById("navigation").style.visibility = 'visible'; + Game.panorama.setOptions({ + clickToGo: true, + linksControl: true, + scrollwheel: true + }); + document.getElementById('panningBlockerCover').style.display = null; + Game.initialize(); }, diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index 71ea558..668e895 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -194,6 +194,13 @@ class GameFlowController } } } + + $response['restrictions'] = [ + 'timeLimit' => $challenge->getTimeLimit(), + 'noMove' => $challenge->getNoMove(), + 'noPan' => $challenge->getNoPan(), + 'noZoom' => $challenge->getNoZoom() + ]; } return $response; diff --git a/views/game.php b/views/game.php index 8795d3a..2c9624a 100644 --- a/views/game.php +++ b/views/game.php @@ -25,6 +25,7 @@

+
diff --git a/views/maps.php b/views/maps.php index dfb9d2f..c097e4c 100644 --- a/views/maps.php +++ b/views/maps.php @@ -65,14 +65,14 @@ TODO: condition!
-
- - -
+
+ + +
-- 2.45.2 From bbb66ca9797ad7aa2219252313eada7d7bc6a181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Thu, 20 May 2021 08:31:52 +0200 Subject: [PATCH 24/41] MAPG-235 challenge token related error handling --- src/Controller/GameController.php | 4 ++++ src/Repository/ChallengeRepository.php | 7 +++++++ src/Repository/UserInChallengeRepository.php | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index ecacef4..af5b293 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -107,6 +107,10 @@ class GameController { // create Challenge $challengeToken = rand(); + while ($this->challengeRepository->getByToken($challengeToken)) { + // if a challenge with the same token already exists + $challengeToken = rand(); + } $challenge = new Challenge(); $challenge->setToken($challengeToken); diff --git a/src/Repository/ChallengeRepository.php b/src/Repository/ChallengeRepository.php index 1da5ed0..bcce4d9 100644 --- a/src/Repository/ChallengeRepository.php +++ b/src/Repository/ChallengeRepository.php @@ -31,6 +31,13 @@ class ChallengeRepository public function getByTokenStr(string $token_str): ?Challenge { + // validate token string + foreach (str_split($token_str) as $char) { + if (!(('0' <= $char && $char <= '9') || ('a' <= $char && $char <= 'f'))) { + return null; + } + } + // convert token to int $token = hexdec($token_str); return $this->getByToken($token); diff --git a/src/Repository/UserInChallengeRepository.php b/src/Repository/UserInChallengeRepository.php index a033589..c059c82 100644 --- a/src/Repository/UserInChallengeRepository.php +++ b/src/Repository/UserInChallengeRepository.php @@ -48,6 +48,13 @@ class UserInChallengeRepository $withRelations = array_unique(array_merge($withRelations, $necessaryRelations)); } + // validate token string + foreach (str_split($token_str) as $char) { + if (!(('0' <= $char && $char <= '9') || ('a' <= $char && $char <= 'f'))) { + return null; + } + } + // convert token to int $token = hexdec($token_str); $select = new Select(\Container::$dbConnection); -- 2.45.2 From c2df9b77135b15b4a701c82c1f63112ec356cc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Thu, 20 May 2021 20:55:44 +0200 Subject: [PATCH 25/41] MAPG-235 timelimit restriction per round functionality implemented for challenges --- public/static/js/game.js | 88 ++++++++++++++++++--------- src/Controller/GameFlowController.php | 67 ++++++++++++-------- 2 files changed, 101 insertions(+), 54 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index 4fe154d..718efe1 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -20,6 +20,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); adaptGuess: false, googleLink: null, history: [], + restrictions: null, readyToContinue: true, timeoutEnd: null, @@ -304,17 +305,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); } else { - if (this.response.restrictions) { - Game.panorama.setOptions({ - clickToGo: !this.response.restrictions.noMove, - linksControl: !this.response.restrictions.noMove, - scrollwheel: !this.response.restrictions.noZoom - }); - - if (this.response.restrictions.noPan) { - document.getElementById('panningBlockerCover').style.display = 'block'; - } - } + Game.restrictions = this.response.restrictions; Game.panoId = this.response.place.panoId; Game.pov = this.response.place.pov; @@ -327,6 +318,40 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); }); }, + enableRestrictions: function () { + if (!Game.restrictions) { + return; + } + + Game.panorama.setOptions({ + clickToGo: !Game.restrictions.noMove, + linksControl: !(Game.restrictions.noMove || Game.restrictions.noPan), + scrollwheel: !Game.restrictions.noZoom + }); + + if (Game.restrictions.noPan) { + document.getElementById('panningBlockerCover').style.display = 'block'; + } + + if (Game.restrictions.timeLimit) { + Game.startCountdown(Game.restrictions.timeLimit, function() { + Game.guess(); + }); + } + }, + + disableRestrictions: function () { + Game.panorama.setOptions({ + clickToGo: true, + linksControl: true, + scrollwheel: true + }); + + document.getElementById('panningBlockerCover').style.display = null; + + Game.startCountdown(0); + }, + transitToResultMap: function () { // TODO: refactor - it is necessary for mobile if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') { @@ -452,11 +477,8 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); // needs to be set visible after the show guess map hid it in mobile view document.getElementById("navigation").style.visibility = 'visible'; - Game.panorama.setOptions({ - clickToGo: true, - linksControl: true, - scrollwheel: true - }); + Game.disableRestrictions(); + document.getElementById('panningBlockerCover').style.display = null; Game.initialize(); @@ -511,6 +533,8 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); // update the compass const heading = Game.panorama.getPov().heading; document.getElementById("compass").style.transform = "translateY(-50%) rotate(" + heading + "deg)"; + + Game.enableRestrictions(); }, handleErrorResponse: function (error) { @@ -625,22 +649,24 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); }, guess: function () { - if (!Game.guessMarker) { - return; - } + + Game.disableRestrictions(); - var guessPosition = Game.guessMarker.getPosition().toJSON(); - Game.rounds[Game.rounds.length - 1].guessPosition = guessPosition; + var data = new FormData(); + + if (Game.guessMarker) { + var guessPosition = Game.guessMarker.getPosition().toJSON(); + Game.rounds[Game.rounds.length - 1].guessPosition = guessPosition; + + data.append('lat', String(guessPosition.lat)); + data.append('lng', String(guessPosition.lng)); + } document.getElementById('guessButton').disabled = true; document.getElementById('panoCover').style.visibility = 'visible'; - - var data = new FormData(); - data.append('lat', String(guessPosition.lat)); - data.append('lng', String(guessPosition.lng)); - document.getElementById('loading').style.visibility = 'visible'; var url = Game.getGameIdentifier() + '/guess.json'; + MapGuesser.httpRequest('POST', url, function () { document.getElementById('loading').style.visibility = 'hidden'; @@ -650,6 +676,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); } Game.history = this.response.history; + Game.restrictions = this.response.restrictions; Game.receiveResult(this.response.position, guessPosition, this.response.result, this.response.allResults); @@ -851,7 +878,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); }, 1); }, - startCountdown: function (timeout) { + startCountdown: function (timeout, timedOutHandler) { if (Game.countdownHandler) { clearInterval(Game.countdownHandler); } @@ -873,7 +900,12 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); Game.setCountdownTime(timeLeft); if (timeLeft <= 0) { - document.getElementById('panoCover').style.visibility = 'visible'; + if (typeof timedOutHandler === 'function') { + timedOutHandler(); + } else { + document.getElementById('panoCover').style.visibility = 'visible'; + } + clearInterval(Game.countdownHandler); } }, 1000); diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index 668e895..02b6df4 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -150,6 +150,7 @@ class GameFlowController $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); if (!isset($currentPlace) || $withHistory) { + $withRelations = [User::class, PlaceInChallenge::class, Place::class]; foreach ($this->guessRepository->getAllInChallenge($challenge, $withRelations) as $guess) { $round = $guess->getPlaceInChallenge()->getRound(); @@ -171,6 +172,22 @@ class GameFlowController ]; } } + + // setting default values for rounds without guesses (because of timeout) + for ($i = 0; $i < $currentRound; ++$i) { + if (!isset($response['history'][$i]) || !isset($response['history'][$i]['result'])) { + $response['history'][$i]['result'] = [ + 'guessPosition' => null, + 'distance' => null, + 'score' => 0 + ]; + + $response['history'][$i]['position'] = + $this->placeRepository->getByRoundInChallenge($challenge, $i)->getPosition()->toArray(); + } + } + + $response['history']['length'] = $currentRound; } if (!isset($currentPlace)) { // game finished @@ -196,7 +213,7 @@ class GameFlowController } $response['restrictions'] = [ - 'timeLimit' => $challenge->getTimeLimit(), + 'timeLimit' => $challenge->getTimeLimit() * 1000, 'noMove' => $challenge->getNoMove(), 'noPan' => $challenge->getNoPan(), 'noZoom' => $challenge->getNoZoom() @@ -339,39 +356,37 @@ class GameFlowController $currentPlace = $currentPlaceInChallenge->getPlace(); $map = $currentPlace->getMap(); - - $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); - $result = $this->evaluateGuess($currentPlace->getPosition(), $guessPosition, $map->getArea()); - - // save guess - $guess = new Guess(); - $guess->setUserId($userId); - $guess->setPlaceInChallenge($currentPlaceInChallenge); - $guess->setPosition($guessPosition); - $guess->setDistance($result['distance']); - $guess->setScore($result['score']); - $this->pdm->saveToDb($guess); - - - - // update round + // creating response $nextRound = $currentRound + 1; + $response = $this->prepareChallengeResponse($userId, $challenge, $nextRound); + $response['position'] = $currentPlace->getPosition()->toArray(); + + if ($this->request->post('lat') && $this->request->post('lng')) { + $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); + $result = $this->evaluateGuess($currentPlace->getPosition(), $guessPosition, $map->getArea()); + + // save guess + $guess = new Guess(); + $guess->setUserId($userId); + $guess->setPlaceInChallenge($currentPlaceInChallenge); + $guess->setPosition($guessPosition); + $guess->setDistance($result['distance']); + $guess->setScore($result['score']); + $this->pdm->saveToDb($guess); + + $response['result'] = $result; + + } else { + // user didn't manage to guess in the round in the given timeframe + $response['result'] = ['distance' => null, 'score' => 0]; + } $userInChallenge->setCurrentRound($nextRound); $this->pdm->saveToDb($userInChallenge); - - // creating response - $response = $this->prepareChallengeResponse($userId, $challenge, $nextRound); - - $response['position'] = $currentPlace->getPosition()->toArray(); - $response['result'] = $result; - if(isset($response['history'][$currentRound]['allResults'])) { $response['allResults'] = $response['history'][$currentRound]['allResults']; } - - $this->saveVisit($currentPlace->getId()); -- 2.45.2 From b78d564f6f14cf3c44f9665ea967ea65403755ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Thu, 20 May 2021 21:32:01 +0200 Subject: [PATCH 26/41] MAPG-235 time limit restriction for the whole game feature added --- public/static/js/game.js | 11 +++++++++-- src/Controller/GameFlowController.php | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index 718efe1..23ede69 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -341,6 +341,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); }, disableRestrictions: function () { + Game.panorama.setOptions({ clickToGo: true, linksControl: true, @@ -350,6 +351,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); document.getElementById('panningBlockerCover').style.display = null; Game.startCountdown(0); + Game.timeoutEnd = null; }, transitToResultMap: function () { @@ -649,11 +651,16 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); }, guess: function () { - - Game.disableRestrictions(); var data = new FormData(); + if (Game.timeoutEnd) { + var timeLeft = Math.ceil((Game.timeoutEnd - new Date()) / 1000); + data.append('timeLeft', timeLeft); + } + + Game.disableRestrictions(); + if (Game.guessMarker) { var guessPosition = Game.guessMarker.getPosition().toJSON(); Game.rounds[Game.rounds.length - 1].guessPosition = guessPosition; diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index 02b6df4..b0e4473 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -381,9 +381,19 @@ class GameFlowController $response['result'] = ['distance' => null, 'score' => 0]; } + // save user relevant state of challenge $userInChallenge->setCurrentRound($nextRound); + if ($this->request->post('timeLeft')) { + $userInChallenge->setTimeLeft(intval($this->request->post('timeLeft'))); + } $this->pdm->saveToDb($userInChallenge); + + if ($challenge->getTimeLimitType() === 'game' && $this->request->post('timeLeft')) { + $timeLimit = max(10, intval($this->request->post('timeLeft'))); + $response['restrictions']['timeLimit'] = $timeLimit * 1000; + } + if(isset($response['history'][$currentRound]['allResults'])) { $response['allResults'] = $response['history'][$currentRound]['allResults']; } -- 2.45.2 From aadeda05afea6247545acea7fd2a5b06fa46897e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Fri, 21 May 2021 11:30:30 +0200 Subject: [PATCH 27/41] MAPG-235 timeLimit restored after reload of the page. Time limit selector usability improved --- public/static/css/maps.css | 1 + public/static/js/maps.js | 20 ++++++++++++++++++-- src/Controller/GameFlowController.php | 5 +++++ src/PersistentData/Model/UserInChallenge.php | 2 +- views/maps.php | 2 +- 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/public/static/css/maps.css b/public/static/css/maps.css index acfb23d..7c3eed5 100644 --- a/public/static/css/maps.css +++ b/public/static/css/maps.css @@ -93,6 +93,7 @@ div.mapItem>div.buttonContainer { #restrictions input[type=range] { height: 1.5em; margin-left: 2em; + width: 70%; } #timeLimitType { diff --git a/public/static/js/maps.js b/public/static/js/maps.js index 8631cc5..41c16ae 100644 --- a/public/static/js/maps.js +++ b/public/static/js/maps.js @@ -65,6 +65,22 @@ } }; + var Util = { + printTimeForHuman: function (time) { + if (time < 60) { + return '' + time + ' seconds'; + } else if (time == 60) { + return '1 minute'; + } else if (time % 60 == 0) { + return '' + Math.floor(time / 60) + ' minutes'; + } else if (time % 60 == 1) { + return '' + Math.floor(time / 60) + ' minutes and 1 second'; + } else { + return '' + Math.floor(time / 60) + ' minutes and ' + time % 60 + ' seconds'; + } + } + }; + Maps.addStaticMaps(); Maps.initializeDescriptionDivs(); @@ -119,7 +135,7 @@ document.getElementById('playMode').style.visibility = 'hidden'; var timeLimit = document.getElementById('timeLimit').value; - document.getElementById('timeLimitLabel').innerText = 'Time limit of ' + timeLimit + ' seconds per round'; + document.getElementById('timeLimitLabel').innerText = 'Time limit of ' + Util.printTimeForHuman(timeLimit); }; } @@ -149,7 +165,7 @@ document.getElementById('timeLimit').oninput = function () { var timeLimit = document.getElementById('timeLimit').value; - document.getElementById('timeLimitLabel').innerText = 'Time limit of ' + timeLimit + ' seconds per round'; + document.getElementById('timeLimitLabel').innerText = 'Time limit of ' + Util.printTimeForHuman(timeLimit); document.getElementById('timerEnabled').checked = true; } })(); diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index b0e4473..88eef5c 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -239,6 +239,11 @@ class GameFlowController $response = $this->prepareChallengeResponse($userId, $challenge, $currentRound, true); + if ($challenge->getTimeLimitType() === 'game' && $userInChallenge->getCurrentRound() > 0) { + $timeLimit = max(10, $userInChallenge->getTimeLeft()); + $response['restrictions']['timeLimit'] = $timeLimit * 1000; + } + return new JsonContent($response); } diff --git a/src/PersistentData/Model/UserInChallenge.php b/src/PersistentData/Model/UserInChallenge.php index 680e04c..1b636e9 100644 --- a/src/PersistentData/Model/UserInChallenge.php +++ b/src/PersistentData/Model/UserInChallenge.php @@ -50,7 +50,7 @@ class UserInChallenge extends Model public function setTimeLeft(?int $timeLeft): void { if(isset($timeLeft)) { - $this->timeLeft = $timeLeft; + $this->timeLeft = max(0, $timeLeft); } } diff --git a/views/maps.php b/views/maps.php index c097e4c..1e3f712 100644 --- a/views/maps.php +++ b/views/maps.php @@ -51,7 +51,7 @@ TODO: condition!
- +
Time limit -- 2.45.2 From d4a279d2f4540f4be0cd7c5cf11dcc096c45ccae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Fri, 21 May 2021 11:51:47 +0200 Subject: [PATCH 28/41] MAPG-235 refactored human readable time format --- public/static/js/maps.js | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/public/static/js/maps.js b/public/static/js/maps.js index 41c16ae..35c4e33 100644 --- a/public/static/js/maps.js +++ b/public/static/js/maps.js @@ -67,17 +67,27 @@ var Util = { printTimeForHuman: function (time) { - if (time < 60) { - return '' + time + ' seconds'; - } else if (time == 60) { - return '1 minute'; - } else if (time % 60 == 0) { - return '' + Math.floor(time / 60) + ' minutes'; - } else if (time % 60 == 1) { - return '' + Math.floor(time / 60) + ' minutes and 1 second'; - } else { - return '' + Math.floor(time / 60) + ' minutes and ' + time % 60 + ' seconds'; + const minutes = Math.floor(time / 60); + const seconds = time % 60; + var time_str = ''; + + if (minutes == 1) { + time_str += '1 minute'; + } else if (minutes > 1) { + time_str += minutes + ' minutes'; } + + if (minutes > 0 && seconds > 0) { + time_str += ' and '; + } + + if (seconds == 1) { + time_str += '1 second'; + } else if (seconds > 1) { + time_str += seconds + ' seconds'; + } + + return time_str; } }; -- 2.45.2 From 5f0b639fe7e9300addba8eacbf326fbced648720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Fri, 21 May 2021 14:44:28 +0200 Subject: [PATCH 29/41] MAPG-235 distance and score info added also to the player's own markers --- public/static/js/game.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index 23ede69..9f0a5fd 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -419,7 +419,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); Game.addPositionToResultMap(true); if (round.result.guessPosition) { - Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); + Game.addGuessPositionToResultMap(round.result.guessPosition, round.result, true); } Game.scoreSum += round.result.score; @@ -591,7 +591,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); resultBounds.extend(position); if (guessPosition) { - Game.addGuessPositionToResultMap(guessPosition); + Game.addGuessPositionToResultMap(guessPosition, result); resultBounds.extend(guessPosition); } @@ -725,8 +725,8 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); var position = round.position; var guessMarker = { marker: null, line: null, info: null }; - var markerSvg = result ? 'marker-gray-empty.svg' : 'marker-blue-empty.svg'; - var markerLabel = result ? result.userName.charAt(0).toUpperCase() : '?'; + var markerSvg = result && result.userName ? 'marker-gray-empty.svg' : 'marker-blue-empty.svg'; + var markerLabel = result && result.userName ? result.userName.charAt(0).toUpperCase() : '?'; guessMarker.marker = new google.maps.Marker({ map: Game.map, @@ -776,8 +776,9 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); }); if (result) { + const userName = result.userName ? result.userName : 'me'; guessMarker.info = new google.maps.InfoWindow({ - content: '

' + result.userName + '

' + + content: '

' + userName + '

' + '

' + Util.printDistanceForHuman(result.distance) + ' | ' + result.score + ' points

', }); -- 2.45.2 From 4520d1155993d4ecda0ab18225872a3e1e841ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Fri, 21 May 2021 14:45:40 +0200 Subject: [PATCH 30/41] MAPG-235 fix calculating timelimit when 0 sec time was left from the last round --- src/Controller/GameFlowController.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index 88eef5c..9e173d0 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -388,14 +388,14 @@ class GameFlowController // save user relevant state of challenge $userInChallenge->setCurrentRound($nextRound); - if ($this->request->post('timeLeft')) { - $userInChallenge->setTimeLeft(intval($this->request->post('timeLeft'))); + $timeLeft = $this->request->post('timeLeft'); + if (isset($timeLeft)) { + $userInChallenge->setTimeLeft(intval($timeLeft)); } $this->pdm->saveToDb($userInChallenge); - - if ($challenge->getTimeLimitType() === 'game' && $this->request->post('timeLeft')) { - $timeLimit = max(10, intval($this->request->post('timeLeft'))); + if ($challenge->getTimeLimitType() === 'game' && isset($timeLeft)) { + $timeLimit = max(10, intval($timeLeft)); $response['restrictions']['timeLimit'] = $timeLimit * 1000; } -- 2.45.2 From 636c47366a34cd5b3ef6dcf42616b4a3601be67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Fri, 21 May 2021 15:40:43 +0200 Subject: [PATCH 31/41] MAPG-235 fix loading other players' history and calculating final score --- public/static/js/game.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index 9f0a5fd..66ccfee 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -412,6 +412,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); } Game.rounds = []; + Game.scoreSum = 0; for (var i = 0; i < Game.history.length; ++i) { var round = Game.history[i]; @@ -682,7 +683,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); return; } - Game.history = this.response.history; + Game.loadHistory(this.response.history); Game.restrictions = this.response.restrictions; Game.receiveResult(this.response.position, guessPosition, this.response.result, this.response.allResults); -- 2.45.2 From 599d8bf1532012df787fed4a63540bcf2f28ff01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Fri, 21 May 2021 15:41:17 +0200 Subject: [PATCH 32/41] MAPG-235 fix unwanted time limit after page reload --- src/Controller/GameFlowController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index 9e173d0..ba07f54 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -149,6 +149,7 @@ class GameFlowController { $currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound); + // if the last round was played ($currentPlace == null) or history is explicitly requested (for initializing) if (!isset($currentPlace) || $withHistory) { $withRelations = [User::class, PlaceInChallenge::class, Place::class]; @@ -239,7 +240,7 @@ class GameFlowController $response = $this->prepareChallengeResponse($userId, $challenge, $currentRound, true); - if ($challenge->getTimeLimitType() === 'game' && $userInChallenge->getCurrentRound() > 0) { + if ($challenge->getTimeLimitType() === 'game' && $challenge->getTimeLimit() !== null && $userInChallenge->getCurrentRound() > 0) { $timeLimit = max(10, $userInChallenge->getTimeLeft()); $response['restrictions']['timeLimit'] = $timeLimit * 1000; } -- 2.45.2 From b00fe4ebe35421025b3521f43d06616e1a00b602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Sat, 22 May 2021 11:34:55 +0200 Subject: [PATCH 33/41] MAPG-235 highscores added to the end of challenge --- public/static/css/game.css | 34 ++++++++ public/static/css/mapguesser.css | 2 +- public/static/js/game.js | 92 ++++++++++++++++++-- src/Controller/GameFlowController.php | 8 ++ src/Repository/UserInChallengeRepository.php | 8 ++ views/game.php | 16 +++- 6 files changed, 150 insertions(+), 10 deletions(-) diff --git a/public/static/css/game.css b/public/static/css/game.css index b3def48..a5ca714 100644 --- a/public/static/css/game.css +++ b/public/static/css/game.css @@ -168,6 +168,37 @@ display: none; } +#highscoresTable { + margin: 1em; + border-collapse: collapse; + width: 90%; +} + +#highscoresTable td, #highscoresTable th { + border: 1px solid #ddd; + padding: 8px; +} + +#highscoresTable tr:nth-child(even) { + background-color: #f2f2f2; +} + +#highscoresTable tr:hover { + background-color: #ddd; +} + +#highscoresTable th { + padding-top: 12px; + padding-bottom: 12px; + text-align: left; + background-color: #e8a349; + color: white; +} + +#highscoresTable tr.ownPlayer { + font-weight: 500; +} + @media screen and (max-width: 599px) { #mapName { display: none; @@ -195,6 +226,9 @@ bottom: 25px; left: 10px; } + .hideOnMobile { + display: none; + } } @media screen and (min-width: 600px) { diff --git a/public/static/css/mapguesser.css b/public/static/css/mapguesser.css index 3a8e64e..3da0b76 100644 --- a/public/static/css/mapguesser.css +++ b/public/static/css/mapguesser.css @@ -31,7 +31,7 @@ main { color: #ffffff; } -p, h1, h2, input, textarea, select, button, a { +p, h1, h2, input, textarea, select, button, a, table { font-family: 'Roboto', sans-serif; } diff --git a/public/static/js/game.js b/public/static/js/game.js index 66ccfee..05e03db 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -21,6 +21,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); googleLink: null, history: [], restrictions: null, + finishers: null, readyToContinue: true, timeoutEnd: null, @@ -296,7 +297,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); return; } - Game.loadHistory(this.response.history); + Game.loadHistory(this.response); if (this.response.finished) { @@ -334,14 +335,14 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); } if (Game.restrictions.timeLimit) { - Game.startCountdown(Game.restrictions.timeLimit, function() { + Game.startCountdown(Game.restrictions.timeLimit, function () { Game.guess(); }); } }, disableRestrictions: function () { - + Game.panorama.setOptions({ clickToGo: true, linksControl: true, @@ -389,11 +390,11 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); } }, - loadHistory: function (history) { - if (!history) + loadHistory: function (response) { + if (!response.history) return; - Game.history = history; + Game.history = response.history; for (var i = 0; i < Game.rounds.length; ++i) { var round = Game.rounds[i]; @@ -435,6 +436,10 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); } } } + + if (response.finishers) { + Game.finishers = new Set(response.finishers); + } }, reset: function () { @@ -466,6 +471,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); distanceInfo.children[0].style.display = null; distanceInfo.children[1].style.display = null; distanceInfo.children[2].style.display = null; + document.getElementById('summaryInfo').innerHTML = "Game finished." var scoreInfo = document.getElementById('scoreInfo'); scoreInfo.children[0].style.display = null; scoreInfo.children[1].style.display = null; @@ -484,6 +490,9 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); document.getElementById('panningBlockerCover').style.display = null; + Game.history = []; + Game.finishers = null; + Game.initialize(); }, @@ -659,7 +668,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); var timeLeft = Math.ceil((Game.timeoutEnd - new Date()) / 1000); data.append('timeLeft', timeLeft); } - + Game.disableRestrictions(); if (Game.guessMarker) { @@ -683,7 +692,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); return; } - Game.loadHistory(this.response.history); + Game.loadHistory(this.response); Game.restrictions = this.response.restrictions; Game.receiveResult(this.response.position, guessPosition, this.response.result, this.response.allResults); @@ -806,6 +815,31 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); return { width: percent + '%', backgroundColor: color }; }, + calculateHighScores: function () { + + var highscores = new Map(); + highscores.set('me', Game.scoreSum); + + for (var i = 0; i < Game.history.length; ++i) { + const round = Game.history[i]; + if (round.allResults) { + for (const result of round.allResults) { + if (Game.finishers.has(result.userName)) { + if (highscores.has(result.userName)) { + highscores.set(result.userName, highscores.get(result.userName) + result.score); + } else { + highscores.set(result.userName, result.score); + } + } + } + } + } + + var sortedHighscores = Array.from(highscores, ([userName, score]) => ({ 'userName': userName, 'score': score })) + .sort(function (resultA, resultB) { return resultB.score - resultA.score }); + return sortedHighscores; + }, + showSummary: function () { var distanceInfo = document.getElementById('distanceInfo'); distanceInfo.children[0].style.display = 'none'; @@ -865,6 +899,44 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); var scoreBar = document.getElementById('scoreBar'); scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor; scoreBar.style.width = scoreBarProperties.width; + + if (Game.type == GameType.CHALLENGE) { + var highscores = this.calculateHighScores(); + var summaryInfo = document.getElementById('summaryInfo'); + + if (highscores.length > 2) { + var table = document.getElementById('highscoresTable'); + for (const result of highscores) { + var userName = document.createElement('td'); + userName.innerHTML = result.userName; + var score = document.createElement('td'); + score.innerHTML = result.score; + var line = document.createElement('tr'); + line.appendChild(userName); + line.appendChild(score); + table.appendChild(line); + + if (result.userName === 'me') { + line.setAttribute('class', 'ownPlayer'); + } + } + + MapGuesser.showModal('highscores'); + } else if (highscores.length == 2) { + + if (highscores[0].userName === 'me') { + summaryInfo.innerHTML = 'You won! ' + highscores[1].userName + ' got only ' + highscores[1].score + ' points.'; + } else { + summaryInfo.innerHTML = 'You lost! ' + highscores[0].userName + ' won with ' + highscores[0].score + ' points.'; + } + + } else if (highscores.length == 1) { + // summaryInfo.innerHTML = 'You are the first to finish. Challenge other by sending them the link and come back for the results.' + summaryInfo.innerHTML = 'You are the first to finish.' + } + + } + }, rewriteGoogleLink: function () { @@ -1112,4 +1184,8 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); document.getElementById('compassContainer').onclick = function () { Game.panorama.setPov({ heading: 0, pitch: Game.panorama.getPov().pitch }); } + + document.getElementById('closeHighscoresButton').onclick = function () { + MapGuesser.hideModal(); + }; })(); diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index ba07f54..ae809f3 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -193,6 +193,14 @@ class GameFlowController if (!isset($currentPlace)) { // game finished $response['finished'] = true; + + // list all players who finished the challenge + $response['finishers'] = []; + foreach ($this->userInChallengeRepository->getAllByChallengeWithUsers($challenge) as $userInChallenge) { + if ($userInChallenge->getCurrentRound() == $currentRound && $userInChallenge->getUser()->getId() != $userId) { + $response['finishers'][] = $userInChallenge->getUser()->getDisplayName(); + } + } } else { // continue game $response['place'] = [ 'panoId' => $currentPlace->getPanoIdCached(), diff --git a/src/Repository/UserInChallengeRepository.php b/src/Repository/UserInChallengeRepository.php index c059c82..80e55e7 100644 --- a/src/Repository/UserInChallengeRepository.php +++ b/src/Repository/UserInChallengeRepository.php @@ -32,6 +32,14 @@ class UserInChallengeRepository yield from $this->pdm->selectMultipleFromDb($select, UserInChallenge::class); } + public function getAllByChallengeWithUsers(Challenge $challenge) : Generator + { + $select = new Select(\Container::$dbConnection); + $select->where('challenge_id', '=', $challenge->getId()); + + yield from $this->pdm->selectMultipleFromDb($select, UserInChallenge::class, true, [User::class]); + } + public function getByUserIdAndChallenge(int $userId, Challenge $challenge): ?UserInChallenge { $select = new Select(\Container::$dbConnection); diff --git a/views/game.php b/views/game.php index 2c9624a..c59f20f 100644 --- a/views/game.php +++ b/views/game.php @@ -12,6 +12,20 @@
+ @endsection @section(subheader) @@ -42,7 +56,7 @@

You were close.

You didn't guess in this round.

-

Game finished.

+

Game finished.

You earned points.

-- 2.45.2 From a5238234d23a448ca5e741aa37b662b757fa2e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Sat, 22 May 2021 20:54:58 +0200 Subject: [PATCH 34/41] MAPG-235 information of the current restrictions displayed on the ribbon in the top --- public/static/css/game.css | 9 ++-- public/static/js/game.js | 69 ++++++++++++++++++++++++--- src/Controller/GameFlowController.php | 15 +++--- 3 files changed, 76 insertions(+), 17 deletions(-) diff --git a/public/static/css/game.css b/public/static/css/game.css index a5ca714..2970387 100644 --- a/public/static/css/game.css +++ b/public/static/css/game.css @@ -199,6 +199,12 @@ font-weight: 500; } +@media screen and (max-width: 899px) { + .hideOnNarrowScreen { + display: none; + } +} + @media screen and (max-width: 599px) { #mapName { display: none; @@ -226,9 +232,6 @@ bottom: 25px; left: 10px; } - .hideOnMobile { - display: none; - } } @media screen and (min-width: 600px) { diff --git a/public/static/js/game.js b/public/static/js/game.js index 05e03db..2edc43b 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -299,6 +299,9 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); Game.loadHistory(this.response); + Game.restrictions = this.response.restrictions; + Game.displayRestrictions(); + if (this.response.finished) { Game.transitToResultMap(); @@ -306,8 +309,6 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); } else { - Game.restrictions = this.response.restrictions; - Game.panoId = this.response.place.panoId; Game.pov = this.response.place.pov; @@ -339,6 +340,47 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); Game.guess(); }); } + + }, + + displayRestrictions: function () { + if (!Game.restrictions) { + return; + } + + + + var restrictionsForDisplay = []; + if (Game.restrictions.timeLimit) { + restrictionsForDisplay.push('time limit per ' + Game.restrictions.timeLimitType); + } + if (Game.restrictions.noPan) { + restrictionsForDisplay.push('no camera change'); + } + else { + if (Game.restrictions.noMove) { + restrictionsForDisplay.push('no move'); + } + if (Game.restrictions.noZoom) { + restrictionsForDisplay.push('no zoom'); + } + } + + // create restrictions span for header + var restrictions = document.createElement('span'); + restrictions.setAttribute('id', 'restrictions'); + restrictions.setAttribute('class', 'hideOnNarrowScreen'); + var restrictionsTitle = document.createElement('span'); + restrictionsTitle.setAttribute('class', 'bold'); + restrictionsTitle.innerText = 'Restrictions: '; + var restrictionsList = document.createElement('span'); + restrictionsList.innerText = restrictionsForDisplay.join(', '); + restrictions.appendChild(restrictionsTitle); + restrictions.appendChild(restrictionsList); + + var roundContainer = document.getElementById('roundContainer'); + var header = roundContainer.parentNode; + header.insertBefore(restrictions, roundContainer); }, disableRestrictions: function () { @@ -355,6 +397,14 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); Game.timeoutEnd = null; }, + hideRestrictions: function() { + var restrictions = document.getElementById('restrictions'); + if (restrictions) { + var header = restrictions.parentNode; + header.removeChild(restrictions); + } + }, + transitToResultMap: function () { // TODO: refactor - it is necessary for mobile if (window.getComputedStyle(document.getElementById('guess')).visibility === 'hidden') { @@ -487,6 +537,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); document.getElementById("navigation").style.visibility = 'visible'; Game.disableRestrictions(); + Game.hideRestrictions(); document.getElementById('panningBlockerCover').style.display = null; @@ -900,6 +951,12 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor; scoreBar.style.width = scoreBarProperties.width; + Game.showHighscores(); + + }, + + showHighscores: function() { + if (Game.type == GameType.CHALLENGE) { var highscores = this.calculateHighScores(); var summaryInfo = document.getElementById('summaryInfo'); @@ -925,18 +982,16 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); } else if (highscores.length == 2) { if (highscores[0].userName === 'me') { - summaryInfo.innerHTML = 'You won! ' + highscores[1].userName + ' got only ' + highscores[1].score + ' points.'; + summaryInfo.innerHTML = 'You won! ' + highscores[1].userName + ' got only ' + highscores[1].score + ' points.'; } else { - summaryInfo.innerHTML = 'You lost! ' + highscores[0].userName + ' won with ' + highscores[0].score + ' points.'; + summaryInfo.innerHTML = 'You lost! ' + highscores[0].userName + ' won with ' + highscores[0].score + ' points.'; } } else if (highscores.length == 1) { - // summaryInfo.innerHTML = 'You are the first to finish. Challenge other by sending them the link and come back for the results.' - summaryInfo.innerHTML = 'You are the first to finish.' + summaryInfo.innerHTML = 'You are the first to finish. Invite your friends by sending them the link.' } } - }, rewriteGoogleLink: function () { diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index 3b8f620..220c704 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -226,15 +226,16 @@ class GameFlowController implements ISecured } } } - - $response['restrictions'] = [ - 'timeLimit' => $challenge->getTimeLimit() * 1000, - 'noMove' => $challenge->getNoMove(), - 'noPan' => $challenge->getNoPan(), - 'noZoom' => $challenge->getNoZoom() - ]; } + $response['restrictions'] = [ + 'timeLimit' => $challenge->getTimeLimit() * 1000, + 'timeLimitType' => $challenge->getTimeLimitType(), + 'noMove' => $challenge->getNoMove(), + 'noPan' => $challenge->getNoPan(), + 'noZoom' => $challenge->getNoZoom() + ]; + return $response; } -- 2.45.2 From 567602c74916c47a4b68b991da20b26f3df3a469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Sat, 22 May 2021 21:00:54 +0200 Subject: [PATCH 35/41] MAPG-235 hide restrictions information if there are no restrictions for the challenge --- public/static/js/game.js | 6 ++++-- views/game.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index 2edc43b..d5e8f09 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -348,8 +348,6 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); return; } - - var restrictionsForDisplay = []; if (Game.restrictions.timeLimit) { restrictionsForDisplay.push('time limit per ' + Game.restrictions.timeLimitType); @@ -366,6 +364,10 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); } } + if (restrictionsForDisplay.length == 0) { + return; + } + // create restrictions span for header var restrictions = document.createElement('span'); restrictions.setAttribute('id', 'restrictions'); diff --git a/views/game.php b/views/game.php index c59f20f..b901cca 100644 --- a/views/game.php +++ b/views/game.php @@ -30,7 +30,7 @@ @section(subheader) Loading map...Round Round Score @endsection -- 2.45.2 From 79709276547db27bcd2ea7153bdbdbe8039bf8b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Sat, 22 May 2021 21:17:49 +0200 Subject: [PATCH 36/41] MAPG-235 refactored whitespaces --- public/static/js/game.js | 6 +++--- src/Controller/GameController.php | 4 ++-- src/Controller/GameFlowController.php | 6 +++--- src/Database/Query/Select.php | 6 +++--- src/PersistentData/Model/Challenge.php | 2 +- src/PersistentData/Model/UserInChallenge.php | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index d5e8f09..c548434 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -340,7 +340,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); Game.guess(); }); } - + }, displayRestrictions: function () { @@ -399,7 +399,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); Game.timeoutEnd = null; }, - hideRestrictions: function() { + hideRestrictions: function () { var restrictions = document.getElementById('restrictions'); if (restrictions) { var header = restrictions.parentNode; @@ -957,7 +957,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); }, - showHighscores: function() { + showHighscores: function () { if (Game.type == GameType.CHALLENGE) { var highscores = this.calculateHighScores(); diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index 5248137..56aa673 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -208,7 +208,7 @@ class GameController implements ISecured $room = $this->multiRoomRepository->getByRoomId($roomId); - if(!isset($room)) { + if (!isset($room)) { return new JsonContent(['error' => 'game_not_found']); } @@ -265,7 +265,7 @@ class GameController implements ISecured return new JsonContent(['error' => 'game_not_found']); } - if(!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) { + if (!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) { // new player is joining $userInChallenge = new UserInChallenge(); $userInChallenge->setUserId($userId); diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index 220c704..fdbf379 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -309,9 +309,9 @@ class GameFlowController implements ISecured $session = $this->request->session(); $userId = $session->get('userId'); - if(isset($userId)) { + if (isset($userId)) { $userPlayedPlace = $this->userPlayedPlaceRepository->getByUserIdAndPlaceId($userId, $placeId); - if(!$userPlayedPlace) { + if (!$userPlayedPlace) { $userPlayedPlace = new UserPlayedPlace(); $userPlayedPlace->setUserId($userId); $userPlayedPlace->setPlaceId($placeId); @@ -415,7 +415,7 @@ class GameFlowController implements ISecured $response['restrictions']['timeLimit'] = $timeLimit * 1000; } - if(isset($response['history'][$currentRound]['allResults'])) { + if (isset($response['history'][$currentRound]['allResults'])) { $response['allResults'] = $response['history'][$currentRound]['allResults']; } diff --git a/src/Database/Query/Select.php b/src/Database/Query/Select.php index d62bd84..f9c2879 100644 --- a/src/Database/Query/Select.php +++ b/src/Database/Query/Select.php @@ -261,7 +261,7 @@ class Select $queryString .= ' LIMIT ' . $this->limit[1] . ', ' . $this->limit[0]; } - if($this->isDerivedTable()) { + if ($this->isDerivedTable()) { $queryString = '(' . $queryString . ') AS ' . $this->tableAliases[Select::DERIVED_TABLE_KEY]; } @@ -276,7 +276,7 @@ class Select return [(string) $table, $params]; } - if($table instanceof Select) + if ($table instanceof Select) { return $table->generateQuery(); } @@ -332,7 +332,7 @@ class Select $joinQueries = []; $params = []; - foreach($this->joins as $join) { + foreach ($this->joins as $join) { list($joinQueryFragment, $paramsFragment) = $this->generateTable($join[1], true); $joinQueries[] = $join[0] . ' JOIN ' . $joinQueryFragment . ' ON ' . $this->generateColumn($join[2]) . ' ' . $join[3] . ' ' . $this->generateColumn($join[4]); $params = array_merge($params, $paramsFragment); diff --git a/src/PersistentData/Model/Challenge.php b/src/PersistentData/Model/Challenge.php index 6b159f0..4e61cc9 100644 --- a/src/PersistentData/Model/Challenge.php +++ b/src/PersistentData/Model/Challenge.php @@ -33,7 +33,7 @@ class Challenge extends Model public function setTimeLimit(?int $timeLimit): void { - if(isset($timeLimit)) { + if (isset($timeLimit)) { $this->timeLimit = $timeLimit; } } diff --git a/src/PersistentData/Model/UserInChallenge.php b/src/PersistentData/Model/UserInChallenge.php index 1b636e9..0b74c9c 100644 --- a/src/PersistentData/Model/UserInChallenge.php +++ b/src/PersistentData/Model/UserInChallenge.php @@ -49,7 +49,7 @@ class UserInChallenge extends Model public function setTimeLeft(?int $timeLeft): void { - if(isset($timeLeft)) { + if (isset($timeLeft)) { $this->timeLeft = max(0, $timeLeft); } } -- 2.45.2 From 283c214c500cb217d306a948f805842942109977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Wed, 26 May 2021 07:49:05 +0200 Subject: [PATCH 37/41] MAPG-235 noZoom label fix --- views/maps.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/maps.php b/views/maps.php index d7eca41..40d2932 100644 --- a/views/maps.php +++ b/views/maps.php @@ -67,7 +67,7 @@ TODO: condition!
- +
-- 2.45.2 From 1c1e5f051d61d7bb70818d3c13fea8a09165a010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Wed, 26 May 2021 08:16:32 +0200 Subject: [PATCH 38/41] MAPG-235 simplified highscore calculation --- public/static/js/game.js | 25 ++++++++++++------------- src/Controller/GameFlowController.php | 8 -------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/public/static/js/game.js b/public/static/js/game.js index c548434..8d73f7f 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -21,7 +21,6 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); googleLink: null, history: [], restrictions: null, - finishers: null, readyToContinue: true, timeoutEnd: null, @@ -488,10 +487,6 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); } } } - - if (response.finishers) { - Game.finishers = new Set(response.finishers); - } }, reset: function () { @@ -544,7 +539,6 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); document.getElementById('panningBlockerCover').style.display = null; Game.history = []; - Game.finishers = null; Game.initialize(); }, @@ -873,16 +867,21 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 }); var highscores = new Map(); highscores.set('me', Game.scoreSum); - for (var i = 0; i < Game.history.length; ++i) { + // collect the results of users who are through the last round + const round = Game.history[Game.history.length - 1]; + if (round.allResults) { + for (const result of round.allResults) { + highscores.set(result.userName, result.score); + } + } + + // add up scores only for the finishers + for (var i = Game.history.length - 2; i >= 0; --i) { const round = Game.history[i]; if (round.allResults) { for (const result of round.allResults) { - if (Game.finishers.has(result.userName)) { - if (highscores.has(result.userName)) { - highscores.set(result.userName, highscores.get(result.userName) + result.score); - } else { - highscores.set(result.userName, result.score); - } + if (highscores.has(result.userName)) { + highscores.set(result.userName, highscores.get(result.userName) + result.score); } } } diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index fdbf379..3f4c586 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -199,14 +199,6 @@ class GameFlowController implements ISecured if (!isset($currentPlace)) { // game finished $response['finished'] = true; - - // list all players who finished the challenge - $response['finishers'] = []; - foreach ($this->userInChallengeRepository->getAllByChallengeWithUsers($challenge) as $userInChallenge) { - if ($userInChallenge->getCurrentRound() == $currentRound && $userInChallenge->getUser()->getId() != $userId) { - $response['finishers'][] = $userInChallenge->getUser()->getDisplayName(); - } - } } else { // continue game $response['place'] = [ 'panoId' => $currentPlace->getPanoIdCached(), -- 2.45.2 From 28165d76d3478a797e9aaf36e0c5f0f820a9b7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Fri, 28 May 2021 08:07:02 +0200 Subject: [PATCH 39/41] MAPG-235 refactored challenge token generation and check --- src/Controller/GameController.php | 9 ++++----- src/Repository/UserInChallengeRepository.php | 6 ++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index 56aa673..9a9de40 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -112,11 +112,10 @@ class GameController implements ISecured public function createNewChallenge(): IContent { // create Challenge - $challengeToken = rand(); - while ($this->challengeRepository->getByToken($challengeToken)) { - // if a challenge with the same token already exists - $challengeToken = rand(); - } + do { + // initiliaze or if a challenge with the same token already exists + $challengeToken = mt_rand(); + } while ($this->challengeRepository->getByToken($challengeToken)); $challenge = new Challenge(); $challenge->setToken($challengeToken); diff --git a/src/Repository/UserInChallengeRepository.php b/src/Repository/UserInChallengeRepository.php index 80e55e7..f50c7f8 100644 --- a/src/Repository/UserInChallengeRepository.php +++ b/src/Repository/UserInChallengeRepository.php @@ -57,10 +57,8 @@ class UserInChallengeRepository } // validate token string - foreach (str_split($token_str) as $char) { - if (!(('0' <= $char && $char <= '9') || ('a' <= $char && $char <= 'f'))) { - return null; - } + if (!ctype_xdigit($token_str)) { + return null; } // convert token to int $token = hexdec($token_str); -- 2.45.2 From bbaa2fe1eb190a0f4474731c8fe367f9a101403c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Fri, 28 May 2021 08:19:35 +0200 Subject: [PATCH 40/41] MAPG-235 removed font-family and font-weight css attributes for restrictions and now handled on higher level --- public/static/css/mapguesser.css | 10 +++++++--- public/static/css/maps.css | 5 ----- views/maps.php | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/public/static/css/mapguesser.css b/public/static/css/mapguesser.css index 3da0b76..a5eb801 100644 --- a/public/static/css/mapguesser.css +++ b/public/static/css/mapguesser.css @@ -31,11 +31,11 @@ main { color: #ffffff; } -p, h1, h2, input, textarea, select, button, a, table { +p, h1, h2, h3, input, textarea, select, button, a, table, label { font-family: 'Roboto', sans-serif; } -h1, h2 { +h1, h2, h3 { font-weight: 500; } @@ -55,7 +55,11 @@ h2, header.small h1 { font-size: 24px; } -p, h2 { +h3 { + font-size: 18px; +} + +p, h2, h3 { line-height: 150%; } diff --git a/public/static/css/maps.css b/public/static/css/maps.css index 7c3eed5..2470f5f 100644 --- a/public/static/css/maps.css +++ b/public/static/css/maps.css @@ -78,11 +78,6 @@ div.mapItem>div.buttonContainer { #restrictions { margin-top: 1em; margin-bottom: 1em; - font-family: 'Roboto', sans-serif; -} - -#restrictions h3 { - font-weight: 500; } #restrictions input { diff --git a/views/maps.php b/views/maps.php index 40d2932..5268af2 100644 --- a/views/maps.php +++ b/views/maps.php @@ -54,7 +54,7 @@ TODO: condition!
- Time limit + -- 2.45.2 From 6f27450423d1e6da2ac1e63f214ec8fc9f705bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vigh?= Date: Fri, 28 May 2021 08:36:45 +0200 Subject: [PATCH 41/41] MAPG-235 replaced explicit margin-top and margin-bottom with marginTop and marginBottom classes --- public/static/css/maps.css | 5 ----- views/maps.php | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/public/static/css/maps.css b/public/static/css/maps.css index 2470f5f..70e7821 100644 --- a/public/static/css/maps.css +++ b/public/static/css/maps.css @@ -75,11 +75,6 @@ div.mapItem>div.buttonContainer { grid-auto-flow: column; } -#restrictions { - margin-top: 1em; - margin-bottom: 1em; -} - #restrictions input { height: auto; margin: 0.5em; diff --git a/views/maps.php b/views/maps.php index 5268af2..730ddbf 100644 --- a/views/maps.php +++ b/views/maps.php @@ -43,7 +43,7 @@ TODO: condition!

OR

--> -
+

Optional restrictions

-- 2.45.2