added game handler

This commit is contained in:
Wenszel 2022-05-30 10:59:33 +02:00
parent 5eea80b804
commit 7a72626f87
8 changed files with 286 additions and 211 deletions

View File

@ -23,12 +23,9 @@ node server.js
- Express-session - Express-session
- MongoDB, Mongoose - MongoDB, Mongoose
- MongoDB sessions store - MongoDB sessions store
- SocketIO
### Frontend ### Frontend
- React - React
- Axios - Axios
- Material UI - Material UI
- Canvas - Canvas
## ToDo
- Redis
- SocketIO
- Add more game logic

View File

@ -0,0 +1,56 @@
const RoomModel = require('../schemas/room');
const { getPositionAfterMove } = require('../utils/functions');
/*
Function handle all requests
file constains functions:
1. roll
2. move
*/
module.exports = (io, socket) => {
const req = socket.request;
const roll = () => {
const rolledNumber = Math.ceil(Math.random() * 6);
req.session.reload(err => {
if (err) return socket.disconnect();
// Saving session data
req.session.rolledNumber = rolledNumber;
req.session.save();
socket.emit('game:roll', rolledNumber);
});
};
const move = ({ pawnId }) => {
RoomModel.findOne({ _id: req.session.roomId }, function (err, room) {
if (!room) return err;
const pawnIndex = room.pawns.findIndex(pawn => pawn._id == pawnId);
room.pawns[pawnIndex].position = getPositionAfterMove(req.session.rolledNumber, room.pawns[pawnIndex]);
const pawnsOnPos = room.pawns.filter(pawn => pawn.position == room.pawns[pawnIndex].position);
pawnsOnPos.forEach(pawn => {
if (pawn.color !== req.session.color) {
const index = room.pawns.findIndex(i => i._id === pawn._id);
room.pawns[index].position = room.pawns[index].basePos;
}
});
// Updating moving player
const playerIndex = room.players.findIndex(player => player.nowMoving === true);
const roomSize = room.players.length;
room.players[playerIndex].nowMoving = false;
if (playerIndex + 1 === roomSize) {
room.players[0].nowMoving = true;
} else {
room.players[playerIndex + 1].nowMoving = true;
}
// Updating timer
room.nextMoveTime = Date.now() + 15000;
// Pushing above data to database
RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room, (err, updatedRoom) => {
if (!updatedRoom) return err;
io.to(req.session.roomId).emit('room:data', JSON.stringify(updatedRoom));
socket.emit('room:move');
});
});
};
socket.on('game:roll', roll);
socket.on('game:move', move);
};

View File

@ -42,64 +42,6 @@ router.post('/move', function (req, res){
}); });
}); });
function getPosition(rolledNumber, pawn){
const { position, color } = pawn;
switch (color){
case 'red':
if(pawn.position + rolledNumber <= 73){
if(position >= 0 && position <= 3) {
return 16;
}else if(position <= 66 && position + rolledNumber >= 67){
return position + rolledNumber + 1;
}else{
return position + rolledNumber;
}
}else{
return position;
}
case 'blue':
if(pawn.position + rolledNumber <= 79){
if(position >= 4 && position <= 7){
return 55;
}else if(position <= 67 && position + rolledNumber > 67){
return position + rolledNumber - 52;
}else if(position <= 53 && position + rolledNumber >= 54){
return position + rolledNumber + 20;
}else{
return position + rolledNumber;
}
}else{
return position;
}
case 'green':
if(pawn.position + rolledNumber <= 85){
if(position >= 8 && position <= 11){
return 42;
}else if(position <= 67 && position + rolledNumber > 67){
return position + rolledNumber - 52;
}else if(position <= 40 && position + rolledNumber >= 41){
return position + rolledNumber + 39;
}else{
return position + rolledNumber;
}
}else{
return position;
}
case 'yellow':
if(pawn.position + rolledNumber <= 85){
if(position >= 12 && position <= 15){
return 29;
}else if(position <= 67 && position + rolledNumber > 67){
return position + rolledNumber - 52;
}else if(position <= 27 && position + rolledNumber >= 28){
return position + rolledNumber + 58;
}else{
return position + rolledNumber;
}
}else{
return position;
}
}
};
module.exports = router; module.exports = router;

View File

@ -4,6 +4,7 @@ const cookieParser = require('cookie-parser');
const { sessionMiddleware, wrap } = require('./controllers/serverController'); const { sessionMiddleware, wrap } = require('./controllers/serverController');
const registerPlayerHandlers = require('./handlers/playerHandler'); const registerPlayerHandlers = require('./handlers/playerHandler');
const registerRoomHandlers = require('./handlers/roomHandler'); const registerRoomHandlers = require('./handlers/roomHandler');
const registerGameHandlers = require('./handlers/gameHandler');
const PORT = 8080; const PORT = 8080;
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const CONNECTION_URI = require('./credentials.js'); const CONNECTION_URI = require('./credentials.js');
@ -75,6 +76,7 @@ io.use(wrap(sessionMiddleware));
io.on('connection', socket => { io.on('connection', socket => {
registerPlayerHandlers(io, socket); registerPlayerHandlers(io, socket);
registerRoomHandlers(io, socket); registerRoomHandlers(io, socket);
registerGameHandlers(io, socket);
if (socket.request.session.roomId) { if (socket.request.session.roomId) {
socket.join(socket.request.session.roomId); socket.join(socket.request.session.roomId);
socket.emit('player:data', JSON.stringify(socket.request.session)); socket.emit('player:data', JSON.stringify(socket.request.session));
@ -82,13 +84,6 @@ io.on('connection', socket => {
} }
}); });
//ROUTES CONFIG
const playerRoutes = require('./routes/player');
const gameRoutes = require('./routes/game');
app.use('/player', playerRoutes);
app.use('/game', gameRoutes);
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
app.use(express.static('/app/build')); app.use(express.static('/app/build'));
app.get('/', (req, res) => { app.get('/', (req, res) => {

View File

@ -1,16 +1,75 @@
const { colors } = require("./constants"); const { colors } = require('./constants');
function getStartPositions() { function getStartPositions() {
const startPositions = []; const startPositions = [];
for (let i = 0; i < 16; i++) { for (let i = 0; i < 16; i++) {
let pawn = {}; let pawn = {};
pawn.basePos = i; pawn.basePos = i;
pawn.position = i; pawn.position = i;
if (i < 4) pawn.color = colors[0]; if (i < 4) pawn.color = colors[0];
else if (i < 8) pawn.color = colors[1]; else if (i < 8) pawn.color = colors[1];
else if (i < 12) pawn.color = colors[2]; else if (i < 12) pawn.color = colors[2];
else if (i < 16) pawn.color = colors[3]; else if (i < 16) pawn.color = colors[3];
startPositions.push(pawn); startPositions.push(pawn);
} }
return startPositions; return startPositions;
} }
module.exports = { getStartPositions }; function getPositionAfterMove(rolledNumber, pawn) {
const { position, color } = pawn;
switch (color) {
case 'red':
if (pawn.position + rolledNumber <= 73) {
if (position >= 0 && position <= 3) {
return 16;
} else if (position <= 66 && position + rolledNumber >= 67) {
return position + rolledNumber + 1;
} else {
return position + rolledNumber;
}
} else {
return position;
}
case 'blue':
if (pawn.position + rolledNumber <= 79) {
if (position >= 4 && position <= 7) {
return 55;
} else if (position <= 67 && position + rolledNumber > 67) {
return position + rolledNumber - 52;
} else if (position <= 53 && position + rolledNumber >= 54) {
return position + rolledNumber + 20;
} else {
return position + rolledNumber;
}
} else {
return position;
}
case 'green':
if (pawn.position + rolledNumber <= 85) {
if (position >= 8 && position <= 11) {
return 42;
} else if (position <= 67 && position + rolledNumber > 67) {
return position + rolledNumber - 52;
} else if (position <= 40 && position + rolledNumber >= 41) {
return position + rolledNumber + 39;
} else {
return position + rolledNumber;
}
} else {
return position;
}
case 'yellow':
if (pawn.position + rolledNumber <= 85) {
if (position >= 12 && position <= 15) {
return 29;
} else if (position <= 67 && position + rolledNumber > 67) {
return position + rolledNumber - 52;
} else if (position <= 27 && position + rolledNumber >= 28) {
return position + rolledNumber + 58;
} else {
return position + rolledNumber;
}
} else {
return position;
}
}
}
module.exports = { getStartPositions, getPositionAfterMove };

View File

@ -1,6 +1,5 @@
import React, { useState, useEffect, useContext, useCallback } from 'react'; import React, { useState, useEffect, useContext, useCallback } from 'react';
import { PlayerDataContext, SocketContext } from '../App'; import { PlayerDataContext, SocketContext } from '../App';
import axios from 'axios';
import Map from './game-board-components/Map'; import Map from './game-board-components/Map';
import Dice from './game-board-components/Dice'; import Dice from './game-board-components/Dice';
import Navbar from './Navbar'; import Navbar from './Navbar';

View File

@ -1,5 +1,5 @@
import React, { useState } from 'react'; import React, { useState, useEffect, useContext } from 'react';
import axios from 'axios'; import { SocketContext } from '../../App';
import one from '../../images/dice/1.png'; import one from '../../images/dice/1.png';
import two from '../../images/dice/2.png'; import two from '../../images/dice/2.png';
import three from '../../images/dice/3.png'; import three from '../../images/dice/3.png';
@ -8,20 +8,28 @@ import five from '../../images/dice/5.png';
import six from '../../images/dice/6.png'; import six from '../../images/dice/6.png';
const Dice = ({ rolledNumberCallback, nowMoving }) => { const Dice = ({ rolledNumberCallback, nowMoving }) => {
const socket = useContext(SocketContext);
const [rolledNumber, setRolledNumber] = useState(); const [rolledNumber, setRolledNumber] = useState();
const [images] = useState([one, two, three, four, five, six]); const [images] = useState([one, two, three, four, five, six]);
const handleRoll = () => { const handleRoll = () => {
axios.get('/game/roll', {withCredentials: true}).then(response => { socket.emit('game:roll');
const utterance = new SpeechSynthesisUtterance(response.data.number); };
useEffect(() => {
socket.on('game:roll', number => {
const utterance = new SpeechSynthesisUtterance(number);
speechSynthesis.speak(utterance); speechSynthesis.speak(utterance);
setRolledNumber(response.data.number); setRolledNumber(number);
rolledNumberCallback(response.data.number); rolledNumberCallback(number);
}) });
} }, []);
return( return (
<div className="dice-container"> <div className='dice-container'>
{rolledNumber ? <img src={images[rolledNumber - 1]} alt={rolledNumber} width="100" height="100"/> : nowMoving ? <button onClick={handleRoll}> Roll </button> : null} {rolledNumber ? (
<img src={images[rolledNumber - 1]} alt={rolledNumber} width='100' height='100' />
) : nowMoving ? (
<button onClick={handleRoll}> Roll </button>
) : null}
</div> </div>
) );
} };
export default Dice; export default Dice;

View File

@ -1,13 +1,13 @@
import React, { useEffect, useRef, useState, useContext, useCallback } from 'react'; import React, { useEffect, useRef, useState, useContext, useCallback } from 'react';
import { PlayerDataContext } from '../../App'; import { PlayerDataContext, SocketContext } from '../../App';
import axios from 'axios';
import positions from './positions'; import positions from './positions';
const Map = ({ pawns, nowMoving, rolledNumber }) => { const Map = ({ pawns, nowMoving, rolledNumber }) => {
const context = useContext(PlayerDataContext); const context = useContext(PlayerDataContext);
const socket = useContext(SocketContext);
const [hintPawn, setHintPawn] = useState(); const [hintPawn, setHintPawn] = useState();
const [blinking, setBlinking] = useState(false); const [blinking, setBlinking] = useState(false);
const paintPawn = (context, x, y, color) =>{ const paintPawn = (context, x, y, color) => {
const circle = new Path2D(); const circle = new Path2D();
circle.arc(x, y, 12, 0, 2 * Math.PI); circle.arc(x, y, 12, 0, 2 * Math.PI);
context.strokeStyle = 'black'; context.strokeStyle = 'black';
@ -15,112 +15,111 @@ const Map = ({ pawns, nowMoving, rolledNumber }) => {
context.fillStyle = color; context.fillStyle = color;
context.fill(circle); context.fill(circle);
return circle; return circle;
} };
const canvasRef = useRef(null); const canvasRef = useRef(null);
// Return true when pawn can move // Return true when pawn can move
const checkIfPawnCanMove = useCallback(pawn => { const checkIfPawnCanMove = useCallback(
// If is in base pawn => {
if((rolledNumber === 1 || rolledNumber === 6) && pawn.position === pawn.basePos){ // If is in base
return true; if ((rolledNumber === 1 || rolledNumber === 6) && pawn.position === pawn.basePos) {
// Other situations: pawn is on map or pawn is in end positions return true;
}else if(pawn.position !== pawn.basePos){ // Other situations: pawn is on map or pawn is in end positions
switch (pawn.color){ } else if (pawn.position !== pawn.basePos) {
case 'red': switch (pawn.color) {
if(pawn.position + rolledNumber <= 73) return true; case 'red':
break; if (pawn.position + rolledNumber <= 73) return true;
case 'blue': break;
if(pawn.position + rolledNumber <= 79) return true; case 'blue':
break; if (pawn.position + rolledNumber <= 79) return true;
case 'green': break;
if(pawn.position + rolledNumber <= 85) return true; case 'green':
break; if (pawn.position + rolledNumber <= 85) return true;
case 'yellow': break;
if(pawn.position + rolledNumber <= 91) return true; case 'yellow':
break; if (pawn.position + rolledNumber <= 91) return true;
default: break;
return false; default:
return false;
}
} else {
return false;
} }
}else{ },
return false; [rolledNumber]
} );
},[rolledNumber]);
const handleCanvasClick = event => { const handleCanvasClick = event => {
// If hint pawn exist it means that pawn can move // If hint pawn exist it means that pawn can move
if(hintPawn){ if (hintPawn) {
const canvas = canvasRef.current const canvas = canvasRef.current;
const ctx = canvas.getContext('2d') const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect(), const rect = canvas.getBoundingClientRect(),
x = event.clientX - rect.left, x = event.clientX - rect.left,
y = event.clientY - rect.top; y = event.clientY - rect.top;
for(const pawn of pawns){ for (const pawn of pawns) {
if (ctx.isPointInPath(pawn.circle, x, y)) { if (ctx.isPointInPath(pawn.circle, x, y)) {
axios.post('/game/move', {pawnId: pawn._id}, {withCredentials: true, mode: 'cors'}) socket.emit('game:move', { pawnId: pawn._id });
.then(() => {
setHintPawn(null);
});
} }
} }
} }
} };
const getHintPawnPosition = pawn => { const getHintPawnPosition = pawn => {
// Based on color (because specific color have specific base and end positions) // Based on color (because specific color have specific base and end positions)
let { position } = pawn; let { position } = pawn;
switch (context.color){ switch (context.color) {
case 'red': case 'red':
// When in base // When in base
if(position >= 0 && position <= 3) { if (position >= 0 && position <= 3) {
return 16; return 16;
// Next to end // Next to end
}else if(position <= 66 && position + rolledNumber >= 67){ } else if (position <= 66 && position + rolledNumber >= 67) {
return position + rolledNumber + 1; // 1 is difference between last position on map and first on end return position + rolledNumber + 1; // 1 is difference between last position on map and first on end
// Normal move // Normal move
}else{ } else {
return position + rolledNumber; return position + rolledNumber;
} }
case 'blue': case 'blue':
// When in base // When in base
if(position >= 4 && position <= 7){ if (position >= 4 && position <= 7) {
return 55; return 55;
// Next to red base // Next to red base
}else if(position <= 67 && position + rolledNumber > 67){ } else if (position <= 67 && position + rolledNumber > 67) {
return position + rolledNumber - 52; return position + rolledNumber - 52;
// Next to base // Next to base
}else if(position <= 53 && position + rolledNumber >= 54){ } else if (position <= 53 && position + rolledNumber >= 54) {
return position + rolledNumber + 20; return position + rolledNumber + 20;
// Normal move // Normal move
}else{ } else {
return position + rolledNumber; return position + rolledNumber;
} }
case 'green': case 'green':
// When in base // When in base
if(position >= 8 && position <= 11){ if (position >= 8 && position <= 11) {
return 42; return 42;
// Next to red base // Next to red base
}else if(position <= 67 && position + rolledNumber > 67){ } else if (position <= 67 && position + rolledNumber > 67) {
return position + rolledNumber - 52; return position + rolledNumber - 52;
// Next to base // Next to base
}else if(position <= 40 && position + rolledNumber >= 41){ } else if (position <= 40 && position + rolledNumber >= 41) {
return position + rolledNumber + 39; return position + rolledNumber + 39;
// Normal move // Normal move
}else{ } else {
return position + rolledNumber; return position + rolledNumber;
} }
case 'yellow': case 'yellow':
// When in base // When in base
if(position >= 12 && position <= 15){ if (position >= 12 && position <= 15) {
return 29; return 29;
// Next to red base // Next to red base
}else if(position <= 67 && position + rolledNumber > 67){ } else if (position <= 67 && position + rolledNumber > 67) {
return position + rolledNumber - 52; return position + rolledNumber - 52;
// Next to base // Next to base
}else if(position <= 27 && position + rolledNumber >= 28){ } else if (position <= 27 && position + rolledNumber >= 28) {
return position + rolledNumber + 58; return position + rolledNumber + 58;
// Normal move // Normal move
}else{ } else {
return position + rolledNumber; return position + rolledNumber;
} }
default: default:
@ -128,16 +127,16 @@ const Map = ({ pawns, nowMoving, rolledNumber }) => {
} }
}; };
const handleMouseMove = event => { const handleMouseMove = event => {
if(nowMoving && rolledNumber){ if (nowMoving && rolledNumber) {
const canvas = canvasRef.current; const canvas = canvasRef.current;
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
// Gets x and y cords of mouse on canvas // Gets x and y cords of mouse on canvas
const rect = canvas.getBoundingClientRect(), const rect = canvas.getBoundingClientRect(),
x = event.clientX - rect.left, x = event.clientX - rect.left,
y = event.clientY - rect.top; y = event.clientY - rect.top;
canvas.style.cursor = "default"; canvas.style.cursor = 'default';
for (const pawn of pawns){ for (const pawn of pawns) {
if(pawn.circle){ if (pawn.circle) {
/* /*
This condition checks if mouse location is: This condition checks if mouse location is:
1) on pawn 1) on pawn
@ -145,16 +144,20 @@ const Map = ({ pawns, nowMoving, rolledNumber }) => {
3) if pawn can move 3) if pawn can move
And then sets cursor to pointer and paints hint pawn - where will be pawn after click And then sets cursor to pointer and paints hint pawn - where will be pawn after click
*/ */
if (ctx.isPointInPath(pawn.circle, x, y) && context.color === pawn.color && checkIfPawnCanMove(pawn)) { if (
ctx.isPointInPath(pawn.circle, x, y) &&
context.color === pawn.color &&
checkIfPawnCanMove(pawn)
) {
const pawnPosition = getHintPawnPosition(pawn); const pawnPosition = getHintPawnPosition(pawn);
setBlinking(false); setBlinking(false);
// Checks if pawn can make a move // Checks if pawn can make a move
if(pawnPosition){ if (pawnPosition) {
canvas.style.cursor = "pointer"; canvas.style.cursor = 'pointer';
setHintPawn({id: pawn._id, position: pawnPosition, color: 'grey'}); setHintPawn({ id: pawn._id, position: pawnPosition, color: 'grey' });
break; break;
} }
}else{ } else {
setHintPawn(null); setHintPawn(null);
} }
} }
@ -166,35 +169,51 @@ const Map = ({ pawns, nowMoving, rolledNumber }) => {
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
const image = new Image(); const image = new Image();
image.src = 'https://img-9gag-fun.9cache.com/photo/a8GdpYZ_460s.jpg'; image.src = 'https://img-9gag-fun.9cache.com/photo/a8GdpYZ_460s.jpg';
image.onload = function() { image.onload = function () {
ctx.drawImage(image, 0, 0); ctx.drawImage(image, 0, 0);
pawns.forEach( (pawn, index) => { pawns.forEach((pawn, index) => {
if(nowMoving && rolledNumber && blinking && pawn.color === context.color && checkIfPawnCanMove(pawn)){ if (nowMoving && rolledNumber && blinking && pawn.color === context.color && checkIfPawnCanMove(pawn)) {
pawns[index].circle = paintPawn(ctx, positions[pawn.position].x, positions[pawn.position].y, 'white'); pawns[index].circle = paintPawn(
}else{ ctx,
pawns[index].circle = paintPawn(ctx, positions[pawn.position].x, positions[pawn.position].y, pawn.color); positions[pawn.position].x,
positions[pawn.position].y,
'white'
);
} else {
pawns[index].circle = paintPawn(
ctx,
positions[pawn.position].x,
positions[pawn.position].y,
pawn.color
);
} }
setBlinking(!blinking); setBlinking(!blinking);
}); });
if (hintPawn){ if (hintPawn) {
paintPawn(ctx, positions[hintPawn.position].x, positions[hintPawn.position].y, hintPawn.color); paintPawn(ctx, positions[hintPawn.position].x, positions[hintPawn.position].y, hintPawn.color);
} }
} };
},[ checkIfPawnCanMove, context.color, hintPawn, nowMoving, pawns, rolledNumber]); }, [checkIfPawnCanMove, context.color, hintPawn, nowMoving, pawns, rolledNumber]);
// Rerender canvas when pawns have changed // Rerender canvas when pawns have changed
useEffect(() => { useEffect(() => {
rerenderCanvas(); rerenderCanvas();
}, [hintPawn, pawns, rerenderCanvas]); }, [hintPawn, pawns, rerenderCanvas]);
return( useEffect(() => {
socket.on('game:move', () => {
setHintPawn(null);
});
}, [socket]);
return (
<canvas <canvas
className="canvas-container" className='canvas-container'
width={480} width={480}
height={480} height={480}
ref={canvasRef} ref={canvasRef}
onClick={handleCanvasClick} onClick={handleCanvasClick}
onMouseMove={handleMouseMove} onMouseMove={handleMouseMove}
/> />
) );
} };
export default Map; export default Map;