feature/MAPG-203-initial-multiplayer-implementation #8

Merged
bence merged 17 commits from feature/MAPG-203-initial-multiplayer-implementation into develop 2021-03-20 20:46:37 +01:00
4 changed files with 305 additions and 29 deletions
Showing only changes of commit 24a10c534e - Show all commits

View File

@ -102,6 +102,15 @@
display: none; display: none;
} }
#startMultiGameButton {
display: none;
}
#players > p {
font-size: 14px;
font-weight: bold;
}
@media screen and (max-width: 599px) { @media screen and (max-width: 599px) {
#mapName { #mapName {
display: none; display: none;

View File

@ -5,6 +5,8 @@
NUMBER_OF_ROUNDS: 5, NUMBER_OF_ROUNDS: 5,
MAX_SCORE: 1000, MAX_SCORE: 1000,
mapBounds: null,
multi: { token: null, owner: false },
rounds: [], rounds: [],
scoreSum: 0, scoreSum: 0,
panoId: null, panoId: null,
@ -15,8 +17,190 @@
adaptGuess: false, adaptGuess: false,
googleLink: null, googleLink: null,
initialize: function () { MultiConnector: {
connection: null,
reconnectCounter: 0,
connect: function () {
if (Game.MultiConnector.connection && Game.MultiConnector.connection.readyState !== WebSocket.CLOSED) {
return;
}
Game.MultiConnector.connection = new WebSocket((MapGuesser.isSecure ? 'wss' : 'ws') + '://' + multiUrl);
Game.MultiConnector.connection.onopen = function () {
document.getElementById('loading').style.visibility = 'hidden';
Game.MultiConnector.reconnectCounter = 0;
Game.MultiConnector.connection.send(JSON.stringify({ func: 'connect_to_room', args: { roomId: roomId, token: Game.multi.token } }));
};
Game.MultiConnector.connection.onclose = Game.MultiConnector.noConnection;
Game.MultiConnector.connection.onerror = function (event) {
console.error('WebSocket error in Game.MultiConnector:', event);
};
Game.MultiConnector.connection.onmessage = function (message) {
var json;
try {
json = JSON.parse(message.data);
} catch (e) {
console.error('Cannot parse message!');
console.error(message.data);
return;
}
switch (json.type) {
case 'initialize':
Game.MultiConnector.initialize(json.data);
break;
case 'member_joined':
Game.MultiConnector.memberJoined(json.data);
break;
case 'new_round':
Game.MultiConnector.newRound(json.data);
break;
case 'results':
//TODO
break;
case 'guess':
//TODO
break;
}
};
},
noConnection: function () {
if (Game.MultiConnector.reconnectCounter === 2) {
console.error('Could not reconnect WebSocket for Game.MultiConnector...')
}
setTimeout(function () {
Game.MultiConnector.reconnectCounter++;
console.log('Reconnecting WebSocket for Game.MultiConnector... ' + Game.MultiConnector.reconnectCounter);
Game.MultiConnector.connect();
}, 1000 + Math.min(Game.MultiConnector.reconnectCounter * 500, 9000));
},
initialize: function (data) {
if (data.history) {
for (var i = 0; i < data.history.length; ++i) {
var round = data.history[i];
Game.rounds.push({ position: round.position, guessPosition: round.guessPosition, realMarker: null, guessMarker: null, line: null });
Game.addRealGuessPair(round.position, round.guessPosition, true);
Game.scoreSum += round.score;
}
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);
}
if (data.place) {
Game.panoId = data.place.panoId;
Game.pov = data.place.pov;
document.getElementById('panoCover').style.visibility = 'hidden';
MapGuesser.hideModal();
Game.startNewRound();
}
document.getElementById('loading').style.visibility = 'hidden';
var div = document.getElementById('players');
for (var i = 0; i < data.members.length; ++i) {
var member = data.members[i];
var p = document.createElement('p');
p.innerHTML = member.userName + (member.me ? ' (me)' : '');
div.appendChild(p);
}
},
memberJoined: function (data) {
var div = document.getElementById('players');
var p = document.createElement('p');
p.innerHTML = data.userName;
div.appendChild(p);
},
newRound: function (data) {
//TODO: workaround until results are not sent
if (Game.adaptGuess) {
document.getElementById('guess').classList.remove('adapt');
}
if (Game.rounds.length === Game.NUMBER_OF_ROUNDS) {
Game.reset();
}
// if player didn't guess - TODO: show everything on a map
if (data.result && Game.rounds.length > 0 && !Game.rounds[Game.rounds.length - 1].position) {
Game.rounds[Game.rounds.length - 1].position = data.result.position;
Game.addRealGuessPair(data.result.position, null);
}
Game.panoId = data.place.panoId;
Game.pov = data.place.pov;
MapGuesser.hideModal();
Game.resetRound();
Game.startNewRound();
}
},
prepare: function () {
var data = new FormData();
var userNames;
if (roomId) {
var userNames = localStorage.userNames ? JSON.parse(localStorage.userNames) : {};
if (!userNames.hasOwnProperty(roomId)) {
userNames[roomId] = prompt('Your name: ');
localStorage.userNames = JSON.stringify(userNames);
}
data.append('userName', userNames[roomId]);
}
document.getElementById('loading').style.visibility = 'visible'; document.getElementById('loading').style.visibility = 'visible';
var url = roomId ? '/multiGame/' + roomId + '/prepare.json' : '/game/' + mapId + '/prepare.json';
MapGuesser.httpRequest('POST', url, function () {
document.getElementById('loading').style.visibility = 'hidden';
document.getElementById('mapName').innerHTML = this.response.mapName;
Game.mapBounds = this.response.bounds;
Game.initialize();
if (roomId) {
Game.multi.token = this.response.token;
Game.multi.owner = this.response.owner;
MapGuesser.showModal('multi');
if (Game.multi.owner) {
document.getElementById('startMultiGameButton').style.display = 'block';
}
document.getElementById('loading').style.visibility = 'visible';
Game.MultiConnector.connect();
}
}, data);
},
initialize: function () {
document.getElementById('panoCover').style.visibility = 'visible'; document.getElementById('panoCover').style.visibility = 'visible';
document.getElementById('currentRound').innerHTML = '1/' + String(Game.NUMBER_OF_ROUNDS); document.getElementById('currentRound').innerHTML = '1/' + String(Game.NUMBER_OF_ROUNDS);
document.getElementById('currentScoreSum').innerHTML = '0/0'; document.getElementById('currentScoreSum').innerHTML = '0/0';
@ -24,9 +208,16 @@
Game.map.setOptions({ Game.map.setOptions({
draggableCursor: 'crosshair' draggableCursor: 'crosshair'
}); });
Game.map.fitBounds(mapBounds); Game.map.fitBounds(Game.mapBounds);
MapGuesser.httpRequest('GET', '/game/' + mapId + '/initialData.json', function () { if (roomId) {
// if it is multiplayer mode, data is sent via WS
return;
}
document.getElementById('loading').style.visibility = 'visible';
MapGuesser.httpRequest('POST', '/game/' + mapId + '/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';
@ -63,8 +254,10 @@
for (var i = 0; i < Game.rounds.length; ++i) { for (var i = 0; i < Game.rounds.length; ++i) {
var round = Game.rounds[i]; var round = Game.rounds[i];
if (round.realMarker && round.guessMarker && round.line) { if (round.realMarker) {
round.realMarker.setMap(null); round.realMarker.setMap(null);
}
if (round.guessMarker) {
round.guessMarker.setMap(null); round.guessMarker.setMap(null);
round.line.setMap(null); round.line.setMap(null);
} }
@ -96,9 +289,11 @@
var lastRound = Game.rounds[Game.rounds.length - 1]; var lastRound = Game.rounds[Game.rounds.length - 1];
lastRound.realMarker.setVisible(false); lastRound.realMarker.setVisible(false);
if (lastRound.guessMarker) {
lastRound.guessMarker.setVisible(false); lastRound.guessMarker.setVisible(false);
lastRound.line.setVisible(false); lastRound.line.setVisible(false);
} }
}
document.getElementById('panoCover').style.visibility = 'hidden'; document.getElementById('panoCover').style.visibility = 'hidden';
document.getElementById('showGuessButton').style.visibility = null; document.getElementById('showGuessButton').style.visibility = null;
@ -108,7 +303,12 @@
Game.map.setOptions({ Game.map.setOptions({
draggableCursor: 'crosshair' draggableCursor: 'crosshair'
}); });
Game.map.fitBounds(mapBounds); Game.map.fitBounds(Game.mapBounds);
if (roomId) {
// if it is multiplayer mode, data is sent via WS
return;
}
Game.startNewRound(); Game.startNewRound();
}, },
@ -124,8 +324,14 @@
handleErrorResponse: function (error) { handleErrorResponse: function (error) {
// for the time being we only handle the "no_session_found" error and reset the game // for the time being we only handle the "no_session_found" error and reset the game
MapGuesser.httpRequest('GET', '/game/' + mapId + '/json', function () { if (roomId) {
mapBounds = this.response.bounds; //TODO: better error message
alert('Your session is invalid, please start multiplayer again!')
return;
}
MapGuesser.httpRequest('GET', '/game/' + mapId + '/prepare.json', function () {
Game.mapBounds = this.response.bounds;
Game.reset(); Game.reset();
}); });
@ -160,7 +366,8 @@
data.append('lat', String(guessPosition.lat)); data.append('lat', String(guessPosition.lat));
data.append('lng', String(guessPosition.lng)); data.append('lng', String(guessPosition.lng));
MapGuesser.httpRequest('POST', '/game/' + mapId + '/guess.json', function () { var url = roomId ? '/multiGame/' + roomId + '/guess.json' : '/game/' + mapId + '/guess.json';
MapGuesser.httpRequest('POST', url, function () {
if (this.response.error) { if (this.response.error) {
Game.handleErrorResponse(this.response.error); Game.handleErrorResponse(this.response.error);
return; return;
@ -204,6 +411,10 @@
Game.panoId = this.response.place.panoId; Game.panoId = this.response.place.panoId;
Game.pov = this.response.place.pov; Game.pov = this.response.place.pov;
} else { } else {
if (!Game.multi.owner) {
//TODO: "waiting for" disabled button
document.getElementById('continueButton').style.display = 'none';
}
Game.panoId = null; Game.panoId = null;
Game.pov = null; Game.pov = null;
} }
@ -233,6 +444,10 @@
window.open('https://www.google.com/maps/search/?api=1&query=' + this.getPosition().toUrlValue(), '_blank'); window.open('https://www.google.com/maps/search/?api=1&query=' + this.getPosition().toUrlValue(), '_blank');
}); });
if (!guessPosition) {
return;
}
round.guessMarker = new google.maps.Marker({ round.guessMarker = new google.maps.Marker({
map: Game.map, map: Game.map,
visible: !hidden, visible: !hidden,
@ -304,7 +519,10 @@
scoreInfo.children[0].style.display = 'none'; scoreInfo.children[0].style.display = 'none';
scoreInfo.children[1].style.display = 'block'; scoreInfo.children[1].style.display = 'block';
document.getElementById('showSummaryButton').style.display = null; document.getElementById('showSummaryButton').style.display = null;
if (Game.multi.owner) {
document.getElementById('startNewGameButton').style.display = 'block'; document.getElementById('startNewGameButton').style.display = 'block';
}
var resultBounds = new google.maps.LatLngBounds(); var resultBounds = new google.maps.LatLngBounds();
@ -325,13 +543,18 @@
fontWeight: '500', fontWeight: '500',
text: String(i + 1) text: String(i + 1)
}); });
round.realMarker.setVisible(true); round.realMarker.setVisible(true);
if (round.guessMarker) {
round.guessMarker.setVisible(true); round.guessMarker.setVisible(true);
round.line.setVisible(true); round.line.setVisible(true);
}
resultBounds.extend(round.position); resultBounds.extend(round.position);
if (round.guessMarker) {
resultBounds.extend(round.guessPosition); resultBounds.extend(round.guessPosition);
} }
}
Game.map.fitBounds(resultBounds); Game.map.fitBounds(resultBounds);
@ -378,13 +601,7 @@
} }
}; };
MapGuesser.sessionAvailableHooks.reinitializeGame = function () { MapGuesser.sessionAvailableHooks.reinitializeGame = Game.prepare;
MapGuesser.httpRequest('GET', '/game/' + mapId + '/json', function () {
mapBounds = this.response.bounds;
Game.initialize();
});
};
if (!('ontouchstart' in document.documentElement)) { if (!('ontouchstart' in document.documentElement)) {
Game.adaptGuess = true; Game.adaptGuess = true;
@ -445,7 +662,9 @@
Game.rewriteGoogleLink(); Game.rewriteGoogleLink();
}); });
Game.initialize(); if (COOKIES_CONSENT) {
Game.prepare();
}
document.getElementById('showGuessButton').onclick = function () { document.getElementById('showGuessButton').onclick = function () {
this.style.visibility = 'hidden'; this.style.visibility = 'hidden';
@ -462,14 +681,51 @@
} }
document.getElementById('continueButton').onclick = function () { document.getElementById('continueButton').onclick = function () {
if (roomId) {
if (!Game.multi.owner) {
return;
}
document.getElementById('loading').style.visibility = 'visible';
MapGuesser.httpRequest('POST', '/multiGame/' + roomId + '/nextRound.json', function () {
document.getElementById('loading').style.visibility = 'hidden';
});
} else {
Game.resetRound(); Game.resetRound();
} }
}
document.getElementById('showSummaryButton').onclick = function () { document.getElementById('showSummaryButton').onclick = function () {
Game.showSummary(); Game.showSummary();
} }
document.getElementById('startNewGameButton').onclick = function () { document.getElementById('startNewGameButton').onclick = function () {
if (roomId) {
if (!Game.multi.owner) {
return;
}
document.getElementById('loading').style.visibility = 'visible';
MapGuesser.httpRequest('POST', '/multiGame/' + roomId + '/initialData.json', function () {
document.getElementById('loading').style.visibility = 'hidden';
});
} else {
Game.reset(); Game.reset();
} }
}
document.getElementById('startMultiGameButton').onclick = function () {
if (!roomId || !Game.multi.owner) {
return;
}
MapGuesser.hideModal();
document.getElementById('loading').style.visibility = 'visible';
MapGuesser.httpRequest('POST', '/multiGame/' + roomId + '/initialData.json', function () {
document.getElementById('loading').style.visibility = 'hidden';
});
}
})(); })();

View File

@ -1,4 +1,5 @@
var MapGuesser = { var MapGuesser = {
isSecure: window.location.protocol === 'https:',
cookiesAgreed: false, cookiesAgreed: false,
sessionAvailableHooks: {}, sessionAvailableHooks: {},

View File

@ -5,8 +5,17 @@
@extends(templates/layout_full) @extends(templates/layout_full)
@section(pagemodal)
<div id="multi" class="modal">
<h2>Multiplayer (beta)</h2>
<p class="marginTop">Waiting for players...</p>
<div id="players" class="marginTop"></div>
<button id="startMultiGameButton" class="button fullWidth marginTop green">Start game</button>
</div>
@endsection
@section(subheader) @section(subheader)
<span id="mapName" class="bold"><?= $mapName ?></span><!-- <span id="mapName" class="bold"></span><!--
--><span>Round <span id="currentRound" class="bold"></span></span><!-- --><span>Round <span id="currentRound" class="bold"></span></span><!--
--><span>Score <span id="currentScoreSum" class="bold"></span></span> --><span>Score <span id="currentScoreSum" class="bold"></span></span>
@endsection @endsection
@ -50,7 +59,8 @@
@section(pageScript) @section(pageScript)
<script> <script>
var mapId = <?= $mapId ?>; var multiUrl = '<?= $_ENV['MULTI_WS_HOST'] . ':' . $_ENV['MULTI_WS_PORT'] ?>';
var mapBounds = <?= json_encode($bounds) ?>; var roomId = <?= isset($roomId) ? '\'' . $roomId . '\'' : 'null' ?>;
var mapId = <?= isset($mapId) ? '\'' . $mapId . '\'' : 'null' ?>;
</script> </script>
@endsection @endsection