Compare commits
51
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00afd65d75 | ||
|
|
21e41b7c36 | ||
|
|
69964acfb2 | ||
|
|
e7869d67f7 | ||
|
|
93f8fc3f34 | ||
|
|
5daed10036 | ||
|
|
d7147b30d6 | ||
|
|
99244e93d2 | ||
|
|
313a3568aa | ||
|
|
75bea7c05c | ||
|
|
1c3238e64e | ||
|
|
6cb9e17ddf | ||
|
|
88ad6e9d61
|
||
|
|
2fb807c479
|
||
|
|
8e9e5b08f9
|
||
|
|
94624d5b2c | ||
|
|
71c728d56c | ||
|
|
216d30329f | ||
|
|
3f8311d708 | ||
|
|
7f8c1eb291 | ||
|
|
d57a4c63e0 | ||
|
|
ed8e1773c4 | ||
|
|
2963bfe739 | ||
|
|
7232b322ab | ||
|
|
e6819b6dd3 | ||
|
|
01a4fdf5e5 | ||
|
|
1cc3294ee7 | ||
|
|
ad0b8d078f | ||
|
|
899817a853 | ||
|
|
c626e36bbb | ||
|
|
8d8074977b | ||
|
|
8b3c95bdc7 | ||
|
|
b2535ad78a | ||
|
|
886bd02f88 | ||
|
|
7143c7ec63 | ||
|
|
fb7c0e7a5c | ||
|
|
8136273b33 | ||
|
|
3c6a7a3c5f | ||
|
|
01f3c7c8cf | ||
|
|
66c871fbc2 | ||
|
|
807b4d024f | ||
|
|
430b32d0c6 | ||
|
|
11eee64895
|
||
|
|
cc24f96395 | ||
|
|
c3e5a6db1c | ||
|
|
398204c5d5 | ||
|
|
aaa5a1daba | ||
|
|
c112a25409
|
||
|
|
35cab17c21
|
||
|
|
f9c1829a00
|
||
|
|
e03850ce05
|
No files matched your search
@@ -7,6 +7,7 @@ DB_NAME=mapguesser
|
||||
GOOGLE_MAPS_SERVER_API_KEY=your_google_maps_server_api_key
|
||||
GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key
|
||||
LEAFLET_TILESERVER_URL=a_leaflet_compatible_tileserver_url
|
||||
LEAFLET_TILESERVER_SUBDOMAINS=list_of_subdomains_for_the_tileserver_without_separators
|
||||
LEAFLET_TILESERVER_ATTRIBUTION=attribution_to_be_shown_for_tiles
|
||||
STATIC_ROOT=/static
|
||||
MAIL_FROM=mapguesser@mapguesser-dev.ch
|
||||
|
||||
@@ -65,7 +65,13 @@ Required API keys:
|
||||
* **GOOGLE_MAPS_SERVER_API_KEY**: this it used by the backend and should have access to **Street View Static API**
|
||||
* **GOOGLE_MAPS_JS_API_KEY**: this is used by the frontend and should have access to **Maps JavaScript API** and **Maps Static API**
|
||||
|
||||
Additionally, a tile provider is also needed for map editor. This should be configured by `LEAFLET_TILESERVER_URL` and `LEAFLET_TILESERVER_ATTRIBUTION`. You can find some providers here: https://wiki.openstreetmap.org/wiki/Tile_servers. OpenStreetMap's tile server is fine for testing.
|
||||
Additionally, a tile provider is also needed for map editor. This should be configured by `LEAFLET_TILESERVER_URL`, `LEAFLET_TILESERVER_SUBDOMAINS` and `LEAFLET_TILESERVER_ATTRIBUTION`. You can find some providers here: https://wiki.openstreetmap.org/wiki/Tile_servers. OpenStreetMap's tile server is fine for testing.
|
||||
Example:
|
||||
```
|
||||
LEAFLET_TILESERVER_URL=https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
|
||||
LEAFLET_TILESERVER_SUBDOMAINS=abc
|
||||
LEAFLET_TILESERVER_ATTRIBUTION="© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors"
|
||||
```
|
||||
|
||||
### (Production only) Create cron job
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE `user_played_place` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(10) unsigned NOT NULL,
|
||||
`place_id` int(10) unsigned NOT NULL,
|
||||
`last_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`occurrences` int(10) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY(`id`),
|
||||
KEY `user_id` (`user_id`),
|
||||
KEY `place_id` (`place_id`),
|
||||
CONSTRAINT `user_played_place_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
|
||||
CONSTRAINT `user_played_place_place_id` FOREIGN KEY (`place_id`) REFERENCES `places` (`id`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||
@@ -0,0 +1,53 @@
|
||||
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,
|
||||
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
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 0,
|
||||
`time_left` int(10) unsigned,
|
||||
`is_owner` tinyint(1) NOT NULL DEFAULT 0,
|
||||
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,
|
||||
`score` int(10) NOT NULL,
|
||||
`distance` int(10) 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;
|
||||
@@ -150,39 +150,9 @@
|
||||
}
|
||||
|
||||
#navigation {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#navigation .navigationItem {
|
||||
margin-top: 10px;
|
||||
opacity: 70%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#navigation .navigationItem:hover {
|
||||
opacity: 100%;
|
||||
}
|
||||
|
||||
#navigation .navigationItem div {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#navigation .circleBackground {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
#navigation .navigationIcon {
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
margin: auto;
|
||||
margin-top: 50%;
|
||||
transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 599px) {
|
||||
#mapName {
|
||||
display: none;
|
||||
@@ -209,10 +179,6 @@
|
||||
#navigation {
|
||||
bottom: 25px;
|
||||
left: 10px;
|
||||
width: 45px;
|
||||
}
|
||||
#navigation .navigationItem {
|
||||
height: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,10 +226,6 @@
|
||||
#navigation {
|
||||
bottom: 50px;
|
||||
left: 20px;
|
||||
width: 60px;
|
||||
}
|
||||
#navigation .navigationItem {
|
||||
height: 60px;
|
||||
}
|
||||
@media screen and (max-height: 424px) {
|
||||
#guess {
|
||||
@@ -282,10 +244,6 @@
|
||||
#navigation {
|
||||
bottom: 30px;
|
||||
left: 10px;
|
||||
width: 45px;
|
||||
}
|
||||
#navigation .navigationItem {
|
||||
height: 45px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,12 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#overlayControl {
|
||||
bottom: 20px;
|
||||
right: 10px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#deleteButton {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -443,6 +443,45 @@ div.box {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.circleControl {
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
bottom: 20px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.circleControl .controlItem {
|
||||
position: relative;
|
||||
height: 60px;
|
||||
margin-top: 10px;
|
||||
opacity: 70%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.circleControl .controlItem:hover {
|
||||
opacity: 100%;
|
||||
}
|
||||
|
||||
.circleControl .controlItem div {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.circleControl .controlBackground {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
.circleControl .controlIcon {
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
margin: auto;
|
||||
margin-top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 599px) {
|
||||
header h1 span {
|
||||
display: none;
|
||||
@@ -469,6 +508,12 @@ div.box {
|
||||
div.box {
|
||||
width: initial;
|
||||
}
|
||||
.circleControl {
|
||||
width: 45px;
|
||||
}
|
||||
.circleControl .controlItem {
|
||||
height: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
@@ -498,6 +543,12 @@ div.box {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.circleControl {
|
||||
width: 45px;
|
||||
}
|
||||
.circleControl .controlItem {
|
||||
height: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-height: 400px) and (max-height: 499px) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+152
-6
@@ -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: [],
|
||||
@@ -16,6 +19,7 @@
|
||||
guessMarker: null,
|
||||
adaptGuess: false,
|
||||
googleLink: null,
|
||||
history: [],
|
||||
|
||||
readyToContinue: true,
|
||||
timeoutEnd: null,
|
||||
@@ -211,6 +215,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 +243,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 +286,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';
|
||||
|
||||
@@ -278,17 +295,56 @@
|
||||
return;
|
||||
}
|
||||
|
||||
Game.panoId = this.response.place.panoId;
|
||||
Game.pov = this.response.place.pov;
|
||||
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) {
|
||||
|
||||
// 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();
|
||||
|
||||
document.getElementById('continueButton').style.display = 'none';
|
||||
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);
|
||||
|
||||
@@ -410,6 +466,10 @@
|
||||
MapGuesser.showModalWithContent('Error', 'This game is already started, you cannot join.');
|
||||
break;
|
||||
|
||||
case 'game_not_found':
|
||||
MapGuesser.showModalWithContent('Error', 'The game room was not found by this ID. Please check the link.');
|
||||
break;
|
||||
|
||||
default:
|
||||
MapGuesser.showModalWithContent('Error', 'Error code: \'' + error + '\'');
|
||||
break
|
||||
@@ -541,7 +601,7 @@
|
||||
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';
|
||||
|
||||
@@ -550,12 +610,57 @@
|
||||
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);
|
||||
},
|
||||
|
||||
@@ -669,6 +774,41 @@
|
||||
},
|
||||
|
||||
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';
|
||||
@@ -872,6 +1012,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();
|
||||
}
|
||||
|
||||
@@ -86,14 +86,14 @@
|
||||
sv.getPanorama({
|
||||
location: location,
|
||||
preference: google.maps.StreetViewPreference.NEAREST,
|
||||
radius: 100,
|
||||
radius: MapEditor.map.getSearchRadius(location),
|
||||
source: canBeIndoor ? google.maps.StreetViewSource.DEFAULT : google.maps.StreetViewSource.OUTDOOR
|
||||
}, function (data, status) {
|
||||
var panoLocationData = status === google.maps.StreetViewStatus.OK ? data.location : null;
|
||||
|
||||
if (panoLocationData === null && !canBeIndoor) {
|
||||
MapEditor.requestPanoData(location, true);
|
||||
return;
|
||||
if (panoLocationData === null && !canBeIndoor) {
|
||||
MapEditor.requestPanoData(location, true);
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('loading').style.visibility = 'hidden';
|
||||
@@ -377,8 +377,7 @@
|
||||
LMapWrapper.divId = divId;
|
||||
LMapWrapper.map = L.map(LMapWrapper.divId, {
|
||||
center: { lat: 0., lng: 0. },
|
||||
zoom: 2,
|
||||
zoomControl: false
|
||||
zoom: 2
|
||||
});
|
||||
|
||||
LMapWrapper.map.on('click', function (e) {
|
||||
@@ -389,7 +388,7 @@
|
||||
|
||||
L.tileLayer(tileUrl, {
|
||||
attribution: tileAttribution,
|
||||
subdomains: '1234',
|
||||
subdomains: tileSubdomains,
|
||||
ppi: highResData.ppi,
|
||||
tileSize: highResData.tileSize,
|
||||
zoomOffset: highResData.zoomOffset,
|
||||
@@ -404,7 +403,7 @@
|
||||
|
||||
LMapWrapper.loadMarkers(places);
|
||||
|
||||
document.getElementById('streetViewCoverSelector').disabled = true;
|
||||
document.getElementById('streetViewCoverSelector').style.display = 'none';
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
@@ -474,7 +473,11 @@
|
||||
LMapWrapper.markers.removeLayer(marker);
|
||||
},
|
||||
|
||||
toggleStreetViewCover: function () { }
|
||||
toggleStreetViewCover: function () { },
|
||||
|
||||
getSearchRadius: function (location) {
|
||||
return 100;
|
||||
}
|
||||
};
|
||||
|
||||
var GMapWrapper = {
|
||||
@@ -517,6 +520,7 @@
|
||||
zoomControlOptions: {
|
||||
position: google.maps.ControlPosition.LEFT_BOTTOM
|
||||
},
|
||||
streetViewControl: false,
|
||||
draggableCursor: 'crosshair'
|
||||
});
|
||||
|
||||
@@ -536,7 +540,7 @@
|
||||
|
||||
GMapWrapper.loadMarkers(places);
|
||||
|
||||
document.getElementById('streetViewCoverSelector').disabled = false;
|
||||
document.getElementById('streetViewCoverSelector').style.display = 'block'
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
@@ -626,6 +630,20 @@
|
||||
GMapWrapper.streetViewCover.setMap(GMapWrapper.map);
|
||||
GMapWrapper.streetViewCoverOn = true;
|
||||
}
|
||||
},
|
||||
|
||||
getSearchRadius: function (location) {
|
||||
// source: https://www.yorku.ca/mack/CHI01.htm
|
||||
var movementOffset = 4;
|
||||
|
||||
// source: https://groups.google.com/g/google-maps-js-api-v3/c/hDRO4oHVSeM/m/osOYQYXg2oUJ?pli=1
|
||||
var metersPerPixel = 156543.03392 * Math.cos(location.lat * Math.PI / 180) / Math.pow(2, GMapWrapper.map.getZoom());
|
||||
|
||||
var minSearchRadius = 5;
|
||||
|
||||
var searchRadius = Math.max(minSearchRadius, Math.round(movementOffset * metersPerPixel));
|
||||
|
||||
return searchRadius;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
})();
|
||||
@@ -27,7 +27,7 @@ class AddUserCommand extends Command
|
||||
$user->setActive(true);
|
||||
$user->setCreatedDate(new DateTime());
|
||||
|
||||
if ($input->hasArgument('type')) {
|
||||
if ($input->hasArgument('type') && $input->getArgument('type') !== null) {
|
||||
$user->setType($input->getArgument('type'));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use MapGuesser\PersistentData\PersistentDataManager;
|
||||
use MapGuesser\Repository\MultiRoomRepository;
|
||||
use MapGuesser\Repository\UserConfirmationRepository;
|
||||
use MapGuesser\Repository\UserPasswordResetterRepository;
|
||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||
use MapGuesser\Repository\UserRepository;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
@@ -25,6 +26,8 @@ class MaintainDatabaseCommand extends Command
|
||||
|
||||
private MultiRoomRepository $multiRoomRepository;
|
||||
|
||||
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
@@ -34,6 +37,7 @@ class MaintainDatabaseCommand extends Command
|
||||
$this->userConfirmationRepository = new UserConfirmationRepository();
|
||||
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
||||
$this->multiRoomRepository = new MultiRoomRepository();
|
||||
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||
}
|
||||
|
||||
public function configure(): void
|
||||
@@ -81,6 +85,10 @@ class MaintainDatabaseCommand extends Command
|
||||
$this->pdm->deleteFromDb($userPasswordResetter);
|
||||
}
|
||||
|
||||
foreach ($this->userPlayedPlaceRepository->getAllByUser($user) as $userPlayedPlace) {
|
||||
$this->pdm->deleteFromDb($userPlayedPlace);
|
||||
}
|
||||
|
||||
$this->pdm->deleteFromDb($user);
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
@@ -114,6 +194,11 @@ class GameController
|
||||
}
|
||||
|
||||
$room = $this->multiRoomRepository->getByRoomId($roomId);
|
||||
|
||||
if(!isset($room)) {
|
||||
return new JsonContent(['error' => 'game_not_found']);
|
||||
}
|
||||
|
||||
$state = $room->getStateArray();
|
||||
$map = $this->mapRepository->getById($state['mapId']);
|
||||
$token = $this->getMultiToken($roomId);
|
||||
@@ -149,6 +234,31 @@ 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)) {
|
||||
// new player is joining
|
||||
$userInChallenge = new UserInChallenge();
|
||||
$userInChallenge->setUserId($userId);
|
||||
$userInChallenge->setChallenge($challenge);
|
||||
$userInChallenge->setTimeLeft($challenge->getTimerSec());
|
||||
$this->pdm->saveToDb($userInChallenge);
|
||||
}
|
||||
|
||||
$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();
|
||||
|
||||
@@ -6,9 +6,19 @@ 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\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;
|
||||
use MapGuesser\Repository\UserRepository;
|
||||
|
||||
class GameFlowController
|
||||
{
|
||||
@@ -25,6 +35,20 @@ class GameFlowController
|
||||
|
||||
private PlaceRepository $placeRepository;
|
||||
|
||||
private MapRepository $mapRepository;
|
||||
|
||||
private UserRepository $userRepository;
|
||||
|
||||
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||
|
||||
private ChallengeRepository $challengeRepository;
|
||||
|
||||
private UserInChallengeRepository $userInChallengeRepository;
|
||||
|
||||
private PlaceInChallengeRepository $placeInChallengeRepository;
|
||||
|
||||
private GuessRepository $guessRepository;
|
||||
|
||||
public function __construct(IRequest $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
@@ -32,6 +56,13 @@ class GameFlowController
|
||||
$this->multiConnector = new MultiConnector();
|
||||
$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();
|
||||
$this->placeInChallengeRepository = new PlaceInChallengeRepository();
|
||||
$this->guessRepository = new GuessRepository();
|
||||
}
|
||||
|
||||
public function initialData(): IContent
|
||||
@@ -110,8 +141,74 @@ class GameFlowController
|
||||
return new JsonContent(['ok' => true]);
|
||||
}
|
||||
|
||||
public function challengeInitialData(): IContent
|
||||
{
|
||||
$session = $this->request->session();
|
||||
$userId = $session->get('userId');
|
||||
$challengeToken_str = $this->request->query('challengeToken');
|
||||
$userInChallenge = $this->userInChallengeRepository->getByUserIdAndToken($userId, $challengeToken_str);
|
||||
|
||||
if (!isset($userInChallenge)) {
|
||||
return new JsonContent(['error' => 'game_not_found']);
|
||||
}
|
||||
|
||||
$challenge = $userInChallenge->getChallenge();
|
||||
$currentRound = $userInChallenge->getRound();
|
||||
$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()]
|
||||
];
|
||||
}
|
||||
|
||||
return new JsonContent($response);
|
||||
}
|
||||
|
||||
public function guess(): IContent
|
||||
{
|
||||
// testing
|
||||
$testPlace = $this->placeRepository->getByIdWithMap(5);
|
||||
|
||||
$mapId = (int) $this->request->query('mapId');
|
||||
$session = $this->request->session();
|
||||
|
||||
@@ -121,7 +218,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'];
|
||||
@@ -145,9 +242,31 @@ class GameFlowController
|
||||
|
||||
$session->set('state', $state);
|
||||
|
||||
$this->saveVisit($last['placeId']);
|
||||
|
||||
return new JsonContent($response);
|
||||
}
|
||||
|
||||
// save the selected place for the round in UserPlayedPlace
|
||||
private function saveVisit($placeId): void
|
||||
{
|
||||
$session = $this->request->session();
|
||||
$userId = $session->get('userId');
|
||||
|
||||
if(isset($userId)) {
|
||||
$userPlayedPlace = $this->userPlayedPlaceRepository->getByUserIdAndPlaceId($userId, $placeId);
|
||||
if(!$userPlayedPlace) {
|
||||
$userPlayedPlace = new UserPlayedPlace();
|
||||
$userPlayedPlace->setUserId($userId);
|
||||
$userPlayedPlace->setPlaceId($placeId);
|
||||
} else {
|
||||
$userPlayedPlace->incrementOccurrences();
|
||||
}
|
||||
$userPlayedPlace->setLastTimeDate(new DateTime());
|
||||
$this->pdm->saveToDb($userPlayedPlace);
|
||||
}
|
||||
}
|
||||
|
||||
public function multiGuess(): IContent
|
||||
{
|
||||
$roomId = $this->request->query('roomId');
|
||||
@@ -162,7 +281,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,
|
||||
@@ -185,6 +304,107 @@ class GameFlowController
|
||||
return new JsonContent($response);
|
||||
}
|
||||
|
||||
public function challengeGuess(): IContent
|
||||
{
|
||||
$session = $this->request->session();
|
||||
$userId = $session->get('userId');
|
||||
$challengeToken_str = $this->request->query('challengeToken');
|
||||
$userInChallenge = $this->userInChallengeRepository->getByUserIdAndToken($userId, $challengeToken_str);
|
||||
|
||||
if (!isset($userInChallenge)) {
|
||||
return new JsonContent(['error' => 'game_not_found']);
|
||||
}
|
||||
|
||||
$challenge = $userInChallenge->getChallenge();
|
||||
$currentRound = $userInChallenge->getRound();
|
||||
$currentPlace = $this->placeRepository->getByRoundInChallenge($challenge, $currentRound);
|
||||
$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());
|
||||
|
||||
// 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
|
||||
// ];
|
||||
$response['position'] = $currentPlace->getPosition()->toArray();
|
||||
$response['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()
|
||||
];
|
||||
|
||||
} 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);
|
||||
}
|
||||
|
||||
public function multiNextRound(): IContent
|
||||
{
|
||||
$roomId = $this->request->query('roomId');
|
||||
@@ -214,7 +434,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);
|
||||
@@ -224,7 +444,10 @@ class GameFlowController
|
||||
|
||||
private function startNewGame(array &$state, int $mapId): void
|
||||
{
|
||||
$places = $this->placeRepository->getRandomNForMapWithValidPano($mapId, static::NUMBER_OF_ROUNDS);
|
||||
$session = $this->request->session();
|
||||
$userId = $session->get('userId');
|
||||
|
||||
$places = $this->placeRepository->getRandomNPlaces($mapId, static::NUMBER_OF_ROUNDS, $userId);
|
||||
|
||||
$state['rounds'] = [];
|
||||
$state['currentRound'] = 0;
|
||||
|
||||
@@ -14,6 +14,7 @@ use MapGuesser\PersistentData\Model\UserPasswordResetter;
|
||||
use MapGuesser\PersistentData\PersistentDataManager;
|
||||
use MapGuesser\Repository\UserConfirmationRepository;
|
||||
use MapGuesser\Repository\UserPasswordResetterRepository;
|
||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||
use MapGuesser\Repository\UserRepository;
|
||||
use MapGuesser\Response\HtmlContent;
|
||||
use MapGuesser\Response\JsonContent;
|
||||
@@ -32,6 +33,8 @@ class LoginController
|
||||
|
||||
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
||||
|
||||
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||
|
||||
public function __construct(IRequest $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
@@ -39,6 +42,7 @@ class LoginController
|
||||
$this->userRepository = new UserRepository();
|
||||
$this->userConfirmationRepository = new UserConfirmationRepository();
|
||||
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
||||
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||
}
|
||||
|
||||
public function getLoginForm()
|
||||
@@ -430,6 +434,10 @@ class LoginController
|
||||
|
||||
$user = $this->userRepository->getById($confirmation->getUserId());
|
||||
|
||||
foreach ($this->userPlayedPlaceRepository->getAllByUser($user) as $userPlayedPlace) {
|
||||
$this->pdm->deleteFromDb($userPlayedPlace);
|
||||
}
|
||||
|
||||
$this->pdm->deleteFromDb($user);
|
||||
|
||||
\Container::$dbConnection->commit();
|
||||
|
||||
@@ -10,6 +10,7 @@ use MapGuesser\PersistentData\Model\Place;
|
||||
use MapGuesser\PersistentData\PersistentDataManager;
|
||||
use MapGuesser\Repository\MapRepository;
|
||||
use MapGuesser\Repository\PlaceRepository;
|
||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||
use MapGuesser\Response\HtmlContent;
|
||||
use MapGuesser\Response\JsonContent;
|
||||
use MapGuesser\Util\Geo\Bounds;
|
||||
@@ -27,12 +28,15 @@ class MapAdminController implements ISecured
|
||||
|
||||
private PlaceRepository $placeRepository;
|
||||
|
||||
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||
|
||||
public function __construct(IRequest $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->pdm = new PersistentDataManager();
|
||||
$this->mapRepository = new MapRepository();
|
||||
$this->placeRepository = new PlaceRepository();
|
||||
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
@@ -138,7 +142,7 @@ class MapAdminController implements ISecured
|
||||
|
||||
$place = $this->placeRepository->getById((int) $placeRaw['id']);
|
||||
|
||||
$this->pdm->deleteFromDb($place);
|
||||
$this->deletePlace($place);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,10 +182,19 @@ class MapAdminController implements ISecured
|
||||
return new JsonContent(['success' => true]);
|
||||
}
|
||||
|
||||
private function deletePlace(Place $place): void
|
||||
{
|
||||
foreach ($this->userPlayedPlaceRepository->getAllByPlace($place) as $userPlayedPlace) {
|
||||
$this->pdm->deleteFromDb($userPlayedPlace);
|
||||
}
|
||||
|
||||
$this->pdm->deleteFromDb($place);
|
||||
}
|
||||
|
||||
private function deletePlaces(Map $map): void
|
||||
{
|
||||
foreach ($this->placeRepository->getAllForMap($map) as $place) {
|
||||
$this->pdm->deleteFromDb($place);
|
||||
$this->deletePlace($place);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use MapGuesser\PersistentData\PersistentDataManager;
|
||||
use MapGuesser\PersistentData\Model\User;
|
||||
use MapGuesser\Repository\UserConfirmationRepository;
|
||||
use MapGuesser\Repository\UserPasswordResetterRepository;
|
||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||
use MapGuesser\Response\HtmlContent;
|
||||
use MapGuesser\Response\JsonContent;
|
||||
use MapGuesser\Response\Redirect;
|
||||
@@ -26,12 +27,15 @@ class UserController implements ISecured
|
||||
|
||||
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
||||
|
||||
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||
|
||||
public function __construct(IRequest $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->pdm = new PersistentDataManager();
|
||||
$this->userConfirmationRepository = new UserConfirmationRepository();
|
||||
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
||||
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
@@ -201,6 +205,10 @@ class UserController implements ISecured
|
||||
$this->pdm->deleteFromDb($userPasswordResetter);
|
||||
}
|
||||
|
||||
foreach ($this->userPlayedPlaceRepository->getAllByUser($user) as $userPlayedPlace) {
|
||||
$this->pdm->deleteFromDb($userPlayedPlace);
|
||||
}
|
||||
|
||||
$this->pdm->deleteFromDb($user);
|
||||
|
||||
\Container::$dbConnection->commit();
|
||||
@@ -210,7 +218,7 @@ class UserController implements ISecured
|
||||
return new JsonContent(['success' => true]);
|
||||
}
|
||||
|
||||
private function confirmUserIdentity(User $user, ?DateTime $authenticatedWithGoogleUntil, ?string $password, string &$error): bool
|
||||
private function confirmUserIdentity(User $user, ?DateTime $authenticatedWithGoogleUntil, ?string $password, ?string &$error): bool
|
||||
{
|
||||
if ($authenticatedWithGoogleUntil !== null && $authenticatedWithGoogleUntil > new DateTime()) {
|
||||
return true;
|
||||
|
||||
@@ -12,6 +12,8 @@ class Select
|
||||
|
||||
const CONDITION_HAVING = 1;
|
||||
|
||||
const DERIVED_TABLE_KEY = 'DERIVED';
|
||||
|
||||
private IConnection $connection;
|
||||
|
||||
private string $table;
|
||||
@@ -55,6 +57,11 @@ class Select
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDerivedTableAlias(string $tableAlias): Select
|
||||
{
|
||||
return $this->setTableAliases([Select::DERIVED_TABLE_KEY => $tableAlias]);
|
||||
}
|
||||
|
||||
public function from(string $table): Select
|
||||
{
|
||||
$this->table = $table;
|
||||
@@ -194,6 +201,11 @@ class Select
|
||||
}
|
||||
}
|
||||
|
||||
private function isDerivedTable(): bool
|
||||
{
|
||||
return array_key_exists(Select::DERIVED_TABLE_KEY, $this->tableAliases);
|
||||
}
|
||||
|
||||
private function addJoin(string $type, $table, $column1, string $relation, $column2): void
|
||||
{
|
||||
$this->joins[] = [$type, $table, $column1, $relation, $column2];
|
||||
@@ -211,10 +223,14 @@ class Select
|
||||
|
||||
private function generateQuery(): array
|
||||
{
|
||||
$queryString = 'SELECT ' . $this->generateColumns() . ' FROM ' . $this->generateTable($this->table, true);
|
||||
list($tableQuery, $tableParams) = $this->generateTable($this->table, true);
|
||||
$queryString = 'SELECT ' . $this->generateColumns() . ' FROM ' . $tableQuery;
|
||||
|
||||
if (count($this->joins) > 0) {
|
||||
$queryString .= ' ' . $this->generateJoins();
|
||||
list($joinQuery, $joinParams) = $this->generateJoins();
|
||||
$queryString .= ' ' . $joinQuery;
|
||||
} else {
|
||||
$joinParams = [];
|
||||
}
|
||||
|
||||
if (count($this->conditions[self::CONDITION_WHERE]) > 0) {
|
||||
@@ -245,20 +261,32 @@ class Select
|
||||
$queryString .= ' LIMIT ' . $this->limit[1] . ', ' . $this->limit[0];
|
||||
}
|
||||
|
||||
return [$queryString, array_merge($whereParams, $havingParams)];
|
||||
if($this->isDerivedTable()) {
|
||||
$queryString = '(' . $queryString . ') AS ' . $this->tableAliases[Select::DERIVED_TABLE_KEY];
|
||||
}
|
||||
|
||||
return [$queryString, array_merge($tableParams, $joinParams, $whereParams, $havingParams)];
|
||||
}
|
||||
|
||||
private function generateTable($table, bool $defineAlias = false): string
|
||||
private function generateTable($table, bool $defineAlias = false): array
|
||||
{
|
||||
$params = [];
|
||||
|
||||
if ($table instanceof RawExpression) {
|
||||
return (string) $table;
|
||||
return [(string) $table, $params];
|
||||
}
|
||||
|
||||
if($table instanceof Select)
|
||||
{
|
||||
return $table->generateQuery();
|
||||
}
|
||||
|
||||
if (isset($this->tableAliases[$table])) {
|
||||
return ($defineAlias ? Utils::backtick($this->tableAliases[$table]) . ' ' . Utils::backtick($table) : Utils::backtick($table));
|
||||
$queryString = ($defineAlias ? Utils::backtick($this->tableAliases[$table]) . ' ' . Utils::backtick($table) : Utils::backtick($table));
|
||||
return [$queryString, $params];
|
||||
}
|
||||
|
||||
return Utils::backtick($table);
|
||||
return [Utils::backtick($table), $params];
|
||||
}
|
||||
|
||||
private function generateColumn($column): string
|
||||
@@ -271,7 +299,8 @@ class Select
|
||||
$out = '';
|
||||
|
||||
if ($column[0]) {
|
||||
$out .= $this->generateTable($column[0]) . '.';
|
||||
list($tableName, $params) = $this->generateTable($column[0]);
|
||||
$out .= $tableName . '.';
|
||||
}
|
||||
|
||||
$out .= Utils::backtick($column[1]);
|
||||
@@ -297,15 +326,19 @@ class Select
|
||||
return implode(',', $columns);
|
||||
}
|
||||
|
||||
private function generateJoins(): string
|
||||
private function generateJoins(): array
|
||||
{
|
||||
$joins = $this->joins;
|
||||
|
||||
array_walk($joins, function (&$value, $key) {
|
||||
$value = $value[0] . ' JOIN ' . $this->generateTable($value[1], true) . ' ON ' . $this->generateColumn($value[2]) . ' ' . $value[3] . ' ' . $this->generateColumn($value[4]);
|
||||
});
|
||||
$joinQueries = [];
|
||||
$params = [];
|
||||
|
||||
return implode(' ', $joins);
|
||||
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);
|
||||
}
|
||||
|
||||
return [implode(' ', $joinQueries), $params];
|
||||
}
|
||||
|
||||
private function generateConditions(int $type): array
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php namespace MapGuesser\PersistentData\Model;
|
||||
|
||||
use DateTime;
|
||||
|
||||
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 $relations = [];
|
||||
|
||||
private int $token;
|
||||
|
||||
private ?int $timerSec = null;
|
||||
|
||||
private bool $noMove = false;
|
||||
|
||||
private bool $noPan = false;
|
||||
|
||||
private bool $noZoom = false;
|
||||
|
||||
private DateTime $created;
|
||||
|
||||
public function setToken(int $token): void
|
||||
{
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function setTimerSec(?int $timerSec): void
|
||||
{
|
||||
if(isset($timerSec)) {
|
||||
$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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php namespace MapGuesser\PersistentData\Model;
|
||||
|
||||
use MapGuesser\Util\Geo\Position;
|
||||
|
||||
class Guess extends Model
|
||||
{
|
||||
protected static string $table = 'guesses';
|
||||
|
||||
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];
|
||||
|
||||
private ?User $user = null;
|
||||
|
||||
private ?int $userId = null;
|
||||
|
||||
private ?PlaceInChallenge $placeInChallenge = null;
|
||||
|
||||
private ?int $placeInChallengeId = null;
|
||||
|
||||
private Position $position;
|
||||
|
||||
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
|
||||
{
|
||||
$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 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;
|
||||
}
|
||||
|
||||
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 getScore(): int
|
||||
{
|
||||
return $this->score;
|
||||
}
|
||||
|
||||
public function getDistance(): int
|
||||
{
|
||||
return $this->distance;
|
||||
}
|
||||
|
||||
public function getTimeSpent(): ?int
|
||||
{
|
||||
return $this->timeSpent;
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ class Place extends Model
|
||||
return $this->pov->getZoom();
|
||||
}
|
||||
|
||||
public function getFreshPanoId(bool $canBeIndoor = false): ?string
|
||||
public function getFreshPanoId(): ?string
|
||||
{
|
||||
if (
|
||||
$this->panoIdCachedTimestamp !== null &&
|
||||
@@ -152,19 +152,13 @@ class Place extends Model
|
||||
$request = new Request('https://maps.googleapis.com/maps/api/streetview/metadata', Request::HTTP_GET);
|
||||
$request->setQuery([
|
||||
'key' => $_ENV['GOOGLE_MAPS_SERVER_API_KEY'],
|
||||
'location' => $this->position->getLat() . ',' . $this->position->getLng(),
|
||||
'source' => $canBeIndoor ? 'default' : 'outdoor'
|
||||
'location' => $this->position->getLat() . ',' . $this->position->getLng()
|
||||
]);
|
||||
|
||||
$response = $request->send();
|
||||
$panoData = json_decode($response->getBody(), true);
|
||||
$panoId = $panoData['status'] === 'OK' ? $panoData['pano_id'] : null;
|
||||
|
||||
// enable indoor panos if no outdoor found
|
||||
if ($panoId === null && !$canBeIndoor) {
|
||||
return $this->getFreshPanoId(true);
|
||||
}
|
||||
|
||||
$this->panoIdCached = $panoId;
|
||||
$this->panoIdCachedTimestamp = new DateTime();
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php namespace MapGuesser\PersistentData\Model;
|
||||
|
||||
class PlaceInChallenge extends Model
|
||||
{
|
||||
protected static string $table = 'place_in_challenge';
|
||||
|
||||
protected static array $fields = ['place_id', 'challenge_id', 'order'];
|
||||
|
||||
protected static array $relations = ['place' => 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php namespace MapGuesser\PersistentData\Model;
|
||||
|
||||
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 $relations = ['user' => User::class, 'challenge' => Challenge::class];
|
||||
|
||||
private ?User $user = null;
|
||||
|
||||
private ?int $userId = null;
|
||||
|
||||
private ?Challenge $challenge = null;
|
||||
|
||||
private ?int $challengeId = null;
|
||||
|
||||
private int $round = 0;
|
||||
|
||||
private ?int $timeLeft = null;
|
||||
|
||||
private bool $isOwner = false;
|
||||
|
||||
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 setTimeLeft(?int $timeLeft): void
|
||||
{
|
||||
if(isset($timeLeft)) {
|
||||
$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 getTimeLeft(): ?int
|
||||
{
|
||||
return $this->timeLeft;
|
||||
}
|
||||
|
||||
public function getIsOwner(): bool
|
||||
{
|
||||
return $this->isOwner;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php namespace MapGuesser\PersistentData\Model;
|
||||
|
||||
use DateTime;
|
||||
|
||||
class UserPlayedPlace extends Model
|
||||
{
|
||||
protected static string $table = 'user_played_place';
|
||||
|
||||
protected static array $fields = ['user_id', 'place_id', 'last_time', 'occurrences'];
|
||||
|
||||
protected static array $relations = ['user' => User::class, 'place' => Place::class];
|
||||
|
||||
private ?User $user = null;
|
||||
|
||||
private ?int $userId = null;
|
||||
|
||||
private ?Place $place = null;
|
||||
|
||||
private ?int $placeId = null;
|
||||
|
||||
private DateTime $lastTime;
|
||||
|
||||
private int $occurrences = 1;
|
||||
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function setUserId(int $userId): void
|
||||
{
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
public function setPlace(Place $place): void
|
||||
{
|
||||
$this->place = $place;
|
||||
}
|
||||
|
||||
public function setPlaceId(int $placeId): void
|
||||
{
|
||||
$this->placeId = $placeId;
|
||||
}
|
||||
|
||||
public function setLastTimeDate(DateTime $lastTime): void
|
||||
{
|
||||
$this->lastTime = $lastTime;
|
||||
}
|
||||
|
||||
public function setLastTime(string $lastTime): void
|
||||
{
|
||||
$this->lastTime = new DateTime($lastTime);
|
||||
}
|
||||
|
||||
public function setOccurrences(int $occurrences): void
|
||||
{
|
||||
$this->occurrences = $occurrences;
|
||||
}
|
||||
|
||||
public function incrementOccurrences(): void
|
||||
{
|
||||
$this->occurrences++;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function getPlace(): ?Place
|
||||
{
|
||||
return $this->place;
|
||||
}
|
||||
|
||||
public function getPlaceId(): ?int
|
||||
{
|
||||
return $this->placeId;
|
||||
}
|
||||
|
||||
public function getLastTimeDate(): DateTime
|
||||
{
|
||||
return $this->lastTime;
|
||||
}
|
||||
|
||||
public function getLastTime(): string
|
||||
{
|
||||
return $this->lastTime->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
public function getOccurrences(): int
|
||||
{
|
||||
return $this->occurrences;
|
||||
}
|
||||
}
|
||||
@@ -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,24 +171,35 @@ 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);
|
||||
|
||||
$columns = [];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$columns[] = [$table, $field];
|
||||
}
|
||||
|
||||
$select->from($table);
|
||||
|
||||
//TODO: only with some relations?
|
||||
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;
|
||||
@@ -165,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;
|
||||
@@ -175,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php namespace MapGuesser\Repository;
|
||||
|
||||
use Generator;
|
||||
use MapGuesser\Database\Query\Select;
|
||||
use MapGuesser\PersistentData\Model\Challenge;
|
||||
use MapGuesser\PersistentData\Model\User;
|
||||
use MapGuesser\PersistentData\PersistentDataManager;
|
||||
|
||||
class ChallengeRepository
|
||||
{
|
||||
private PersistentDataManager $pdm;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->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 getByTokenStr(string $token_str): ?Challenge
|
||||
{
|
||||
$token = hexdec($token_str);
|
||||
|
||||
return $this->getByToken($token);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php namespace MapGuesser\Repository;
|
||||
|
||||
use Generator;
|
||||
use MapGuesser\Database\Query\Select;
|
||||
use MapGuesser\PersistentData\Model\Challenge;
|
||||
use MapGuesser\PersistentData\Model\Guess;
|
||||
use MapGuesser\PersistentData\Model\User;
|
||||
use MapGuesser\PersistentData\Model\UserInChallenge;
|
||||
use MapGuesser\PersistentData\Model\Place;
|
||||
use MapGuesser\PersistentData\PersistentDataManager;
|
||||
|
||||
class GuessRepository
|
||||
{
|
||||
private PersistentDataManager $pdm;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->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'], '=', ['guesses', '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'], '=', ['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 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']);
|
||||
$select->where('user_id', '=', $userId);
|
||||
$select->where('challenge_id', '=', $challenge->getId());
|
||||
$select->orderBy('order');
|
||||
|
||||
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());
|
||||
$select->orderBy('order');
|
||||
|
||||
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);
|
||||
|
||||
yield from $this->pdm->selectMultipleFromDb($select, Guess::class);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
<?php namespace MapGuesser\Repository;
|
||||
|
||||
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
|
||||
@@ -16,4 +19,20 @@ class MapRepository
|
||||
{
|
||||
return $this->pdm->selectFromDbById($mapId, Map::class);
|
||||
}
|
||||
|
||||
public function getByPlace(Place $place): ?Map
|
||||
{
|
||||
return $this->getById($place->getMapId());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php namespace MapGuesser\Repository;
|
||||
|
||||
use Generator;
|
||||
use MapGuesser\Database\Query\Select;
|
||||
use MapGuesser\PersistentData\Model\Challenge;
|
||||
use MapGuesser\PersistentData\Model\Place;
|
||||
use MapGuesser\PersistentData\Model\PlaceInChallenge;
|
||||
use MapGuesser\PersistentData\PersistentDataManager;
|
||||
|
||||
class PlaceInChallengeRepository
|
||||
{
|
||||
private PersistentDataManager $pdm;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -28,13 +29,35 @@ class PlaceRepository
|
||||
yield from $this->pdm->selectMultipleFromDb($select, Place::class);
|
||||
}
|
||||
|
||||
//TODO: use Map and User instead of id
|
||||
public function getRandomNPlaces(int $mapId, int $n, ?int $userId): array
|
||||
{
|
||||
if (!isset($userId)) { // anonymous single player
|
||||
return $this->getRandomNForMapWithValidPano($mapId, $n);
|
||||
} else { // authorized user or multiplayer game with selection based on what the host played before
|
||||
$unvisitedPlaces = $this->getRandomUnvisitedNForMapWithValidPano($mapId, $n, $userId);
|
||||
if (count($unvisitedPlaces) == $n) {
|
||||
return $unvisitedPlaces;
|
||||
}
|
||||
|
||||
$oldPlaces = $this->getRandomOldNForMapWithValidPano($mapId, $n - count($unvisitedPlaces), $userId);
|
||||
|
||||
return array_merge($unvisitedPlaces, $oldPlaces);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: use Map instead of id
|
||||
public function getRandomNForMapWithValidPano(int $mapId, int $n, array $exclude = []): array
|
||||
private function getRandomNForMapWithValidPano(int $mapId, int $n): array
|
||||
{
|
||||
$places = [];
|
||||
|
||||
$select = new Select(\Container::$dbConnection, 'places');
|
||||
$select->where('map_id', '=', $mapId);
|
||||
$numberOfPlaces = $select->count();
|
||||
|
||||
$exclude = [];
|
||||
for ($i = 1; $i <= $n; ++$i) {
|
||||
$place = $this->getRandomForMapWithValidPano($mapId, $exclude);
|
||||
$place = $this->getRandomForMapWithValidPano($numberOfPlaces, $select, $exclude);
|
||||
|
||||
$places[] = $place;
|
||||
$exclude[] = $place->getId();
|
||||
@@ -43,11 +66,15 @@ class PlaceRepository
|
||||
return $places;
|
||||
}
|
||||
|
||||
//TODO: use Map instead of id
|
||||
public function getRandomForMapWithValidPano(int $mapId, array $exclude = []): Place
|
||||
private function getRandomForMapWithValidPano(int $numberOfPlaces, Select $select, array &$exclude, ?callable $pickRandomInt = null): ?Place
|
||||
{
|
||||
do {
|
||||
$place = $this->selectRandomFromDbForMap($mapId, $exclude);
|
||||
$numberOfPlacesLeft = $numberOfPlaces - count($exclude);
|
||||
$place = $this->selectRandomFromDbForMap($numberOfPlacesLeft, $select, $exclude, $pickRandomInt);
|
||||
if ($place === null) {
|
||||
// there is no more never visited place left
|
||||
return null;
|
||||
}
|
||||
|
||||
$panoId = $place->getFreshPanoId();
|
||||
if ($panoId === null) {
|
||||
@@ -58,25 +85,127 @@ class PlaceRepository
|
||||
return $place;
|
||||
}
|
||||
|
||||
private function selectRandomFromDbForMap(int $mapId, array $exclude): Place
|
||||
private function selectRandomFromDbForMap(int $numberOfPlacesLeft, Select $select, array $exclude, ?callable $pickRandomInt): ?Place
|
||||
{
|
||||
//TODO: omit table name here
|
||||
$select = new Select(\Container::$dbConnection, 'places');
|
||||
$select->where('id', 'NOT IN', $exclude);
|
||||
$select->where('map_id', '=', $mapId);
|
||||
if ($numberOfPlacesLeft <= 0)
|
||||
return null;
|
||||
|
||||
$numberOfPlaces = $select->count();
|
||||
|
||||
//TODO: prevent this
|
||||
if ($numberOfPlaces === 0) {
|
||||
throw new \Exception('There is no selectable place (count was 0).');
|
||||
if (!isset($pickRandomInt)) {
|
||||
$randomOffset = random_int(0, $numberOfPlacesLeft - 1);
|
||||
} else {
|
||||
$randomOffset = $pickRandomInt($numberOfPlacesLeft);
|
||||
}
|
||||
|
||||
$randomOffset = random_int(0, $numberOfPlaces - 1);
|
||||
|
||||
$select->orderBy('id');
|
||||
$select->where('id', 'NOT IN', $exclude);
|
||||
$select->limit(1, $randomOffset);
|
||||
|
||||
return $this->pdm->selectFromDb($select, Place::class);
|
||||
}
|
||||
|
||||
// Never visited places
|
||||
private function getRandomUnvisitedNForMapWithValidPano(int $mapId, int $n, int $userId): array
|
||||
{
|
||||
$places = [];
|
||||
$exclude = [];
|
||||
|
||||
// list of places visited by user
|
||||
$selectPlacesByCurrentUser = new Select(\Container::$dbConnection, 'user_played_place');
|
||||
$selectPlacesByCurrentUser->columns(['place_id', 'last_time']);
|
||||
$selectPlacesByCurrentUser->where('user_id', '=', $userId);
|
||||
$selectPlacesByCurrentUser->setDerivedTableAlias('places_by_current_user');
|
||||
|
||||
// count the places never visited
|
||||
$selectUnvisited = new Select(\Container::$dbConnection, 'places');
|
||||
$selectUnvisited->leftJoin($selectPlacesByCurrentUser, ['places', 'id'], '=', ['places_by_current_user', 'place_id']);
|
||||
$selectUnvisited->where('map_id', '=', $mapId);
|
||||
$selectUnvisited->where('last_time', '=', null);
|
||||
$numberOfUnvisitedPlaces = $selectUnvisited->count();
|
||||
|
||||
// look for as many new places as possible but maximum $n
|
||||
do {
|
||||
$place = $this->getRandomForMapWithValidPano($numberOfUnvisitedPlaces, $selectUnvisited, $exclude);
|
||||
if (isset($place)) {
|
||||
$places[] = $place;
|
||||
$exclude[] = $place->getId();
|
||||
}
|
||||
} while (count($places) < $n && isset($place));
|
||||
|
||||
return $places;
|
||||
}
|
||||
|
||||
// Places visited in the longest time
|
||||
private function getRandomOldNForMapWithValidPano(int $mapId, int $n, int $userId): array
|
||||
{
|
||||
$places = [];
|
||||
$exclude = [];
|
||||
|
||||
// list of places visited by user
|
||||
$selectPlacesByCurrentUser = new Select(\Container::$dbConnection, 'user_played_place');
|
||||
$selectPlacesByCurrentUser->columns(['place_id', 'last_time']);
|
||||
$selectPlacesByCurrentUser->where('user_id', '=', $userId);
|
||||
$selectPlacesByCurrentUser->setDerivedTableAlias('places_by_current_user');
|
||||
|
||||
// count places that were visited at least once
|
||||
$selectOldPlaces = new Select(\Container::$dbConnection, 'places');
|
||||
$selectOldPlaces->innerJoin($selectPlacesByCurrentUser, ['places', 'id'], '=', ['places_by_current_user', 'place_id']);
|
||||
$selectOldPlaces->where('map_id', '=', $mapId);
|
||||
$numberOfOldPlaces = $selectOldPlaces->count();
|
||||
|
||||
// set order by datetime, oldest first
|
||||
$selectOldPlaces->orderBy('last_time');
|
||||
|
||||
// selection algorithm with preference (weighting) for older places using Box-Muller transform
|
||||
$pickGaussianRandomInt = function($numberOfPlaces) {
|
||||
$stdev = 0.2;
|
||||
$avg = 0.0;
|
||||
$x = mt_rand() / mt_getrandmax();
|
||||
$y = mt_rand() / mt_getrandmax();
|
||||
$randomNum = abs(sqrt(-2 * log($x)) * cos(2 * pi() * $y) * $stdev + $avg);
|
||||
return (int) min($randomNum * $numberOfPlaces, $numberOfPlaces - 1);
|
||||
};
|
||||
|
||||
// look for n - numberOfUnvisitedPlaces places
|
||||
while (count($places) < $n)
|
||||
{
|
||||
$place = $this->getRandomForMapWithValidPano($numberOfOldPlaces, $selectOldPlaces, $exclude, $pickGaussianRandomInt);
|
||||
if (isset($place))
|
||||
{
|
||||
$places[] = $place;
|
||||
$exclude[] = $place->getId();
|
||||
}
|
||||
}
|
||||
|
||||
return $places;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php namespace MapGuesser\Repository;
|
||||
|
||||
use Generator;
|
||||
use MapGuesser\Database\Query\Select;
|
||||
use MapGuesser\PersistentData\Model\Challenge;
|
||||
use MapGuesser\PersistentData\Model\User;
|
||||
use MapGuesser\PersistentData\Model\UserInChallenge;
|
||||
use MapGuesser\PersistentData\PersistentDataManager;
|
||||
|
||||
class UserInChallengeRepository
|
||||
{
|
||||
private PersistentDataManager $pdm;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
|
||||
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 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');
|
||||
$select->where('user_id', '=', $userId);
|
||||
$select->where('challenge_id', '=', $challenge->getId());
|
||||
|
||||
return $select->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php namespace MapGuesser\Repository;
|
||||
|
||||
use DateTime;
|
||||
use Generator;
|
||||
use MapGuesser\Database\Query\Select;
|
||||
use MapGuesser\PersistentData\Model\User;
|
||||
use MapGuesser\PersistentData\Model\Place;
|
||||
use MapGuesser\PersistentData\Model\UserPlayedPlace;
|
||||
use MapGuesser\PersistentData\PersistentDataManager;
|
||||
|
||||
class UserPlayedPlaceRepository
|
||||
{
|
||||
private PersistentDataManager $pdm;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->pdm = new PersistentDataManager();
|
||||
}
|
||||
|
||||
public function getByUser(User $user): Generator
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection);
|
||||
$select->where('user_id', '=', $user->getId());
|
||||
|
||||
yield from $this->pdm->selectMultipleFromDb($select, UserPlayedPlace::class);
|
||||
}
|
||||
|
||||
public function getAllByPlace(Place $place): Generator
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection);
|
||||
$select->where('place_id', '=', $place->getId());
|
||||
|
||||
yield from $this->pdm->selectMultipleFromDb($select, UserPlayedPlace::class);
|
||||
}
|
||||
|
||||
public function getAllByUser(User $user) : Generator
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection);
|
||||
$select->where('user_id', '=', $user->getId());
|
||||
|
||||
yield from $this->pdm->selectMultipleFromDb($select, UserPlayedPlace::class, true);
|
||||
}
|
||||
|
||||
public function getByUserIdAndPlaceId(int $userId, int $placeId) : ?UserPlayedPlace
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection);
|
||||
$select->where('user_id', '=', $userId);
|
||||
$select->where('place_id', '=', $placeId);
|
||||
|
||||
return $this->pdm->selectFromDb($select, UserPlayedPlace::class);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -11,18 +11,13 @@
|
||||
|
||||
@extends(templates/layout_full)
|
||||
|
||||
@section(subheader)
|
||||
@section(subheader)
|
||||
<span><a href="javascript:;" id="mapName" title="Edit map data"><?= $mapName ?></a></span><!--
|
||||
--><span class="inline hideOnMobile" id="mapSelection">
|
||||
<button id="mapSelector">
|
||||
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/map.svg?rev=<?= REVISION ?>" alt="Map Selector" />
|
||||
</button>
|
||||
</span><!--
|
||||
--><span class="inline hideOnMobile" id="mapSelection">
|
||||
<button id="streetViewCoverSelector">
|
||||
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/street-view-cover.svg?rev=<?= REVISION ?>" alt="Street View Conver" />
|
||||
</button>
|
||||
</span><!--
|
||||
--><span class="inline hideOnMobile">
|
||||
<input type="text" id="jumpCoordinates" placeholder="Insert coordinates here" />
|
||||
<button id="jumpButton" disabled >Jump</button>
|
||||
@@ -71,6 +66,16 @@
|
||||
<div id="map" class="map">
|
||||
<div id="lmap" class="map"></div>
|
||||
<div id="gmap" class="map"></div>
|
||||
<div id="overlayControl" class="circleControl">
|
||||
<div id="streetViewCoverSelector" class="controlItem" title="Toggle Street View Cover">
|
||||
<div>
|
||||
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/circle_background.svg?rev=<?= REVISION ?>" alt="Circle Background" class="controlBackground" />
|
||||
</div>
|
||||
<div>
|
||||
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/street-view-cover.svg?rev=<?= REVISION ?>" alt="Street View Cover" class="controlIcon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="panorama"></div>
|
||||
<div id="noPano">
|
||||
@@ -89,6 +94,7 @@
|
||||
@section(pageScript)
|
||||
<script>
|
||||
var tileUrl = '<?= $_ENV['LEAFLET_TILESERVER_URL'] ?>';
|
||||
var tileSubdomains = '<?= $_ENV['LEAFLET_TILESERVER_SUBDOMAINS'] ?>';
|
||||
var tileAttribution = '<?= $_ENV['LEAFLET_TILESERVER_ATTRIBUTION'] ?>';
|
||||
var mapId = <?= $mapId ?>;
|
||||
var mapBounds = <?= json_encode($bounds) ?>;
|
||||
|
||||
+8
-7
@@ -59,21 +59,21 @@
|
||||
<button id="startNewGameButton" class="fullWidth">Play this map again</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="navigation">
|
||||
<div id="compassContainer" class="navigationItem">
|
||||
<div id="navigation" class="circleControl">
|
||||
<div id="compassContainer" class="controlItem">
|
||||
<div>
|
||||
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/circle_background.svg?rev=<?= REVISION ?>" alt="Circle Background" class="circleBackground" />
|
||||
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/circle_background.svg?rev=<?= REVISION ?>" alt="Circle Background" class="controlBackground" />
|
||||
</div>
|
||||
<div>
|
||||
<img id="compass" src="<?= $_ENV['STATIC_ROOT'] ?>/img/compass.svg?rev=<?= REVISION ?>" alt="compass icon" class="navigationIcon" />
|
||||
<img id="compass" src="<?= $_ENV['STATIC_ROOT'] ?>/img/compass.svg?rev=<?= REVISION ?>" alt="compass icon" class="controlIcon" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="returnToStart" class="navigationItem">
|
||||
<div id="returnToStart" class="controlItem">
|
||||
<div>
|
||||
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/circle_background.svg?rev=<?= REVISION ?>" alt="Circle Background" class="circleBackground" />
|
||||
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/circle_background.svg?rev=<?= REVISION ?>" alt="Circle Background" class="controlBackground" />
|
||||
</div>
|
||||
<div>
|
||||
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/house-fill.svg?rev=<?= REVISION ?>" alt="Return to Start icon" class="navigationIcon" />
|
||||
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/house-fill.svg?rev=<?= REVISION ?>" alt="Return to Start icon" class="controlIcon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -84,5 +84,6 @@
|
||||
var multiUrl = '<?= $_ENV['MULTI_WS_URL'] ?>';
|
||||
var roomId = <?= isset($roomId) ? '\'' . $roomId . '\'' : 'null' ?>;
|
||||
var mapId = <?= isset($mapId) ? '\'' . $mapId . '\'' : 'null' ?>;
|
||||
var challengeToken = <?= isset($challengeToken) ? '\'' . $challengeToken . '\'' : 'null' ?>;
|
||||
</script>
|
||||
@endsection
|
||||
@@ -11,6 +11,8 @@ TODO: condition!
|
||||
<a id="singleButton" class="button fullWidth marginTop" href="" title="Single player">Single player</a>
|
||||
<p class="bold center marginTop marginBottom">OR</p>
|
||||
<button id="multiButton" class="fullWidth green" data-map-id="">Multiplayer (beta)</button>
|
||||
<p class="bold center marginTop marginBottom">OR</p>
|
||||
<button id="challengeButton" class="fullWidth yellow" data-map-id="" data-timer="">Challenge (gamma)</button>
|
||||
<div class="right">
|
||||
<button id="closePlayModeButton" class="gray marginTop" type="button">Close</button>
|
||||
</div>
|
||||
@@ -29,6 +31,47 @@ TODO: condition!
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="challenge" class="modal">
|
||||
<h2>Challenge (gamma)</h2>
|
||||
<form id="challengeForm" class="marginTop" method="get">
|
||||
<!--
|
||||
<div class="inputWithButton">
|
||||
<input type="text" name="challengeToken" placeholder="Challenge to enter" required minlength="6" maxlength="6">
|
||||
</div>
|
||||
<p class="bold center marginTop marginBottom">OR</p>
|
||||
-->
|
||||
|
||||
<div id="restrictions">
|
||||
<h3>Optional restrictions</h3>
|
||||
<div>
|
||||
<div>
|
||||
<input type="checkbox" id="timerEnabled" name="timerEnabled" value="timerEnabled" />
|
||||
<label id="timeLimitLabel" for="timerEnabled">Time limit measured in seconds</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="range" id="timeLimit" name="timeLimit" min="10" max="600" value="120" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="noMove" name="noMove" value="noMove" />
|
||||
<label for="noMove">No movement allowed</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="noPan" name="noPan" value="noPan" />
|
||||
<label for="noPan">No camera rotation allowed</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="noZoom" name="noZoom" value="noZoom" />
|
||||
<label for="noMove">No zoom allowed</label>
|
||||
</div>
|
||||
<input type="hidden" name="mapId" id="challengeMapId" />
|
||||
</div>
|
||||
<button id="createNewChallengeButton" type="submit" class="button fullWidth green" href="" title="Create new challenge">Create new challenge</button>
|
||||
<div class="right">
|
||||
<button id="closeChallengeButton" class="gray marginTop" type="button">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section(main)
|
||||
|
||||
@@ -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']);
|
||||
@@ -74,13 +81,23 @@ if (isset($_COOKIE['COOKIES_CONSENT'])) {
|
||||
|
||||
session_set_save_handler(Container::$sessionHandler, true);
|
||||
session_start([
|
||||
'gc_maxlifetime' => 604800,
|
||||
'gc_probability' => 0, // old sessions are deleted by MaintainDatabaseCommand
|
||||
'cookie_lifetime' => 604800, // TODO: cookie is not renewed so session can be lost
|
||||
'cookie_lifetime' => 604800,
|
||||
'cookie_path' => '/',
|
||||
'cookie_httponly' => true,
|
||||
'cookie_samesite' => 'Lax'
|
||||
]);
|
||||
|
||||
if (isset($_COOKIE[session_name()])) {
|
||||
// extend session cookie lifetime is cookie already exists
|
||||
setcookie(session_name(), session_id(), [
|
||||
'expires' => time() + 604800,
|
||||
'path' => '/',
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax'
|
||||
]);
|
||||
}
|
||||
|
||||
// this is needed to handle old type of session IDs
|
||||
if (!Container::$sessionHandler->validateId(session_id())) {
|
||||
session_regenerate_id(true);
|
||||
|
||||
Reference in new issue
Block a user