Compare commits
41
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 |
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_SERVER_API_KEY=your_google_maps_server_api_key
|
||||||
GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key
|
GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key
|
||||||
LEAFLET_TILESERVER_URL=a_leaflet_compatible_tileserver_url
|
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
|
LEAFLET_TILESERVER_ATTRIBUTION=attribution_to_be_shown_for_tiles
|
||||||
STATIC_ROOT=/static
|
STATIC_ROOT=/static
|
||||||
MAIL_FROM=mapguesser@mapguesser-dev.ch
|
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_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**
|
* **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
|
### (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;
|
||||||
@@ -451,6 +451,7 @@ div.box {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.circleControl .controlItem {
|
.circleControl .controlItem {
|
||||||
|
position: relative;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
opacity: 70%;
|
opacity: 70%;
|
||||||
@@ -463,6 +464,8 @@ div.box {
|
|||||||
|
|
||||||
.circleControl .controlItem div {
|
.circleControl .controlItem div {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.circleControl .controlBackground {
|
.circleControl .controlBackground {
|
||||||
@@ -477,7 +480,6 @@ div.box {
|
|||||||
margin: auto;
|
margin: auto;
|
||||||
margin-top: 50%;
|
margin-top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
-ms-transform: translateY(-50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 599px) {
|
@media screen and (max-width: 599px) {
|
||||||
|
|||||||
@@ -75,6 +75,25 @@ div.mapItem>div.buttonContainer {
|
|||||||
grid-auto-flow: column;
|
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) {
|
@media screen and (min-width: 1504px) {
|
||||||
#mapContainer {
|
#mapContainer {
|
||||||
grid-template-columns: auto auto auto auto;
|
grid-template-columns: auto auto auto auto;
|
||||||
|
|||||||
+152
-6
@@ -1,10 +1,13 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const GameType = Object.freeze({'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2});
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
var Game = {
|
var Game = {
|
||||||
NUMBER_OF_ROUNDS: 5,
|
NUMBER_OF_ROUNDS: 5,
|
||||||
MAX_SCORE: 1000,
|
MAX_SCORE: 1000,
|
||||||
|
|
||||||
|
type: GameType.SINGLE,
|
||||||
mapBounds: null,
|
mapBounds: null,
|
||||||
multi: { token: null, owner: false },
|
multi: { token: null, owner: false },
|
||||||
rounds: [],
|
rounds: [],
|
||||||
@@ -16,6 +19,7 @@
|
|||||||
guessMarker: null,
|
guessMarker: null,
|
||||||
adaptGuess: false,
|
adaptGuess: false,
|
||||||
googleLink: null,
|
googleLink: null,
|
||||||
|
history: [],
|
||||||
|
|
||||||
readyToContinue: true,
|
readyToContinue: true,
|
||||||
timeoutEnd: null,
|
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 () {
|
prepare: function () {
|
||||||
var data = new FormData();
|
var data = new FormData();
|
||||||
var userNames;
|
var userNames;
|
||||||
@@ -226,7 +243,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
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 () {
|
MapGuesser.httpRequest('POST', url, function () {
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
@@ -269,7 +286,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
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('loading').style.visibility = 'hidden';
|
||||||
document.getElementById('panoCover').style.visibility = 'hidden';
|
document.getElementById('panoCover').style.visibility = 'hidden';
|
||||||
|
|
||||||
@@ -278,17 +295,56 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game.panoId = this.response.place.panoId;
|
Game.history = this.response.history;
|
||||||
Game.pov = this.response.place.pov;
|
|
||||||
|
|
||||||
for (var i = 0; i < this.response.history.length; ++i) {
|
for (var i = 0; i < this.response.history.length; ++i) {
|
||||||
var round = this.response.history[i];
|
var round = this.response.history[i];
|
||||||
Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] });
|
Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] });
|
||||||
Game.addPositionToResultMap(true);
|
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;
|
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('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);
|
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.');
|
MapGuesser.showModalWithContent('Error', 'This game is already started, you cannot join.');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'game_not_found':
|
||||||
|
MapGuesser.showModalWithContent('Error', 'The game room was not found by this ID. Please check the link.');
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
MapGuesser.showModalWithContent('Error', 'Error code: \'' + error + '\'');
|
MapGuesser.showModalWithContent('Error', 'Error code: \'' + error + '\'');
|
||||||
break
|
break
|
||||||
@@ -541,7 +601,7 @@
|
|||||||
data.append('lng', String(guessPosition.lng));
|
data.append('lng', String(guessPosition.lng));
|
||||||
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
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 () {
|
MapGuesser.httpRequest('POST', url, function () {
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
@@ -550,12 +610,57 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Game.history = this.response.history;
|
||||||
|
|
||||||
Game.receiveResult(this.response.position, guessPosition, this.response.result, this.response.allResults);
|
Game.receiveResult(this.response.position, guessPosition, this.response.result, this.response.allResults);
|
||||||
|
|
||||||
if (this.response.place) {
|
if (this.response.place) {
|
||||||
Game.panoId = this.response.place.panoId;
|
Game.panoId = this.response.place.panoId;
|
||||||
Game.pov = this.response.place.pov;
|
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);
|
}, data);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -669,6 +774,41 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
showSummary: function () {
|
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');
|
var distanceInfo = document.getElementById('distanceInfo');
|
||||||
distanceInfo.children[0].style.display = 'none';
|
distanceInfo.children[0].style.display = 'none';
|
||||||
distanceInfo.children[1].style.display = 'none';
|
distanceInfo.children[1].style.display = 'none';
|
||||||
@@ -872,6 +1012,12 @@
|
|||||||
document.getElementById("compass").style.transform = "translateY(-50%) rotate(" + heading + "deg)";
|
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) {
|
if (COOKIES_CONSENT) {
|
||||||
Game.prepare();
|
Game.prepare();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,16 +80,22 @@
|
|||||||
MapEditor.panorama.setPano(panoLocationData.pano);
|
MapEditor.panorama.setPano(panoLocationData.pano);
|
||||||
},
|
},
|
||||||
|
|
||||||
requestPanoData: function (location) {
|
requestPanoData: function (location, canBeIndoor) {
|
||||||
var sv = new google.maps.StreetViewService();
|
var sv = new google.maps.StreetViewService();
|
||||||
|
|
||||||
sv.getPanorama({
|
sv.getPanorama({
|
||||||
location: location,
|
location: location,
|
||||||
preference: google.maps.StreetViewPreference.NEAREST,
|
preference: google.maps.StreetViewPreference.NEAREST,
|
||||||
radius: MapEditor.map.getSearchRadius(location),
|
radius: MapEditor.map.getSearchRadius(location),
|
||||||
|
source: canBeIndoor ? google.maps.StreetViewSource.DEFAULT : google.maps.StreetViewSource.OUTDOOR
|
||||||
}, function (data, status) {
|
}, function (data, status) {
|
||||||
var panoLocationData = status === google.maps.StreetViewStatus.OK ? data.location : null;
|
var panoLocationData = status === google.maps.StreetViewStatus.OK ? data.location : null;
|
||||||
|
|
||||||
|
if (panoLocationData === null && !canBeIndoor) {
|
||||||
|
MapEditor.requestPanoData(location, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
MapEditor.loadPanoForNewPlace(panoLocationData);
|
MapEditor.loadPanoForNewPlace(panoLocationData);
|
||||||
@@ -382,7 +388,7 @@
|
|||||||
|
|
||||||
L.tileLayer(tileUrl, {
|
L.tileLayer(tileUrl, {
|
||||||
attribution: tileAttribution,
|
attribution: tileAttribution,
|
||||||
subdomains: '1234',
|
subdomains: tileSubdomains,
|
||||||
ppi: highResData.ppi,
|
ppi: highResData.ppi,
|
||||||
tileSize: highResData.tileSize,
|
tileSize: highResData.tileSize,
|
||||||
zoomOffset: highResData.zoomOffset,
|
zoomOffset: highResData.zoomOffset,
|
||||||
|
|||||||
@@ -85,10 +85,40 @@
|
|||||||
window.location.href = '/multiGame/' + this.elements.roomId.value;
|
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 () {
|
document.getElementById('multiButton').onclick = function () {
|
||||||
MapGuesser.showModal('multi');
|
MapGuesser.showModal('multi');
|
||||||
document.getElementById('createNewRoomButton').href = '/multiGame/new/' + this.dataset.mapId;
|
document.getElementById('createNewRoomButton').href = '/multiGame/new/' + this.dataset.mapId;
|
||||||
document.getElementById('multiForm').elements.roomId.select();
|
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 () {
|
document.getElementById('closePlayModeButton').onclick = function () {
|
||||||
@@ -99,6 +129,10 @@
|
|||||||
MapGuesser.hideModal();
|
MapGuesser.hideModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
document.getElementById('closeChallengeButton').onclick = function () {
|
||||||
|
MapGuesser.hideModal();
|
||||||
|
}
|
||||||
|
|
||||||
var buttons = document.getElementById('mapContainer').getElementsByClassName('playButton');
|
var buttons = document.getElementById('mapContainer').getElementsByClassName('playButton');
|
||||||
for (var i = 0; i < buttons.length; i++) {
|
for (var i = 0; i < buttons.length; i++) {
|
||||||
var button = buttons[i];
|
var button = buttons[i];
|
||||||
@@ -107,6 +141,13 @@
|
|||||||
MapGuesser.showModal('playMode');
|
MapGuesser.showModal('playMode');
|
||||||
document.getElementById('singleButton').href = '/game/' + this.dataset.mapId;
|
document.getElementById('singleButton').href = '/game/' + this.dataset.mapId;
|
||||||
document.getElementById('multiButton').dataset.mapId = 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->setActive(true);
|
||||||
$user->setCreatedDate(new DateTime());
|
$user->setCreatedDate(new DateTime());
|
||||||
|
|
||||||
if ($input->hasArgument('type')) {
|
if ($input->hasArgument('type') && $input->getArgument('type') !== null) {
|
||||||
$user->setType($input->getArgument('type'));
|
$user->setType($input->getArgument('type'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use MapGuesser\PersistentData\PersistentDataManager;
|
|||||||
use MapGuesser\Repository\MultiRoomRepository;
|
use MapGuesser\Repository\MultiRoomRepository;
|
||||||
use MapGuesser\Repository\UserConfirmationRepository;
|
use MapGuesser\Repository\UserConfirmationRepository;
|
||||||
use MapGuesser\Repository\UserPasswordResetterRepository;
|
use MapGuesser\Repository\UserPasswordResetterRepository;
|
||||||
|
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||||
use MapGuesser\Repository\UserRepository;
|
use MapGuesser\Repository\UserRepository;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
@@ -25,6 +26,8 @@ class MaintainDatabaseCommand extends Command
|
|||||||
|
|
||||||
private MultiRoomRepository $multiRoomRepository;
|
private MultiRoomRepository $multiRoomRepository;
|
||||||
|
|
||||||
|
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
@@ -34,6 +37,7 @@ class MaintainDatabaseCommand extends Command
|
|||||||
$this->userConfirmationRepository = new UserConfirmationRepository();
|
$this->userConfirmationRepository = new UserConfirmationRepository();
|
||||||
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
||||||
$this->multiRoomRepository = new MultiRoomRepository();
|
$this->multiRoomRepository = new MultiRoomRepository();
|
||||||
|
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configure(): void
|
public function configure(): void
|
||||||
@@ -81,6 +85,10 @@ class MaintainDatabaseCommand extends Command
|
|||||||
$this->pdm->deleteFromDb($userPasswordResetter);
|
$this->pdm->deleteFromDb($userPasswordResetter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($this->userPlayedPlaceRepository->getAllByUser($user) as $userPlayedPlace) {
|
||||||
|
$this->pdm->deleteFromDb($userPlayedPlace);
|
||||||
|
}
|
||||||
|
|
||||||
$this->pdm->deleteFromDb($user);
|
$this->pdm->deleteFromDb($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,22 @@ use MapGuesser\Response\JsonContent;
|
|||||||
use MapGuesser\Interfaces\Response\IContent;
|
use MapGuesser\Interfaces\Response\IContent;
|
||||||
use MapGuesser\Interfaces\Response\IRedirect;
|
use MapGuesser\Interfaces\Response\IRedirect;
|
||||||
use MapGuesser\Multi\MultiConnector;
|
use MapGuesser\Multi\MultiConnector;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
use MapGuesser\PersistentData\Model\MultiRoom;
|
use MapGuesser\PersistentData\Model\MultiRoom;
|
||||||
|
use MapGuesser\PersistentData\Model\PlaceInChallenge;
|
||||||
|
use MapGuesser\PersistentData\Model\UserInChallenge;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
use MapGuesser\Repository\ChallengeRepository;
|
||||||
use MapGuesser\Repository\MapRepository;
|
use MapGuesser\Repository\MapRepository;
|
||||||
use MapGuesser\Repository\MultiRoomRepository;
|
use MapGuesser\Repository\MultiRoomRepository;
|
||||||
|
use MapGuesser\Repository\PlaceRepository;
|
||||||
|
use MapGuesser\Repository\UserInChallengeRepository;
|
||||||
use MapGuesser\Response\Redirect;
|
use MapGuesser\Response\Redirect;
|
||||||
|
|
||||||
class GameController
|
class GameController
|
||||||
{
|
{
|
||||||
|
const NUMBER_OF_ROUNDS = 5;
|
||||||
|
|
||||||
private IRequest $request;
|
private IRequest $request;
|
||||||
|
|
||||||
private PersistentDataManager $pdm;
|
private PersistentDataManager $pdm;
|
||||||
@@ -26,6 +34,12 @@ class GameController
|
|||||||
|
|
||||||
private MapRepository $mapRepository;
|
private MapRepository $mapRepository;
|
||||||
|
|
||||||
|
private PlaceRepository $placeRepository;
|
||||||
|
|
||||||
|
private ChallengeRepository $challengeRepository;
|
||||||
|
|
||||||
|
private UserInChallengeRepository $userInChallengeRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
@@ -33,6 +47,9 @@ class GameController
|
|||||||
$this->multiConnector = new MultiConnector();
|
$this->multiConnector = new MultiConnector();
|
||||||
$this->multiRoomRepository = new MultiRoomRepository();
|
$this->multiRoomRepository = new MultiRoomRepository();
|
||||||
$this->mapRepository = new MapRepository();
|
$this->mapRepository = new MapRepository();
|
||||||
|
$this->placeRepository = new PlaceRepository();
|
||||||
|
$this->challengeRepository = new ChallengeRepository();
|
||||||
|
$this->userInChallengeRepository = new UserInChallengeRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getGame(): IContent
|
public function getGame(): IContent
|
||||||
@@ -79,6 +96,69 @@ class GameController
|
|||||||
return new HtmlContent('game', ['roomId' => $roomId]);
|
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
|
public function prepareGame(): IContent
|
||||||
{
|
{
|
||||||
$mapId = (int) $this->request->query('mapId');
|
$mapId = (int) $this->request->query('mapId');
|
||||||
@@ -114,6 +194,11 @@ class GameController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$room = $this->multiRoomRepository->getByRoomId($roomId);
|
$room = $this->multiRoomRepository->getByRoomId($roomId);
|
||||||
|
|
||||||
|
if(!isset($room)) {
|
||||||
|
return new JsonContent(['error' => 'game_not_found']);
|
||||||
|
}
|
||||||
|
|
||||||
$state = $room->getStateArray();
|
$state = $room->getStateArray();
|
||||||
$map = $this->mapRepository->getById($state['mapId']);
|
$map = $this->mapRepository->getById($state['mapId']);
|
||||||
$token = $this->getMultiToken($roomId);
|
$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
|
private function getMultiToken(string $roomId): string
|
||||||
{
|
{
|
||||||
$session = $this->request->session();
|
$session = $this->request->session();
|
||||||
|
|||||||
@@ -6,9 +6,19 @@ use MapGuesser\Util\Geo\Position;
|
|||||||
use MapGuesser\Response\JsonContent;
|
use MapGuesser\Response\JsonContent;
|
||||||
use MapGuesser\Interfaces\Response\IContent;
|
use MapGuesser\Interfaces\Response\IContent;
|
||||||
use MapGuesser\Multi\MultiConnector;
|
use MapGuesser\Multi\MultiConnector;
|
||||||
|
use MapGuesser\PersistentData\Model\UserInChallenge;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
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\MultiRoomRepository;
|
||||||
|
use MapGuesser\Repository\PlaceInChallengeRepository;
|
||||||
use MapGuesser\Repository\PlaceRepository;
|
use MapGuesser\Repository\PlaceRepository;
|
||||||
|
use MapGuesser\Repository\UserInChallengeRepository;
|
||||||
|
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||||
|
use MapGuesser\Repository\UserRepository;
|
||||||
|
|
||||||
class GameFlowController
|
class GameFlowController
|
||||||
{
|
{
|
||||||
@@ -25,6 +35,20 @@ class GameFlowController
|
|||||||
|
|
||||||
private PlaceRepository $placeRepository;
|
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)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
@@ -32,6 +56,13 @@ class GameFlowController
|
|||||||
$this->multiConnector = new MultiConnector();
|
$this->multiConnector = new MultiConnector();
|
||||||
$this->multiRoomRepository = new MultiRoomRepository();
|
$this->multiRoomRepository = new MultiRoomRepository();
|
||||||
$this->placeRepository = new PlaceRepository();
|
$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
|
public function initialData(): IContent
|
||||||
@@ -110,8 +141,74 @@ class GameFlowController
|
|||||||
return new JsonContent(['ok' => true]);
|
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
|
public function guess(): IContent
|
||||||
{
|
{
|
||||||
|
// testing
|
||||||
|
$testPlace = $this->placeRepository->getByIdWithMap(5);
|
||||||
|
|
||||||
$mapId = (int) $this->request->query('mapId');
|
$mapId = (int) $this->request->query('mapId');
|
||||||
$session = $this->request->session();
|
$session = $this->request->session();
|
||||||
|
|
||||||
@@ -121,7 +218,7 @@ class GameFlowController
|
|||||||
|
|
||||||
$last = $state['rounds'][$state['currentRound']];
|
$last = $state['rounds'][$state['currentRound']];
|
||||||
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
|
$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['guessPosition'] = $guessPosition;
|
||||||
$last['distance'] = $result['distance'];
|
$last['distance'] = $result['distance'];
|
||||||
@@ -145,9 +242,31 @@ class GameFlowController
|
|||||||
|
|
||||||
$session->set('state', $state);
|
$session->set('state', $state);
|
||||||
|
|
||||||
|
$this->saveVisit($last['placeId']);
|
||||||
|
|
||||||
return new JsonContent($response);
|
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
|
public function multiGuess(): IContent
|
||||||
{
|
{
|
||||||
$roomId = $this->request->query('roomId');
|
$roomId = $this->request->query('roomId');
|
||||||
@@ -162,7 +281,7 @@ class GameFlowController
|
|||||||
|
|
||||||
$last = $state['rounds'][$state['currentRound']];
|
$last = $state['rounds'][$state['currentRound']];
|
||||||
$guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng'));
|
$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', [
|
$responseFromMulti = $this->multiConnector->sendMessage('guess', [
|
||||||
'roomId' => $roomId,
|
'roomId' => $roomId,
|
||||||
@@ -185,6 +304,107 @@ class GameFlowController
|
|||||||
return new JsonContent($response);
|
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
|
public function multiNextRound(): IContent
|
||||||
{
|
{
|
||||||
$roomId = $this->request->query('roomId');
|
$roomId = $this->request->query('roomId');
|
||||||
@@ -214,7 +434,7 @@ class GameFlowController
|
|||||||
return new JsonContent(['ok' => true]);
|
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);
|
$distance = $this->calculateDistance($realPosition, $guessPosition);
|
||||||
$score = $this->calculateScore($distance, $area);
|
$score = $this->calculateScore($distance, $area);
|
||||||
@@ -224,7 +444,10 @@ class GameFlowController
|
|||||||
|
|
||||||
private function startNewGame(array &$state, int $mapId): void
|
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['rounds'] = [];
|
||||||
$state['currentRound'] = 0;
|
$state['currentRound'] = 0;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use MapGuesser\PersistentData\Model\UserPasswordResetter;
|
|||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
use MapGuesser\Repository\UserConfirmationRepository;
|
use MapGuesser\Repository\UserConfirmationRepository;
|
||||||
use MapGuesser\Repository\UserPasswordResetterRepository;
|
use MapGuesser\Repository\UserPasswordResetterRepository;
|
||||||
|
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||||
use MapGuesser\Repository\UserRepository;
|
use MapGuesser\Repository\UserRepository;
|
||||||
use MapGuesser\Response\HtmlContent;
|
use MapGuesser\Response\HtmlContent;
|
||||||
use MapGuesser\Response\JsonContent;
|
use MapGuesser\Response\JsonContent;
|
||||||
@@ -32,6 +33,8 @@ class LoginController
|
|||||||
|
|
||||||
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
||||||
|
|
||||||
|
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
@@ -39,6 +42,7 @@ class LoginController
|
|||||||
$this->userRepository = new UserRepository();
|
$this->userRepository = new UserRepository();
|
||||||
$this->userConfirmationRepository = new UserConfirmationRepository();
|
$this->userConfirmationRepository = new UserConfirmationRepository();
|
||||||
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
||||||
|
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLoginForm()
|
public function getLoginForm()
|
||||||
@@ -430,6 +434,10 @@ class LoginController
|
|||||||
|
|
||||||
$user = $this->userRepository->getById($confirmation->getUserId());
|
$user = $this->userRepository->getById($confirmation->getUserId());
|
||||||
|
|
||||||
|
foreach ($this->userPlayedPlaceRepository->getAllByUser($user) as $userPlayedPlace) {
|
||||||
|
$this->pdm->deleteFromDb($userPlayedPlace);
|
||||||
|
}
|
||||||
|
|
||||||
$this->pdm->deleteFromDb($user);
|
$this->pdm->deleteFromDb($user);
|
||||||
|
|
||||||
\Container::$dbConnection->commit();
|
\Container::$dbConnection->commit();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use MapGuesser\PersistentData\Model\Place;
|
|||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
use MapGuesser\Repository\MapRepository;
|
use MapGuesser\Repository\MapRepository;
|
||||||
use MapGuesser\Repository\PlaceRepository;
|
use MapGuesser\Repository\PlaceRepository;
|
||||||
|
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||||
use MapGuesser\Response\HtmlContent;
|
use MapGuesser\Response\HtmlContent;
|
||||||
use MapGuesser\Response\JsonContent;
|
use MapGuesser\Response\JsonContent;
|
||||||
use MapGuesser\Util\Geo\Bounds;
|
use MapGuesser\Util\Geo\Bounds;
|
||||||
@@ -27,12 +28,15 @@ class MapAdminController implements ISecured
|
|||||||
|
|
||||||
private PlaceRepository $placeRepository;
|
private PlaceRepository $placeRepository;
|
||||||
|
|
||||||
|
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
$this->pdm = new PersistentDataManager();
|
$this->pdm = new PersistentDataManager();
|
||||||
$this->mapRepository = new MapRepository();
|
$this->mapRepository = new MapRepository();
|
||||||
$this->placeRepository = new PlaceRepository();
|
$this->placeRepository = new PlaceRepository();
|
||||||
|
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
@@ -138,7 +142,7 @@ class MapAdminController implements ISecured
|
|||||||
|
|
||||||
$place = $this->placeRepository->getById((int) $placeRaw['id']);
|
$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]);
|
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
|
private function deletePlaces(Map $map): void
|
||||||
{
|
{
|
||||||
foreach ($this->placeRepository->getAllForMap($map) as $place) {
|
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\PersistentData\Model\User;
|
||||||
use MapGuesser\Repository\UserConfirmationRepository;
|
use MapGuesser\Repository\UserConfirmationRepository;
|
||||||
use MapGuesser\Repository\UserPasswordResetterRepository;
|
use MapGuesser\Repository\UserPasswordResetterRepository;
|
||||||
|
use MapGuesser\Repository\UserPlayedPlaceRepository;
|
||||||
use MapGuesser\Response\HtmlContent;
|
use MapGuesser\Response\HtmlContent;
|
||||||
use MapGuesser\Response\JsonContent;
|
use MapGuesser\Response\JsonContent;
|
||||||
use MapGuesser\Response\Redirect;
|
use MapGuesser\Response\Redirect;
|
||||||
@@ -26,12 +27,15 @@ class UserController implements ISecured
|
|||||||
|
|
||||||
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
||||||
|
|
||||||
|
private UserPlayedPlaceRepository $userPlayedPlaceRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
$this->pdm = new PersistentDataManager();
|
$this->pdm = new PersistentDataManager();
|
||||||
$this->userConfirmationRepository = new UserConfirmationRepository();
|
$this->userConfirmationRepository = new UserConfirmationRepository();
|
||||||
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
||||||
|
$this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
@@ -201,6 +205,10 @@ class UserController implements ISecured
|
|||||||
$this->pdm->deleteFromDb($userPasswordResetter);
|
$this->pdm->deleteFromDb($userPasswordResetter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($this->userPlayedPlaceRepository->getAllByUser($user) as $userPlayedPlace) {
|
||||||
|
$this->pdm->deleteFromDb($userPlayedPlace);
|
||||||
|
}
|
||||||
|
|
||||||
$this->pdm->deleteFromDb($user);
|
$this->pdm->deleteFromDb($user);
|
||||||
|
|
||||||
\Container::$dbConnection->commit();
|
\Container::$dbConnection->commit();
|
||||||
@@ -210,7 +218,7 @@ class UserController implements ISecured
|
|||||||
return new JsonContent(['success' => true]);
|
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()) {
|
if ($authenticatedWithGoogleUntil !== null && $authenticatedWithGoogleUntil > new DateTime()) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ class Select
|
|||||||
|
|
||||||
const CONDITION_HAVING = 1;
|
const CONDITION_HAVING = 1;
|
||||||
|
|
||||||
|
const DERIVED_TABLE_KEY = 'DERIVED';
|
||||||
|
|
||||||
private IConnection $connection;
|
private IConnection $connection;
|
||||||
|
|
||||||
private string $table;
|
private string $table;
|
||||||
@@ -55,6 +57,11 @@ class Select
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setDerivedTableAlias(string $tableAlias): Select
|
||||||
|
{
|
||||||
|
return $this->setTableAliases([Select::DERIVED_TABLE_KEY => $tableAlias]);
|
||||||
|
}
|
||||||
|
|
||||||
public function from(string $table): Select
|
public function from(string $table): Select
|
||||||
{
|
{
|
||||||
$this->table = $table;
|
$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
|
private function addJoin(string $type, $table, $column1, string $relation, $column2): void
|
||||||
{
|
{
|
||||||
$this->joins[] = [$type, $table, $column1, $relation, $column2];
|
$this->joins[] = [$type, $table, $column1, $relation, $column2];
|
||||||
@@ -211,10 +223,14 @@ class Select
|
|||||||
|
|
||||||
private function generateQuery(): array
|
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) {
|
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) {
|
if (count($this->conditions[self::CONDITION_WHERE]) > 0) {
|
||||||
@@ -245,20 +261,32 @@ class Select
|
|||||||
$queryString .= ' LIMIT ' . $this->limit[1] . ', ' . $this->limit[0];
|
$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) {
|
if ($table instanceof RawExpression) {
|
||||||
return (string) $table;
|
return [(string) $table, $params];
|
||||||
|
}
|
||||||
|
|
||||||
|
if($table instanceof Select)
|
||||||
|
{
|
||||||
|
return $table->generateQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->tableAliases[$table])) {
|
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
|
private function generateColumn($column): string
|
||||||
@@ -271,7 +299,8 @@ class Select
|
|||||||
$out = '';
|
$out = '';
|
||||||
|
|
||||||
if ($column[0]) {
|
if ($column[0]) {
|
||||||
$out .= $this->generateTable($column[0]) . '.';
|
list($tableName, $params) = $this->generateTable($column[0]);
|
||||||
|
$out .= $tableName . '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
$out .= Utils::backtick($column[1]);
|
$out .= Utils::backtick($column[1]);
|
||||||
@@ -297,15 +326,19 @@ class Select
|
|||||||
return implode(',', $columns);
|
return implode(',', $columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateJoins(): string
|
private function generateJoins(): array
|
||||||
{
|
{
|
||||||
$joins = $this->joins;
|
|
||||||
|
|
||||||
array_walk($joins, function (&$value, $key) {
|
$joinQueries = [];
|
||||||
$value = $value[0] . ' JOIN ' . $this->generateTable($value[1], true) . ' ON ' . $this->generateColumn($value[2]) . ' ' . $value[3] . ' ' . $this->generateColumn($value[4]);
|
$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
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
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();
|
$relations = $model::getRelations();
|
||||||
$relationData = [];
|
$relationData = [];
|
||||||
|
|
||||||
foreach ($data as $key => $value) {
|
while (key($data)) {
|
||||||
if ($this->extractRelationData($key, $value, $relationData, $relations)) {
|
$key = key($data);
|
||||||
continue;
|
$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)) {
|
if (method_exists($model, $method)) {
|
||||||
$model->$method($value);
|
$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();
|
$model->saveSnapshot();
|
||||||
}
|
}
|
||||||
@@ -133,24 +171,35 @@ class PersistentDataManager
|
|||||||
$table = call_user_func([$type, 'getTable']);
|
$table = call_user_func([$type, 'getTable']);
|
||||||
$fields = call_user_func([$type, 'getFields']);
|
$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);
|
$select->from($table);
|
||||||
|
|
||||||
//TODO: only with some relations?
|
//TODO: only with some relations?
|
||||||
if ($withRelations) {
|
if ($withRelations) {
|
||||||
$relations = call_user_func([$type, 'getRelations']);
|
$relations = call_user_func([$type, 'getRelations']);
|
||||||
|
|
||||||
$columns = [];
|
// $columns = [];
|
||||||
|
|
||||||
foreach ($fields as $field) {
|
// foreach ($fields as $field) {
|
||||||
$columns[] = [$table, $field];
|
// $columns[] = [$table, $field];
|
||||||
}
|
// }
|
||||||
|
|
||||||
$columns = array_merge($columns, $this->getRelationColumns($relations));
|
$columns = array_merge($columns, $this->getRelationColumns($relations));
|
||||||
|
|
||||||
$this->leftJoinRelations($select, $table, $relations);
|
$this->leftJoinRelations($select, $table, $relations);
|
||||||
$select->columns($columns);
|
$select->columns($columns);
|
||||||
} else {
|
} else {
|
||||||
$select->columns($fields);
|
// $select->columns($fields);
|
||||||
|
$select->columns($columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $select;
|
return $select;
|
||||||
@@ -165,6 +214,9 @@ class PersistentDataManager
|
|||||||
foreach (call_user_func([$relationType, 'getFields']) as $relationField) {
|
foreach (call_user_func([$relationType, 'getFields']) as $relationField) {
|
||||||
$columns[] = [$relationTable, $relationField, $relation . '__' . $relationField];
|
$columns[] = [$relationTable, $relationField, $relation . '__' . $relationField];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$nextOrderRelations = call_user_func([$relationType, 'getRelations']);
|
||||||
|
$columns = array_merge($columns, $this->getRelationColumns($nextOrderRelations));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $columns;
|
return $columns;
|
||||||
@@ -175,6 +227,9 @@ class PersistentDataManager
|
|||||||
foreach ($relations as $relation => $relationType) {
|
foreach ($relations as $relation => $relationType) {
|
||||||
$relationTable = call_user_func([$relationType, 'getTable']);
|
$relationTable = call_user_func([$relationType, 'getTable']);
|
||||||
$select->leftJoin($relationTable, [$relationTable, 'id'], '=', [$table, $relation . '_id']);
|
$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;
|
<?php namespace MapGuesser\Repository;
|
||||||
|
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
use MapGuesser\PersistentData\Model\Map;
|
use MapGuesser\PersistentData\Model\Map;
|
||||||
|
use MapGuesser\PersistentData\Model\Place;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
|
||||||
class MapRepository
|
class MapRepository
|
||||||
@@ -16,4 +19,20 @@ class MapRepository
|
|||||||
{
|
{
|
||||||
return $this->pdm->selectFromDbById($mapId, Map::class);
|
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 Generator;
|
||||||
use MapGuesser\Database\Query\Select;
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\PersistentData\Model\Challenge;
|
||||||
use MapGuesser\PersistentData\Model\Map;
|
use MapGuesser\PersistentData\Model\Map;
|
||||||
use MapGuesser\PersistentData\Model\Place;
|
use MapGuesser\PersistentData\Model\Place;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
@@ -28,13 +29,35 @@ class PlaceRepository
|
|||||||
yield from $this->pdm->selectMultipleFromDb($select, Place::class);
|
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
|
//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 = [];
|
$places = [];
|
||||||
|
|
||||||
|
$select = new Select(\Container::$dbConnection, 'places');
|
||||||
|
$select->where('map_id', '=', $mapId);
|
||||||
|
$numberOfPlaces = $select->count();
|
||||||
|
|
||||||
|
$exclude = [];
|
||||||
for ($i = 1; $i <= $n; ++$i) {
|
for ($i = 1; $i <= $n; ++$i) {
|
||||||
$place = $this->getRandomForMapWithValidPano($mapId, $exclude);
|
$place = $this->getRandomForMapWithValidPano($numberOfPlaces, $select, $exclude);
|
||||||
|
|
||||||
$places[] = $place;
|
$places[] = $place;
|
||||||
$exclude[] = $place->getId();
|
$exclude[] = $place->getId();
|
||||||
@@ -43,11 +66,15 @@ class PlaceRepository
|
|||||||
return $places;
|
return $places;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: use Map instead of id
|
private function getRandomForMapWithValidPano(int $numberOfPlaces, Select $select, array &$exclude, ?callable $pickRandomInt = null): ?Place
|
||||||
public function getRandomForMapWithValidPano(int $mapId, array $exclude = []): Place
|
|
||||||
{
|
{
|
||||||
do {
|
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();
|
$panoId = $place->getFreshPanoId();
|
||||||
if ($panoId === null) {
|
if ($panoId === null) {
|
||||||
@@ -58,25 +85,127 @@ class PlaceRepository
|
|||||||
return $place;
|
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
|
if ($numberOfPlacesLeft <= 0)
|
||||||
$select = new Select(\Container::$dbConnection, 'places');
|
return null;
|
||||||
$select->where('id', 'NOT IN', $exclude);
|
|
||||||
$select->where('map_id', '=', $mapId);
|
|
||||||
|
|
||||||
$numberOfPlaces = $select->count();
|
if (!isset($pickRandomInt)) {
|
||||||
|
$randomOffset = random_int(0, $numberOfPlacesLeft - 1);
|
||||||
//TODO: prevent this
|
} else {
|
||||||
if ($numberOfPlaces === 0) {
|
$randomOffset = $pickRandomInt($numberOfPlacesLeft);
|
||||||
throw new \Exception('There is no selectable place (count was 0).');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$randomOffset = random_int(0, $numberOfPlaces - 1);
|
$select->where('id', 'NOT IN', $exclude);
|
||||||
|
|
||||||
$select->orderBy('id');
|
|
||||||
$select->limit(1, $randomOffset);
|
$select->limit(1, $randomOffset);
|
||||||
|
|
||||||
return $this->pdm->selectFromDb($select, Place::class);
|
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 DateTime;
|
||||||
use Generator;
|
use Generator;
|
||||||
use MapGuesser\Database\Query\Select;
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\PersistentData\Model\Guess;
|
||||||
use MapGuesser\PersistentData\Model\User;
|
use MapGuesser\PersistentData\Model\User;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
|
||||||
@@ -44,4 +45,9 @@ class UserRepository
|
|||||||
|
|
||||||
yield from $this->pdm->selectMultipleFromDb($select, User::class);
|
yield from $this->pdm->selectMultipleFromDb($select, User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getByGuess(Guess $guess): ?User
|
||||||
|
{
|
||||||
|
return $this->getById($guess->getUserId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -94,6 +94,7 @@
|
|||||||
@section(pageScript)
|
@section(pageScript)
|
||||||
<script>
|
<script>
|
||||||
var tileUrl = '<?= $_ENV['LEAFLET_TILESERVER_URL'] ?>';
|
var tileUrl = '<?= $_ENV['LEAFLET_TILESERVER_URL'] ?>';
|
||||||
|
var tileSubdomains = '<?= $_ENV['LEAFLET_TILESERVER_SUBDOMAINS'] ?>';
|
||||||
var tileAttribution = '<?= $_ENV['LEAFLET_TILESERVER_ATTRIBUTION'] ?>';
|
var tileAttribution = '<?= $_ENV['LEAFLET_TILESERVER_ATTRIBUTION'] ?>';
|
||||||
var mapId = <?= $mapId ?>;
|
var mapId = <?= $mapId ?>;
|
||||||
var mapBounds = <?= json_encode($bounds) ?>;
|
var mapBounds = <?= json_encode($bounds) ?>;
|
||||||
|
|||||||
@@ -84,5 +84,6 @@
|
|||||||
var multiUrl = '<?= $_ENV['MULTI_WS_URL'] ?>';
|
var multiUrl = '<?= $_ENV['MULTI_WS_URL'] ?>';
|
||||||
var roomId = <?= isset($roomId) ? '\'' . $roomId . '\'' : 'null' ?>;
|
var roomId = <?= isset($roomId) ? '\'' . $roomId . '\'' : 'null' ?>;
|
||||||
var mapId = <?= isset($mapId) ? '\'' . $mapId . '\'' : 'null' ?>;
|
var mapId = <?= isset($mapId) ? '\'' . $mapId . '\'' : 'null' ?>;
|
||||||
|
var challengeToken = <?= isset($challengeToken) ? '\'' . $challengeToken . '\'' : 'null' ?>;
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -11,6 +11,8 @@ TODO: condition!
|
|||||||
<a id="singleButton" class="button fullWidth marginTop" href="" title="Single player">Single player</a>
|
<a id="singleButton" class="button fullWidth marginTop" href="" title="Single player">Single player</a>
|
||||||
<p class="bold center marginTop marginBottom">OR</p>
|
<p class="bold center marginTop marginBottom">OR</p>
|
||||||
<button id="multiButton" class="fullWidth green" data-map-id="">Multiplayer (beta)</button>
|
<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">
|
<div class="right">
|
||||||
<button id="closePlayModeButton" class="gray marginTop" type="button">Close</button>
|
<button id="closePlayModeButton" class="gray marginTop" type="button">Close</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -29,6 +31,47 @@ TODO: condition!
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</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
|
@endsection
|
||||||
|
|
||||||
@section(main)
|
@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.nextRound-json', '{roomId}/nextRound.json', [MapGuesser\Controller\GameFlowController::class, 'multiNextRound']);
|
||||||
$routeCollection->post('multiGame.guess-json', '{roomId}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'multiGuess']);
|
$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) {
|
Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
||||||
$routeCollection->get('admin.mapEditor', 'mapEditor/{mapId?}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']);
|
$routeCollection->get('admin.mapEditor', 'mapEditor/{mapId?}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']);
|
||||||
$routeCollection->get('admin.place', 'place.json/{placeId}', [MapGuesser\Controller\MapAdminController::class, 'getPlace']);
|
$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_set_save_handler(Container::$sessionHandler, true);
|
||||||
session_start([
|
session_start([
|
||||||
'gc_maxlifetime' => 604800,
|
|
||||||
'gc_probability' => 0, // old sessions are deleted by MaintainDatabaseCommand
|
'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_httponly' => true,
|
||||||
'cookie_samesite' => 'Lax'
|
'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
|
// this is needed to handle old type of session IDs
|
||||||
if (!Container::$sessionHandler->validateId(session_id())) {
|
if (!Container::$sessionHandler->validateId(session_id())) {
|
||||||
session_regenerate_id(true);
|
session_regenerate_id(true);
|
||||||
|
|||||||
Reference in new issue
Block a user