THIS IS A MAJOR MILESTONE
Socket connection re-implemented. Everything is working fine now. All thats left to do is to make the app robust and handle some edge cases. TODO - The friends list is maybe hardcoded or whatever I dont know, but the problem is it shows the same username no matter who the signed in user is. I need to fix that. - Whenever the pawn reaches to tha last square for promotion an 'Invalid move' error is thrown by the Chess class. Need to fix that too. - Need to disable the chessboard (so user cannot make any moves) until the opponent has joined the game. - Set timers for the game. - Need to show the loading states in various places in the app. - Improve the backend such that a user cannot create more than one room at a time. - Remove hardcoded values from various places. - Make the app responsive. The UI is good on desktop but it is very bad on mobile. - Add a feature to search and add friends. - Destroying the rooms when the match ends and set timers for auto destruction after a certain time. - Need some logic to save games in the history. - Add some tests.
This commit is contained in:
+12
-7
@@ -10,24 +10,29 @@ const pendingChallenges = new Map();
|
||||
// and vice versa is not true
|
||||
router.post("/create", checkAuth, async (req, res, next) => {
|
||||
console.log(req.body);
|
||||
// challenger and challenged are username
|
||||
const { challenger, challenged } = req.body;
|
||||
// challenger and challenged are username, color is the color played by challenger, timeLimit is the timeLimit for one player
|
||||
const { challenger, challenged, color, timeLimit } = req.body;
|
||||
|
||||
const challengedEmail = (await User.findOne({ username: challenged })).email;
|
||||
console.log(challengedEmail);
|
||||
// get email of the challenged person
|
||||
// const challengedEmail = (await User.findOne({ username: challenged })).email;
|
||||
// console.log(challengedEmail);
|
||||
|
||||
const roomID = uuid.v4();
|
||||
createRoom(roomID, req.body.timeLimit);
|
||||
createRoom(roomID, timeLimit);
|
||||
|
||||
// create a challenge and add it to pendingChallenges to notify the challenged user
|
||||
// structure of challenge: {challenger,roomID,color,timeLimit}
|
||||
if (pendingChallenges.has(challenged)) {
|
||||
let challenges = pendingChallenges.get(challenged);
|
||||
challenges.push(challenger);
|
||||
challenges.push({ challenger, roomID, color, timeLimit });
|
||||
} else {
|
||||
pendingChallenges.set(challenged, [{ challenger, roomID }]);
|
||||
// color is the choosed by the challenger
|
||||
pendingChallenges.set(challenged, [{ challenger, roomID, color, timeLimit }]);
|
||||
}
|
||||
|
||||
console.log("Pending challenges", pendingChallenges);
|
||||
|
||||
// STOP SENDING EMAILS FOR NOW
|
||||
// sendEmail(
|
||||
// challengedEmail,
|
||||
// `Challenge from ${challenger}`,
|
||||
|
||||
+24
-14
@@ -1,37 +1,43 @@
|
||||
const socket = require("socket.io");
|
||||
|
||||
// roomID => { timeLimit, players:[{username: {color}}] }
|
||||
let activeRooms = new Map();
|
||||
|
||||
function createRoom(roomID, timeLimit) {
|
||||
console.log(roomID, "created");
|
||||
activeRooms.set(roomID, { timeLimit, players: [] });
|
||||
activeRooms.set(roomID, { timeLimit, players: {} });
|
||||
console.log("Currently active rooms", activeRooms.size);
|
||||
}
|
||||
|
||||
// structure of userDetails: {username,color}
|
||||
function addUserToRoom(roomID, userDetails) {
|
||||
console.log(userDetails);
|
||||
let { username, color } = userDetails;
|
||||
let room = activeRooms.get(roomID);
|
||||
|
||||
if (room.players) {
|
||||
// room is full
|
||||
if (Object.keys(room.players).length > 1) {
|
||||
return "room-full";
|
||||
} else {
|
||||
// only one user in room
|
||||
room.players[username].color = color;
|
||||
}
|
||||
} else {
|
||||
// add player in the room
|
||||
room.players = {};
|
||||
room.players[username].color = color;
|
||||
if (room.players[username]) {
|
||||
room.players[username] = { color };
|
||||
return "join-room-success";
|
||||
}
|
||||
if (Object.keys(room.players).length > 1) {
|
||||
// room is full
|
||||
console.log(activeRooms);
|
||||
return "room-full";
|
||||
} else {
|
||||
room.players[username] = { color };
|
||||
}
|
||||
console.log(activeRooms);
|
||||
|
||||
return "join-room-success";
|
||||
}
|
||||
|
||||
// initialize the socket server with the given http server instance
|
||||
function socketIOServerInit(server) {
|
||||
const io = new socket.Server(server);
|
||||
const io = new socket.Server(server, {
|
||||
cors: {
|
||||
origin: process.env.CORS_ALLOWED_HOST,
|
||||
},
|
||||
});
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
let id = socket.id;
|
||||
@@ -57,6 +63,10 @@ function socketIOServerInit(server) {
|
||||
socket.emit("join-room-error", "room does not exist");
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("move", (roomID, moveData) => {
|
||||
socket.to(roomID).emit("opponent-move", moveData);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -23,4 +23,5 @@ dist-ssr
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
.env
|
||||
.env
|
||||
test.jsx
|
||||
@@ -10,7 +10,6 @@ const Cell = ({ cell, chess, marked, dispatch }) => {
|
||||
const [isDropped, setIsDropped] = useState(false);
|
||||
let squareColor = chess.squareColor(square) === 'light' ? "w" : "b";
|
||||
|
||||
console.log(chess.turn() !== localStorage.getItem('my_color'))
|
||||
const handleClick = () => {
|
||||
if (chess.turn() !== localStorage.getItem('my_color')) return;
|
||||
if (chess.myColor === color) {
|
||||
|
||||
@@ -5,10 +5,6 @@ import { getUserData } from '../../utils/auth'
|
||||
|
||||
const Challenges = () => {
|
||||
const navigate = useNavigate();
|
||||
const dummyChallenges = [
|
||||
{ challenger: 'moonpatel', roomID: 'sgnkjsdbnojsnvjsdnkl' },
|
||||
{ challenger: 'user1', roomID: 'sgnkjsdbnojsnvjsdnkl' }
|
||||
]
|
||||
const [challenges, setChallenges] = useState([]);
|
||||
const { username } = getUserData()
|
||||
|
||||
@@ -58,12 +54,19 @@ const Challenges = () => {
|
||||
<Title mt="20px" mb="10px" order={3}>Challenges</Title>
|
||||
<Stack>
|
||||
{
|
||||
challenges.map(({ challenger, roomID }) => {
|
||||
challenges.map(({ challenger, roomID, color, timeLimit }) => {
|
||||
console.log(challenger, roomID, color, timeLimit);
|
||||
return (
|
||||
<Group position='apart'>
|
||||
<Text>Challenge by {challenger}</Text>
|
||||
<Group position='center'>
|
||||
<Button color='lime' onClick={() => navigate(`/game/friend/${roomID}`)}>Accept</Button>
|
||||
<Button color='lime' onClick={() => {
|
||||
localStorage.setItem('myColor', color === 'b' ? 'w' : 'b');
|
||||
localStorage.setItem('roomID', roomID);
|
||||
localStorage.setItem('opponent', challenger);
|
||||
localStorage.setItem('timeLimit', timeLimit);
|
||||
navigate(`/game/friend/${roomID}`);
|
||||
}}>Accept</Button>
|
||||
<Button color='gray'>Decline</Button>
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
@@ -8,41 +8,75 @@ import { getUserData } from '../../../utils/auth'
|
||||
const ChessGame = () => {
|
||||
const user = getUserData();
|
||||
let username = user.username;
|
||||
let color = localStorage.getItem('myColor')
|
||||
const [hasJoinedRoom, setHasJoinedRoom] = useState(localStorage.getItem('socketid'));
|
||||
const [isWaiting, setIsWaiting] = useState(true);
|
||||
const roomID = useParams()['roomID'];
|
||||
const roomID = localStorage.getItem('roomID')
|
||||
const navigate = useNavigate();
|
||||
|
||||
const exitGame = () => {
|
||||
localStorage.removeItem('socketid');
|
||||
localStorage.removeItem('roomID');
|
||||
localStorage.removeItem('opponent');
|
||||
localStorage.removeItem('my_color');
|
||||
localStorage.removeItem('time_limit');
|
||||
localStorage.removeItem('myColor');
|
||||
localStorage.removeItem('timeLimit');
|
||||
socket.disconnect();
|
||||
navigate('/play/friend');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (hasJoinedRoom) {
|
||||
} else {
|
||||
console.log('Connecting');
|
||||
socket.connect();
|
||||
socket.on('connect', () => {
|
||||
localStorage.setItem('socketid', socket.id);
|
||||
console.log('Connected');
|
||||
});
|
||||
socket.emit('join-room', roomID, username, localStorage.getItem('opponent'), { color: localStorage.getItem('my_color'), timeLimit: localStorage.getItem('time_limit') });
|
||||
socket.on('joined-room', () => {
|
||||
setHasJoinedRoom(true);
|
||||
});
|
||||
socket.on('room-created', () => {
|
||||
console.log('Room is created')
|
||||
setIsWaiting(false);
|
||||
});
|
||||
}
|
||||
socket.connect();
|
||||
|
||||
socket.on('connect', () => {
|
||||
console.log('Connected');
|
||||
});
|
||||
|
||||
socket.on('join-room-success', () => {
|
||||
console.log('Room joined:', roomID);
|
||||
setHasJoinedRoom(true);
|
||||
});
|
||||
|
||||
socket.on('room-full', () => {
|
||||
console.log('Room is full');
|
||||
})
|
||||
|
||||
socket.on("join-room-error", (err) => {
|
||||
console.error("Error:", err);
|
||||
})
|
||||
|
||||
console.log('JOINING ROOM')
|
||||
socket.emit("join-room", roomID, { username, color })
|
||||
|
||||
socket.on('disconnect', (reason) => {
|
||||
console.log('Socket disconnected due to', reason);
|
||||
})
|
||||
|
||||
socket.on('opponent-move', (data) => {
|
||||
console.log(data)
|
||||
})
|
||||
|
||||
}, []);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (hasJoinedRoom) {
|
||||
// } else {
|
||||
// console.log('Connecting');
|
||||
// socket.connect();
|
||||
// socket.on('connect', () => {
|
||||
// localStorage.setItem('socketid', socket.id);
|
||||
// console.log('Connected');
|
||||
// });
|
||||
// socket.emit('join-room', roomID, username, localStorage.getItem('opponent'), { color: localStorage.getItem('myColor'), timeLimit: localStorage.getItem('timeLimit') });
|
||||
// socket.on('joined-room', () => {
|
||||
// setHasJoinedRoom(true);
|
||||
// });
|
||||
// socket.on('room-created', () => {
|
||||
// console.log('Room is created')
|
||||
// setIsWaiting(false);
|
||||
// });
|
||||
// }
|
||||
// }, []);
|
||||
|
||||
if (!hasJoinedRoom) return (
|
||||
<Loader variant='bars' />
|
||||
)
|
||||
@@ -56,7 +90,7 @@ const ChessGame = () => {
|
||||
icon={<Avatar radius="3px" />}
|
||||
description={"description"}
|
||||
/>
|
||||
<ChessBoard color={localStorage.getItem('my_color')} />
|
||||
<ChessBoard color={localStorage.getItem('myColor')} />
|
||||
<NavLink
|
||||
p="2px"
|
||||
label={username}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Avatar, Button, Card, Flex, Image, Select, Text, TextInput, Title } from '@mantine/core'
|
||||
import React from 'react'
|
||||
import FriendsList from '../../components/FriendsList'
|
||||
import { IconSearch } from '@tabler/icons-react'
|
||||
import { Form, Link, redirect, useParams, useSearchParams } from 'react-router-dom'
|
||||
import { Form, redirect, useParams } from 'react-router-dom'
|
||||
import { getAuthToken, getUserData } from '../../../utils/auth'
|
||||
|
||||
const ChallengeFriend = () => {
|
||||
@@ -23,7 +21,7 @@ const ChallengeFriend = () => {
|
||||
<Avatar mt="lg" color='lime' size="100px">{friend_username[0].toUpperCase()}</Avatar>
|
||||
<Text>{friend_username}</Text>
|
||||
</Flex>
|
||||
<Select label="Time limit" placeholder='Time limit' name='time_limit' defaultValue='10' data={['5', '10', '15', '30']} />
|
||||
<Select label="Time limit" placeholder='Time limit' name='timeLimit' defaultValue='10' data={['5', '10', '15', '30']} />
|
||||
<Select defaultValue='w' my="20px" color='lime' name='color' label={<Text mx="auto" order={3}>I play as</Text>} placeholder='choose your color' data={[
|
||||
{ value: 'w', label: 'White' },
|
||||
{ value: 'b', label: 'Black' },
|
||||
@@ -40,13 +38,13 @@ const ChallengeFriend = () => {
|
||||
export const playFriendAction = async ({ request, params }) => {
|
||||
const formData = await request.formData();
|
||||
let color = formData.get('color');
|
||||
let timeLimit = formData.get('time_limit');
|
||||
let timeLimit = formData.get('timeLimit');
|
||||
let username = getUserData().username;
|
||||
let challenged = params.friend_username;
|
||||
console.log(color, timeLimit, username, challenged);
|
||||
|
||||
let roomIDURL = `${import.meta.env.VITE_BACKEND_HOST}/api/room/create`;
|
||||
let reqBody = { challenger: username, challenged }
|
||||
let reqBody = { challenger: username, challenged, timeLimit, color }
|
||||
|
||||
try {
|
||||
console.log(reqBody);
|
||||
@@ -61,8 +59,9 @@ export const playFriendAction = async ({ request, params }) => {
|
||||
const { roomID } = resJSON;
|
||||
console.log('Room ID:', roomID);
|
||||
|
||||
localStorage.setItem('roomID', roomID)
|
||||
localStorage.setItem('my_color', color);
|
||||
// set properties of the game
|
||||
localStorage.setItem('roomID', roomID);
|
||||
localStorage.setItem('myColor', color);
|
||||
localStorage.setItem('timeLimit', timeLimit);
|
||||
localStorage.setItem('opponent', challenged);
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'SELECT_PIECE':
|
||||
{
|
||||
if (state.chess.turn() !== localStorage.getItem('my_color')) return state;
|
||||
if (state.chess.turn() !== localStorage.getItem('myColor')) return state;
|
||||
state.chess.select(action.val.square);
|
||||
return { ...state, moveHints: state.chess.getMoves(state.chess.selected) };
|
||||
}
|
||||
@@ -20,14 +20,14 @@ const reducer = (state, action) => {
|
||||
console.log('Moving', action.val, state.chess.turn());
|
||||
let newChessObj = new ChessModified({ prop: state.chess.fen(), color: state.chess.myColor })
|
||||
newChessObj.move(action.val);
|
||||
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('my_color')), moveHints: [] };
|
||||
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [] };
|
||||
}
|
||||
case 'CAPTURE_PIECE':
|
||||
{
|
||||
console.log('Capture', action.val, state.chess.turn())
|
||||
let newChessObj = new ChessModified({ prop: state.chess.fen(), color: state.chess.myColor });
|
||||
newChessObj.move(action.val);
|
||||
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('my_color')), moveHints: [] };
|
||||
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [] };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
@@ -52,6 +52,7 @@ const ChessBoard = ({ color }) => {
|
||||
useEffect(() => {
|
||||
function handleOpponentMove(data) {
|
||||
let { from, to } = data;
|
||||
console.log(from + to)
|
||||
if (!gameState.chess.get(to)) {
|
||||
console.log('Moving piece: ', data)
|
||||
dispatch({ type: 'MOVE_PIECE', val: { from, to } });
|
||||
|
||||
Reference in New Issue
Block a user