Game state persistency implemented. Game state is
now saved on page refresh. The game state is stored on the server and when the user refreshes the page the game state is fetched from the server on socket re-connection Next step: stop user from making any move when the use is viewing past moves
This commit is contained in:
Generated
+6
@@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
|
"chess.js": "^1.0.0-beta.6",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
@@ -144,6 +145,11 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chess.js": {
|
||||||
|
"version": "1.0.0-beta.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/chess.js/-/chess.js-1.0.0-beta.6.tgz",
|
||||||
|
"integrity": "sha512-sqBfX1VL3csSyqVM5ogbKA+aRlZyWDh276ruWXphwI0lDUMs7iYjZs29BOi49f7mXeunJE7cdfnIZhihsyLnsA=="
|
||||||
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
|
"chess.js": "^1.0.0-beta.6",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
|||||||
+11
-3
@@ -2,15 +2,19 @@ const socket = require("socket.io");
|
|||||||
const { SOCKET_EVENTS } = require("./constants");
|
const { SOCKET_EVENTS } = require("./constants");
|
||||||
const { CHESS_MOVE, CHESS_OPPONENT_MOVE, CONNECTION, JOIN_ROOM, JOIN_ROOM_ERROR, JOIN_ROOM_SUCCESS, ROOM_FULL } =
|
const { CHESS_MOVE, CHESS_OPPONENT_MOVE, CONNECTION, JOIN_ROOM, JOIN_ROOM_ERROR, JOIN_ROOM_SUCCESS, ROOM_FULL } =
|
||||||
SOCKET_EVENTS;
|
SOCKET_EVENTS;
|
||||||
// roomID => { timeLimit, players:[{username: {color}}] }
|
// roomID => { timeLimit,gameHistory , players:[{username: {color}}] }
|
||||||
let activeRooms = new Map();
|
let activeRooms = new Map();
|
||||||
|
|
||||||
function createRoom(roomID, timeLimit) {
|
function createRoom(roomID, timeLimit) {
|
||||||
console.log(roomID, "created");
|
console.log(roomID, "created");
|
||||||
activeRooms.set(roomID, { timeLimit, players: {} });
|
activeRooms.set(roomID, { timeLimit, players: {}, gameHistory: [] });
|
||||||
console.log("Currently active rooms", activeRooms.size);
|
console.log("Currently active rooms", activeRooms.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRoom(roomID) {
|
||||||
|
return activeRooms.get(roomID);
|
||||||
|
}
|
||||||
|
|
||||||
// structure of userDetails: {username,color}
|
// structure of userDetails: {username,color}
|
||||||
function addUserToRoom(roomID, userDetails) {
|
function addUserToRoom(roomID, userDetails) {
|
||||||
console.log(userDetails);
|
console.log(userDetails);
|
||||||
@@ -57,7 +61,9 @@ function socketIOServerInit(server) {
|
|||||||
if (result === JOIN_ROOM_SUCCESS) {
|
if (result === JOIN_ROOM_SUCCESS) {
|
||||||
socket.join(roomID);
|
socket.join(roomID);
|
||||||
io.to(roomID).emit("new user joined the room");
|
io.to(roomID).emit("new user joined the room");
|
||||||
socket.emit(result); // room joined successfully
|
console.log(data, "joined");
|
||||||
|
let room = getRoom(roomID);
|
||||||
|
socket.emit(result, room.gameHistory); // room joined successfully
|
||||||
} else {
|
} else {
|
||||||
socket.emit(result); // room is full
|
socket.emit(result); // room is full
|
||||||
}
|
}
|
||||||
@@ -68,6 +74,8 @@ function socketIOServerInit(server) {
|
|||||||
|
|
||||||
socket.on(CHESS_MOVE, (roomID, moveData) => {
|
socket.on(CHESS_MOVE, (roomID, moveData) => {
|
||||||
console.log(moveData);
|
console.log(moveData);
|
||||||
|
let room = activeRooms.get(roomID);
|
||||||
|
room.gameHistory.push(moveData);
|
||||||
socket.to(roomID).emit(CHESS_OPPONENT_MOVE, moveData);
|
socket.to(roomID).emit(CHESS_OPPONENT_MOVE, moveData);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import React, { useContext, useState } from 'react'
|
import React, { useContext, useState } from 'react'
|
||||||
import Piece from './Piece';
|
import Piece from './Piece';
|
||||||
import { socket } from '../socket';
|
import { socket } from '../socket';
|
||||||
import { Box, Flex } from '@mantine/core';
|
import { Box, Flex, Modal } from '@mantine/core';
|
||||||
import { useDroppable } from '@dnd-kit/core'
|
import { useDroppable } from '@dnd-kit/core'
|
||||||
import { ChessGameContext } from '../context/chess-game-context';
|
import { ChessGameContext } from '../context/chess-game-context';
|
||||||
|
import { SOCKET_EVENTS } from '../constants';
|
||||||
|
const { CHESS_MOVE } = SOCKET_EVENTS
|
||||||
|
|
||||||
const Cell = ({ cell }) => {
|
const Cell = ({ cell }) => {
|
||||||
|
let roomID = localStorage.getItem('roomID');
|
||||||
let { square, type, color } = cell;
|
let { square, type, color } = cell;
|
||||||
const { getSquareColor, isSquareMarked, handleSquareClick } = useContext(ChessGameContext)
|
const { getSquareColor, isSquareMarked, handleSquareClick } = useContext(ChessGameContext)
|
||||||
const [isDropped, setIsDropped] = useState(false);
|
const [isDropped, setIsDropped] = useState(false);
|
||||||
@@ -14,7 +17,10 @@ const Cell = ({ cell }) => {
|
|||||||
let marked = isSquareMarked(square);
|
let marked = isSquareMarked(square);
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
handleSquareClick(square);
|
handleSquareClick(square, (moveData) => {
|
||||||
|
// moveData contains fen string, from, to squares of the move
|
||||||
|
socket.emit(CHESS_MOVE, roomID, moveData);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = marked ? <Mark /> : <Piece cell={cell} />;
|
let content = marked ? <Mark /> : <Piece cell={cell} />;
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ export const DISPATCH_EVENTS = {
|
|||||||
MOVE_PIECE: "MOVE_PIECE",
|
MOVE_PIECE: "MOVE_PIECE",
|
||||||
CAPTURE_PIECE: "CAPTURE_PIECE",
|
CAPTURE_PIECE: "CAPTURE_PIECE",
|
||||||
JUMP_TO: "JUMP_TO",
|
JUMP_TO: "JUMP_TO",
|
||||||
|
SET_GAME_HISTORY: "SET_GAME_HISTORY",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { createContext, useReducer, useRef } from 'react'
|
|||||||
import { ChessModified, chessInit } from '../../utils/chess';
|
import { ChessModified, chessInit } from '../../utils/chess';
|
||||||
import { DISPATCH_EVENTS } from '../constants';
|
import { DISPATCH_EVENTS } from '../constants';
|
||||||
import ChessBoard from '../pages/Chess/ChessBoard';
|
import ChessBoard from '../pages/Chess/ChessBoard';
|
||||||
const { CAPTURE_PIECE, MOVE_PIECE, SELECT_PIECE, JUMP_TO } = DISPATCH_EVENTS
|
const { CAPTURE_PIECE, MOVE_PIECE, SELECT_PIECE, JUMP_TO, SET_GAME_HISTORY } = DISPATCH_EVENTS
|
||||||
export const ChessGameContext = createContext();
|
export const ChessGameContext = createContext();
|
||||||
// myColor: null, chess: null, chessBoard: null, moveHints: null, selected: null, dispatch: null, handleOpponentMove: null, handleSquareClick: null, getSquareColor: null, isSquareMarked: null, selectPiece: null, handleDrop: null
|
// myColor: null, chess: null, chessBoard: null, moveHints: null, selected: null, dispatch: null, handleOpponentMove: null, handleSquareClick: null, getSquareColor: null, isSquareMarked: null, selectPiece: null, handleDrop: null
|
||||||
|
|
||||||
@@ -20,8 +20,8 @@ const reducer = (state, action) => {
|
|||||||
console.log('Moving', action.val, state.chess.turn());
|
console.log('Moving', action.val, state.chess.turn());
|
||||||
let newChessObj = new ChessModified({ prop: state.chess.fen(), color: state.chess.myColor });
|
let newChessObj = new ChessModified({ prop: state.chess.fen(), color: state.chess.myColor });
|
||||||
let updatedGameHistory = state.gameHistory;
|
let updatedGameHistory = state.gameHistory;
|
||||||
let move = newChessObj.move(action.val);
|
let { san, after } = newChessObj.move(action.val);
|
||||||
updatedGameHistory.push({ move: move.san, fen: newChessObj.fen() });
|
updatedGameHistory.push({ move: san, fen: after });
|
||||||
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
|
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
|
||||||
}
|
}
|
||||||
case CAPTURE_PIECE:
|
case CAPTURE_PIECE:
|
||||||
@@ -29,8 +29,8 @@ const reducer = (state, action) => {
|
|||||||
console.log('Capture', action.val, state.chess.turn());
|
console.log('Capture', action.val, state.chess.turn());
|
||||||
let newChessObj = new ChessModified({ prop: state.chess.fen(), color: state.chess.myColor });
|
let newChessObj = new ChessModified({ prop: state.chess.fen(), color: state.chess.myColor });
|
||||||
let updatedGameHistory = state.gameHistory;
|
let updatedGameHistory = state.gameHistory;
|
||||||
let move = newChessObj.move(action.val);
|
let { san, after } = newChessObj.move(action.val);
|
||||||
updatedGameHistory.push({ move: move.san, fen: newChessObj.fen() });
|
updatedGameHistory.push({ move: san, fen: after });
|
||||||
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
|
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
|
||||||
}
|
}
|
||||||
case JUMP_TO:
|
case JUMP_TO:
|
||||||
@@ -38,6 +38,17 @@ const reducer = (state, action) => {
|
|||||||
let index = action.val;
|
let index = action.val;
|
||||||
return { ...state, currentIndex: index }
|
return { ...state, currentIndex: index }
|
||||||
}
|
}
|
||||||
|
case SET_GAME_HISTORY:
|
||||||
|
{
|
||||||
|
let fetchedGameHistory = action.val;
|
||||||
|
let newChessObj = new ChessModified({ color: state.chess.myColor });
|
||||||
|
let updatedGameHistory = [];
|
||||||
|
for (let i = 0; i < fetchedGameHistory.length; i++) {
|
||||||
|
let { san, after } = newChessObj.move(fetchedGameHistory[i]);
|
||||||
|
updatedGameHistory.push({ fen: after, move: san })
|
||||||
|
}
|
||||||
|
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(state.chess.myColor), gameHistory: updatedGameHistory }
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -60,7 +71,7 @@ const ChessGameContextProvider = ({ children }) => {
|
|||||||
let myColor = localStorage.getItem('myColor');
|
let myColor = localStorage.getItem('myColor');
|
||||||
console.log('INSIDE CONTEXT PROVIDER');
|
console.log('INSIDE CONTEXT PROVIDER');
|
||||||
const [{ chess, chessBoard, moveHints, selected, gameHistory, currentIndex }, dispatch] = useReducer(reducer, myColor, chessGameStateInit);
|
const [{ chess, chessBoard, moveHints, selected, gameHistory, currentIndex }, dispatch] = useReducer(reducer, myColor, chessGameStateInit);
|
||||||
console.log(gameHistory)
|
console.log(gameHistory);
|
||||||
|
|
||||||
|
|
||||||
console.log(gameHistory);
|
console.log(gameHistory);
|
||||||
@@ -71,7 +82,7 @@ const ChessGameContextProvider = ({ children }) => {
|
|||||||
|
|
||||||
// data received through socket
|
// data received through socket
|
||||||
function handleOpponentMove(data) {
|
function handleOpponentMove(data) {
|
||||||
let { from, to } = data;
|
let { from, to, fen } = data;
|
||||||
console.log(from + to);
|
console.log(from + to);
|
||||||
if (!chess.get(to)) {
|
if (!chess.get(to)) {
|
||||||
console.log('Moving piece: ', data);
|
console.log('Moving piece: ', data);
|
||||||
@@ -87,7 +98,7 @@ const ChessGameContextProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// called when user clicks a square
|
// called when user clicks a square
|
||||||
function handleSquareClick(square) {
|
function handleSquareClick(square, emitToSocketCallback) {
|
||||||
let { type, color } = chess.get(square);
|
let { type, color } = chess.get(square);
|
||||||
let marked = moveHints.includes(square);
|
let marked = moveHints.includes(square);
|
||||||
console.log('handleSquareClick', square)
|
console.log('handleSquareClick', square)
|
||||||
@@ -99,9 +110,13 @@ const ChessGameContextProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
if (!type && selected && marked) {
|
if (!type && selected && marked) {
|
||||||
dispatch({ type: MOVE_PIECE, val: { from: selected, to: square } })
|
dispatch({ type: MOVE_PIECE, val: { from: selected, to: square } })
|
||||||
|
emitToSocketCallback({ from: selected, to: square })
|
||||||
|
captureAudioRef.current.play();
|
||||||
}
|
}
|
||||||
if (type && marked) {
|
if (type && marked) {
|
||||||
dispatch({ type: CAPTURE_PIECE, val: { from: selected, to: square } })
|
dispatch({ type: CAPTURE_PIECE, val: { from: selected, to: square } })
|
||||||
|
emitToSocketCallback({ from: selected, to: square })
|
||||||
|
moveAudioRef.current.play();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
@@ -165,9 +180,14 @@ const ChessGameContextProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fetchedGameHistory is an array of objects of the form {from,to}
|
||||||
|
function setGameHistory(fetchedGameHistory) {
|
||||||
|
dispatch({ type: SET_GAME_HISTORY, val: fetchedGameHistory })
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChessGameContext.Provider value={{
|
<ChessGameContext.Provider value={{
|
||||||
myColor, chessBoard, moveHints, selected, handleOpponentMove, handleSquareClick, getSquareColor, isSquareMarked, selectPiece, handleDrop, gameHistory, jumpTo, getChessBoard, currentIndex, goAhead, goBack
|
myColor, chessBoard, moveHints, selected, handleOpponentMove, handleSquareClick, getSquareColor, isSquareMarked, selectPiece, handleDrop, gameHistory, jumpTo, getChessBoard, currentIndex, goAhead, goBack, setGameHistory
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
<audio src='/src/assets/move-self.mp3' ref={moveAudioRef} />
|
<audio src='/src/assets/move-self.mp3' ref={moveAudioRef} />
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { ChessGameContext } from '../../context/chess-game-context'
|
|||||||
import GameHistory from '../../components/GameHistory'
|
import GameHistory from '../../components/GameHistory'
|
||||||
|
|
||||||
const ChessGame = () => {
|
const ChessGame = () => {
|
||||||
const { gameHistory } = useContext(ChessGameContext);
|
const { gameHistory,setGameHistory } = useContext(ChessGameContext);
|
||||||
const user = getUserData();
|
const user = getUserData();
|
||||||
let username = user.username;
|
let username = user.username;
|
||||||
let color = localStorage.getItem('myColor')
|
let color = localStorage.getItem('myColor')
|
||||||
@@ -35,8 +35,9 @@ const ChessGame = () => {
|
|||||||
console.log('Connected');
|
console.log('Connected');
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('join-room-success', () => {
|
socket.on('join-room-success', (fetchedGameHistory) => {
|
||||||
console.log('Room joined:', roomID);
|
console.log('Room joined:', roomID);
|
||||||
|
setGameHistory(fetchedGameHistory);
|
||||||
setHasJoinedRoom(true);
|
setHasJoinedRoom(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user