I'm writing a multiplayer chess app using chess.js, react-chessboard, socket.io, and React that should allow users to play chess with each other and store the history of the games. I've gotten it to work up to a point, I'm able to start a game, play a few moves between 2 users, and send the position to the database but as soon as I leave to the menu and re-enter the game, it only allows 1 move to be made and it doesn't send that move to the other player. The socket seems to be firing because an error saying the move was invalid shows up on the other users console, which is strange because the move was allowed on the first users screen and they have the same chessboard positions on screen.
Here is my current code for the chess game component. There's not much going on server side just some simple sockets.
function ChessGame({socket}) {
const dispatch = useDispatch();
const user = useSelector(store => store.user);
const room = useSelector(store => store.room);
let [position, setPosition] = useState('start');
let [game, setGame] = useState(new Chess());
let [turn, setTurn] = useState(game.turn());
let [color, setColor] = useState('w')
let [draggable, setDraggable] = useState(true);
const { id } = useParams();
let [pgn, setPgn] = useState();
useEffect(() => {
socket.emit('joinRoom', id);
dispatch({type: 'FETCH_ROOM', payload: id});
}, [id]);
// Loads PGN once the pgn variable has been updated
// function setUpBoard () {
// console.log(pgn)
// game.loadPgn(pgn)
// };
// useEffect(() => {
// if(pgn) {
// setUpBoard()
// }
// }, [pgn])
// Update variables once the room saga has loaded
useEffect(() => {
if(room.length === 1) {
setPlayerColor();
setPgn(room[0].pgn)
if(room[0].position !== 'start') {
setGame(new Chess(room[0].position));
setPosition(room[0].position)
console.log(game)
}
}
}, [room]);
useEffect(() => {
setTurn(game.turn())
}, [game])
// Sets player color from the database
function setPlayerColor() {
if(user.id === room[0].black) {
setColor('b');
} else if (user.id === room[0].white) {
setColor('w');
} else {
setDraggable(false);
}
};
// Updates game on player move
const makeMove = (move) => {
game.move(move, {sloppy: true});
setGame(new Chess(game.fen()));
setPgn(game.pgn());
};
// for onPieceDrop prop in chessboard, emits to makeMove socket
function onDrop(sourceSquare, targetSquare) {
const movePiece = makeMove({
from: sourceSquare,
to: targetSquare,
promotion: "q",
});
const move = {
from: sourceSquare,
to: targetSquare,
promotion: "q",
};
setPosition(game.fen());
putPosition()
if (movePiece === null) {return false}
else {
socket.emit('makeMove', move, id);
return true;
};
}
// Controls whether the pieces are draggable based on whos turn it is
function areDraggable () {
console.log(color, turn)
if(color == turn) {
setDraggable(true)
} else {
setDraggable(false)
}
};
useEffect(() => {
areDraggable();
}, [turn, color]);
socket.on('makeMove', (move) => {
onDrop(move.from, move.to);
setPosition(game.fen());
});
let gameObject = {
position,
pgn,
}
// Update database with new position and pgn
function putPosition() {
axios.put(`/api/game/position/${id}`, gameObject).then((response) => {
}).catch(error => {
console.log('Error in PUT /position', error);
})
};
I've tried .loadPgn to populate the game but that didn't change anything. It may be that I'm implementing it incorrectly. Any insight would be greatly appreciated! I'm pretty new to React and coding in general so any pointers would help a lot. Thank you!
Getting rid of the movePiece !== null
and placing the makeMove
and the variable changes inside of a try/catch worked!