Major changes

- Partially added removing ships on mobile (still doesn't work at all though)
- Added client-side logging
- Fixed a crash caused by the MySQL connector
- Multiple bug fixes
This commit is contained in:
MaciejkaG 2024-04-04 16:08:27 +02:00
parent c0ae701507
commit b1a822f39e
7 changed files with 79 additions and 14 deletions

View File

@ -510,7 +510,7 @@ io.on('connection', async (socket) => {
socket.on('place ship', async (type, posX, posY, rot) => { socket.on('place ship', async (type, posX, posY, rot) => {
const playerGame = await GInfo.getPlayerGameData(socket); const playerGame = await GInfo.getPlayerGameData(socket);
if (playerGame.data.state === 'preparation') { if (playerGame && playerGame.data.state === 'preparation') {
const playerShips = await GInfo.getPlayerShips(socket); const playerShips = await GInfo.getPlayerShips(socket);
let canPlace = bships.validateShipPosition(playerShips, type, posX, posY, rot); let canPlace = bships.validateShipPosition(playerShips, type, posX, posY, rot);
let shipAvailable = bships.getShipsAvailable(playerShips)[type] > 0; let shipAvailable = bships.getShipsAvailable(playerShips)[type] > 0;
@ -534,7 +534,7 @@ io.on('connection', async (socket) => {
socket.on('remove ship', async (posX, posY) => { socket.on('remove ship', async (posX, posY) => {
const playerGame = await GInfo.getPlayerGameData(socket); const playerGame = await GInfo.getPlayerGameData(socket);
if (playerGame.data.state === 'preparation') { if (playerGame && playerGame.data.state === 'preparation') {
const deletedShip = await GInfo.removeShip(socket, posX, posY); const deletedShip = await GInfo.removeShip(socket, posX, posY);
socket.emit("removed ship", { posX: posX, posY: posY, type: deletedShip.type }); socket.emit("removed ship", { posX: posX, posY: posY, type: deletedShip.type });
await GInfo.incrStat(socket, 'placedShips', -1); await GInfo.incrStat(socket, 'placedShips', -1);
@ -544,7 +544,7 @@ io.on('connection', async (socket) => {
socket.on('shoot', async (posX, posY) => { socket.on('shoot', async (posX, posY) => {
let playerGame = await GInfo.getPlayerGameData(socket); let playerGame = await GInfo.getPlayerGameData(socket);
if (playerGame.data.state === 'action') { if (playerGame && playerGame.data.state === 'action') {
if (bships.checkTurn(playerGame.data, socket.session.id)) { if (bships.checkTurn(playerGame.data, socket.session.id)) {
const enemyIdx = socket.session.id === playerGame.data.hostId ? 1 : 0; const enemyIdx = socket.session.id === playerGame.data.hostId ? 1 : 0;

View File

@ -111,6 +111,9 @@
"You cannot place a ship like this": "You cannot place a ship like this", "You cannot place a ship like this": "You cannot place a ship like this",
"You have ran out of ships of that type": "You have ran out of ships of that type", "You have ran out of ships of that type": "You have ran out of ships of that type",
"You have already shot at this field": "You have already shot at this field" "You have already shot at this field": "You have already shot at this field",
"Victory": "Victory",
"Defeat": "Defeat"
} }
} }

View File

@ -113,6 +113,9 @@
"You cannot place a ship like this": "Nie możesz tak postawić statku", "You cannot place a ship like this": "Nie możesz tak postawić statku",
"You have ran out of ships of that type": "Skończyły ci się statki tego typu", "You have ran out of ships of that type": "Skończyły ci się statki tego typu",
"You have already shot at this field": "Już strzelałeś w to pole" "You have already shot at this field": "Już strzelałeś w to pole",
"Victory": "Zwycięstwo",
"Defeat": "Porażka"
} }
} }

View File

@ -15,8 +15,17 @@ if ($(window).width() <= 820) {
animation: "shift-toward-subtle", animation: "shift-toward-subtle",
interactive: true, interactive: true,
content: (reference) => { content: (reference) => {
// Need to fix this
console.log("a");
let fieldData = `${$(reference).data('pos-x')}, ${$(reference).data('pos-y')}`; let fieldData = `${$(reference).data('pos-x')}, ${$(reference).data('pos-y')}`;
let pos = occupiedFields.find((elem) => elem.pos[0] == $(reference).data('pos-x') && elem.pos[1] == $(reference).data('pos-y'));
if (pos) {
return $('#removeTippyTemplate').html().replaceAll("[[FIELDPOS]]", fieldData);
}
return $('#mainTippyTemplate').html().replaceAll("[[FIELDPOS]]", fieldData); return $('#mainTippyTemplate').html().replaceAll("[[FIELDPOS]]", fieldData);
}, },
}); });
@ -39,6 +48,8 @@ $('#board .field').on('click', function () {
if (new Date().getTime() / 1000 - lastTimeClick > 0.3) { if (new Date().getTime() / 1000 - lastTimeClick > 0.3) {
if ($(window).width() > 820) { if ($(window).width() > 820) {
socket.emit("place ship", selectedShip, $(this).data('pos-x'), $(this).data('pos-y'), shipRotation); socket.emit("place ship", selectedShip, $(this).data('pos-x'), $(this).data('pos-y'), shipRotation);
navigator.vibrate(200);
lastTimeClick = new Date().getTime() / 1000; lastTimeClick = new Date().getTime() / 1000;
} }
} }
@ -48,12 +59,15 @@ function manualPlace(posX, posY) {
hoveredShip = null; hoveredShip = null;
refreshBoardView(); refreshBoardView();
socket.emit("place ship", selectedShip, posX, posY, shipRotation); socket.emit("place ship", selectedShip, posX, posY, shipRotation);
lastTimeClick = new Date().getTime() / 1000;
} }
$('#secondaryBoard .field').on('click', function () { $('#secondaryBoard .field').on('click', function () {
if (new Date().getTime() / 1000 - lastTimeClick > 0.3) { if (new Date().getTime() / 1000 - lastTimeClick > 0.3) {
if ($(window).width() > 820) { if ($(window).width() > 820) {
socket.emit("shoot", $(this).data('pos-x'), $(this).data('pos-y')); socket.emit("shoot", $(this).data('pos-x'), $(this).data('pos-y'));
navigator.vibrate(200);
lastTimeClick = new Date().getTime() / 1000; lastTimeClick = new Date().getTime() / 1000;
} }
} }
@ -61,17 +75,29 @@ $('#secondaryBoard .field').on('click', function () {
function manualShoot(posX, posY) { function manualShoot(posX, posY) {
socket.emit("shoot", posX, posY); socket.emit("shoot", posX, posY);
lastTimeClick = new Date().getTime() / 1000;
} }
$('.field').on('contextmenu', function () { $('.field').on('contextmenu', function () {
if ($(this).hasClass('active') && new Date().getTime() / 1000 - lastTimeClick > 0.3) { if ($(this).hasClass('active') && new Date().getTime() / 1000 - lastTimeClick > 0.3) {
let originPos = occupiedFields.find((elem) => elem.pos[0] == $(this).data('pos-x') && elem.pos[1] == $(this).data('pos-y')).origin; let pos = occupiedFields.find((elem) => elem.pos[0] == $(this).data('pos-x') && elem.pos[1] == $(this).data('pos-y'));
socket.emit("remove ship", originPos[0], originPos[1]); if (pos) {
socket.emit("remove ship", pos.origin[0], pos.origin[1]);
lastTimeClick = new Date().getTime() / 1000; lastTimeClick = new Date().getTime() / 1000;
} }
}
}); });
function manualRemove(posX, posY) {
let pos = occupiedFields.find((elem) => elem.pos[0] == posX && elem.pos[1] == posY);
if (pos) {
socket.emit("remove ship", pos.origin[0], pos.origin[1]);
lastTimeClick = new Date().getTime() / 1000;
}
}
socket.on('toast', (msg) => { socket.on('toast', (msg) => {
Toastify({ Toastify({
text: msg, text: msg,
@ -170,7 +196,7 @@ var updateTimer = setInterval(() => {
} else { } else {
const UTCNow = Math.floor((new Date()).getTime() / 1000); const UTCNow = Math.floor((new Date()).getTime() / 1000);
const time = Math.abs(UTCNow - timerDestination); const time = Math.max(timerDestination - UTCNow, 0);
if (time < 10) { if (time < 10) {
$("#timer").addClass("active"); $("#timer").addClass("active");
@ -190,10 +216,10 @@ socket.on("game finished", (winnerIdx, oppName) => {
$("#opponent").html(`Vs. <span class="important">${oppName}</span>`); $("#opponent").html(`Vs. <span class="important">${oppName}</span>`);
if (winnerIdx === playerIdx) { if (winnerIdx === playerIdx) {
$("#state").html("Zwycięstwo"); $("#state").html(locale["Victory"]);
$("#state").addClass("dynamic"); $("#state").addClass("dynamic");
} else { } else {
$("#state").html("Porażka"); $("#state").html(locale["Defeat"]);
$("#state").addClass("danger"); $("#state").addClass("danger");
} }
@ -228,7 +254,6 @@ socket.on('turn update', (turnData) => {
timerDestination = turnData.timerToUTC; timerDestination = turnData.timerToUTC;
gamePhase = turnData.phase; gamePhase = turnData.phase;
refreshBoardView();
}); });
socket.on('player left', () => { socket.on('player left', () => {

View File

@ -7,16 +7,22 @@ socket.on("joined", (nick) => {
$("#oppNameField").html(nick); $("#oppNameField").html(nick);
switchView("preparingGame"); switchView("preparingGame");
lockUI(false); lockUI(false);
console.log("Player joined the game:", nick);
}); });
socket.on("player left", () => { socket.on("player left", () => {
lockUI(true); lockUI(true);
switchView("mainMenuView"); switchView("mainMenuView");
lockUI(false); lockUI(false);
console.log("Player left the game");
}); });
socket.on("gameReady", (gameId) => { socket.on("gameReady", (gameId) => {
console.log("Game is ready, redirecting in 2 seconds. Game ID:", gameId);
setTimeout(() => { setTimeout(() => {
console.log("Redirecting...");
window.location.replace("/game?id=" + gameId); window.location.replace("/game?id=" + gameId);
}, 2000); }, 2000);
}); });
@ -58,19 +64,22 @@ socket.emit("my profile", (profile) => {
} }
$(".matchList").html(matchHistoryDOM); $(".matchList").html(matchHistoryDOM);
console.log("Profile data fetched successfully");
}); });
socket.emit("whats my nick", (myNickname) => { socket.emit("whats my nick", (myNickname) => {
nickname = myNickname; nickname = myNickname;
$("#profileButton").html(nickname); $("#profileButton").html(nickname);
console.log(nickname); console.log("Received player nickname:", myNickname);
}); });
$("#createGameButton").on("click", function () { $("#createGameButton").on("click", function () {
lockUI(true); lockUI(true);
console.log("Creating a lobby...");
socket.emit("create lobby", (response) => { socket.emit("create lobby", (response) => {
switch (response.status) { switch (response.status) {
case "ok": case "ok":
console.log("Lobby created");
$("#createGameCode").val(response.gameCode); $("#createGameCode").val(response.gameCode);
switchView("pvpCreateView"); switchView("pvpCreateView");
returnLock = true; returnLock = true;
@ -78,12 +87,14 @@ $("#createGameButton").on("click", function () {
break; break;
case "alreadyInLobby": case "alreadyInLobby":
console.log("Lobby creation failed (player is already in a lobby)");
$("#createGameCode").val(response.gameCode); $("#createGameCode").val(response.gameCode);
switchView("pvpCreateView"); switchView("pvpCreateView");
lockUI(false); lockUI(false);
break; break;
default: default:
console.log("Lobby creation failed (unknown)");
alert(`${window.locale["Unknown error occured"]}\n${window.locale["Status:"]} ${response.status}`); alert(`${window.locale["Unknown error occured"]}\n${window.locale["Status:"]} ${response.status}`);
lockUI(false); lockUI(false);
break; break;
@ -107,9 +118,11 @@ form.addEventListener('submit', (e) => {
e.preventDefault(); e.preventDefault();
if (input.value && input.value.length === 6) { if (input.value && input.value.length === 6) {
lockUI(true); lockUI(true);
console.log("Joining a lobby with code:", input.value);
socket.emit("join lobby", input.value, (response) => { socket.emit("join lobby", input.value, (response) => {
switch (response.status) { switch (response.status) {
case "ok": case "ok":
console.log("Joined a lobby by:", response.oppNickname);
$("#oppNameField").html(response.oppNickname); $("#oppNameField").html(response.oppNickname);
switchView("preparingGame"); switchView("preparingGame");
lockUI(false); lockUI(false);

View File

@ -84,6 +84,8 @@ export class MailAuth {
const row = response[0]; const row = response[0];
resolve({ status: 1, uid: row.user_id }); resolve({ status: 1, uid: row.user_id });
conn.end();
}); });
}); });
} }
@ -99,6 +101,8 @@ export class MailAuth {
} else { } else {
resolve(null); resolve(null);
} }
conn.end();
}); });
}); });
} }
@ -187,6 +191,8 @@ export class MailAuth {
resolve({ status: 1, uid: row.user_id }); resolve({ status: 1, uid: row.user_id });
conn.end();
}); });
}); });
} }
@ -199,6 +205,8 @@ export class MailAuth {
else conn.query(`INSERT INTO statistics(match_id, user_id, board, won) VALUES (${conn.escape(matchId)}, ${conn.escape(hostId)}, ${conn.escape(JSON.stringify(boards[0]))}, ${conn.escape(winnerIdx == 0 ? 1 : 0)}), (${conn.escape(matchId)}, ${conn.escape(guestId)}, ${conn.escape(JSON.stringify(boards[1]))}, ${conn.escape(winnerIdx == 1 ? 1 : 0)})`, async (error, response) => { else conn.query(`INSERT INTO statistics(match_id, user_id, board, won) VALUES (${conn.escape(matchId)}, ${conn.escape(hostId)}, ${conn.escape(JSON.stringify(boards[0]))}, ${conn.escape(winnerIdx == 0 ? 1 : 0)}), (${conn.escape(matchId)}, ${conn.escape(guestId)}, ${conn.escape(JSON.stringify(boards[1]))}, ${conn.escape(winnerIdx == 1 ? 1 : 0)})`, async (error, response) => {
if (error) reject(error); if (error) reject(error);
else resolve(); else resolve();
conn.end();
}); });
}); });
}); });
@ -207,7 +215,7 @@ export class MailAuth {
getProfile(userId) { getProfile(userId) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const conn = mysql.createConnection(this.mysqlOptions); const conn = mysql.createConnection(this.mysqlOptions);
conn.query(`SELECT nickname, account_creation FROM accounts WHERE user_id = ${conn.escape(userId)}; SELECT ROUND((AVG(statistics.won)) * 100) AS winrate, COUNT(statistics.match_id) AS alltime_matches, COUNT(CASE WHEN (YEAR(matches.date) = YEAR(NOW()) AND MONTH(matches.date) = MONTH(NOW())) THEN matches.match_id END) AS monthly_matches FROM accounts NATURAL JOIN statistics NATURAL JOIN matches WHERE accounts.user_id = ${conn.escape(userId)}; SELECT statistics.match_id, accounts.nickname AS opponent, matches.match_type, statistics.won, matches.duration, matches.date FROM statistics JOIN matches ON matches.match_id = statistics.match_id JOIN accounts ON accounts.user_id = (CASE WHEN matches.host_id != statistics.user_id THEN matches.host_id ELSE matches.guest_id END) WHERE statistics.user_id = ${conn.escape(userId)} LIMIT 10;`, async (error, response) => { conn.query(`SELECT nickname, account_creation FROM accounts WHERE user_id = ${conn.escape(userId)}; SELECT ROUND((AVG(statistics.won)) * 100) AS winrate, COUNT(statistics.match_id) AS alltime_matches, COUNT(CASE WHEN (YEAR(matches.date) = YEAR(NOW()) AND MONTH(matches.date) = MONTH(NOW())) THEN matches.match_id END) AS monthly_matches FROM accounts NATURAL JOIN statistics NATURAL JOIN matches WHERE accounts.user_id = ${conn.escape(userId)}; SELECT statistics.match_id, accounts.nickname AS opponent, matches.match_type, statistics.won, matches.duration, matches.date FROM statistics JOIN matches ON matches.match_id = statistics.match_id JOIN accounts ON accounts.user_id = (CASE WHEN matches.host_id != statistics.user_id THEN matches.host_id ELSE matches.guest_id END) WHERE statistics.user_id = ${conn.escape(userId)} ORDER BY matches.date DESC LIMIT 10;`, async (error, response) => {
if (error) reject(error); if (error) reject(error);
else { else {
if (response[0].length === 0 || response[1].length === 0) { if (response[0].length === 0 || response[1].length === 0) {
@ -219,6 +227,8 @@ export class MailAuth {
resolve({ profile, stats, matchHistory }); resolve({ profile, stats, matchHistory });
} }
conn.end();
}); });
}); });
} }
@ -241,6 +251,8 @@ export class MailAuth {
conn.query(`UPDATE accounts SET nickname = ${conn.escape(nickname)} WHERE user_id = ${conn.escape(uid)}`, (error) => { conn.query(`UPDATE accounts SET nickname = ${conn.escape(nickname)} WHERE user_id = ${conn.escape(uid)}`, (error) => {
if (error) reject(error); if (error) reject(error);
resolve(); resolve();
conn.end();
}); });
}); });
} }
@ -251,6 +263,8 @@ export class MailAuth {
conn.query(`SELECT nickname FROM accounts WHERE user_id = ${conn.escape(uid)}`, (error, response) => { conn.query(`SELECT nickname FROM accounts WHERE user_id = ${conn.escape(uid)}`, (error, response) => {
if (error) reject(error); if (error) reject(error);
resolve(response[0].nickname); resolve(response[0].nickname);
conn.end();
}); });
}); });
} }

View File

@ -46,6 +46,10 @@
<button class="tippyBtn" style="background-color: var(--important)" onclick="switchRotation()"><span class="material-symbols-outlined">refresh</span></button> <button class="tippyBtn" style="background-color: var(--important)" onclick="switchRotation()"><span class="material-symbols-outlined">refresh</span></button>
</div> </div>
<div id="removeTippyTemplate" class="tippyTemplate">
<button class="tippyBtn" style="background-color: var(--danger)" onclick="manualRemove([[FIELDPOS]])"><span class="material-symbols-outlined">delete</span></button>
</div>
<div id="secondaryTippyTemplate" class="tippyTemplate"> <div id="secondaryTippyTemplate" class="tippyTemplate">
<button class="tippyBtn" style="background-color: var(--danger)" onclick="manualShoot([[FIELDPOS]])"><span class="material-symbols-outlined">ads_click</span></button> <button class="tippyBtn" style="background-color: var(--danger)" onclick="manualShoot([[FIELDPOS]])"><span class="material-symbols-outlined">ads_click</span></button>
</div> </div>
@ -67,6 +71,9 @@
"Three-masted": "{{ t 'board.Three-masted' }}", "Three-masted": "{{ t 'board.Three-masted' }}",
"Four-masted": "{{ t 'board.Four-masted' }}", "Four-masted": "{{ t 'board.Four-masted' }}",
"Victory": "{{ t 'board.Victory' }}",
"Defeat": "{{ t 'board.Defeat' }}",
"Preparation phase": "{{ t 'board.Preparation phase' }}", "Preparation phase": "{{ t 'board.Preparation phase' }}",
"Your turn": "{{ t 'board.Your turn' }}", "Your turn": "{{ t 'board.Your turn' }}",
"Opponents turn": "{{ t 'board.Opponents turn' }}", "Opponents turn": "{{ t 'board.Opponents turn' }}",