diff --git a/backend/controllers/roomController.js b/backend/controllers/roomController.js index f698a3e..80a36ac 100644 --- a/backend/controllers/roomController.js +++ b/backend/controllers/roomController.js @@ -17,8 +17,9 @@ const getJoinableRoom = async () => { return await Room.findOne({ full: false, started: false }).exec(); }; -const createNewRoom = () => { - const room = new Room(); +const createNewRoom = data => { + const room = new Room(data); + room.save(); return room; }; diff --git a/backend/handlers/playerHandler.js b/backend/handlers/playerHandler.js index 2918d92..825d568 100644 --- a/backend/handlers/playerHandler.js +++ b/backend/handlers/playerHandler.js @@ -1,17 +1,15 @@ -const { getRoom, updateRoom, getJoinableRoom, createNewRoom, findPlayer } = require('../controllers/roomController'); +const { getRoom, updateRoom } = require('../controllers/roomController'); const { colors } = require('../utils/constants'); module.exports = socket => { const req = socket.request; const handleLogin = async data => { - if (await findPlayer(req.sessionID)) return; - const room = await getJoinableRoom(); - if (room) { - addPlayerToExistingRoom(room, data); - } else { - addNewRoom(data); - } + const room = await getRoom(data.roomId); + if (room.isFull()) return socket.emit('error:changeRoom'); + if (room.started) return socket.emit('error:changeRoom'); + if (room.private && room.password !== data.password) return socket.emit('error:wrongPassword'); + addPlayerToExistingRoom(room, data); }; const handleReady = async () => { @@ -23,13 +21,6 @@ module.exports = socket => { await updateRoom(room); }; - const addNewRoom = async data => { - const room = createNewRoom(); - room.addPlayer(data.name, req.sessionID); - await room.save(); - reloadSession(room); - }; - const addPlayerToExistingRoom = async (room, data) => { room.addPlayer(data.name); if (room.isFull()) { diff --git a/backend/handlers/roomHandler.js b/backend/handlers/roomHandler.js index fbde0d6..bee90de 100644 --- a/backend/handlers/roomHandler.js +++ b/backend/handlers/roomHandler.js @@ -1,10 +1,10 @@ -const { getRooms, getRoom, updateRoom } = require('../controllers/roomController'); +const { getRooms, getRoom, updateRoom, createNewRoom } = require('../controllers/roomController'); const { sendToOnePlayerRooms, sendToOnePlayerData, sendToPlayersData } = require('../socket/emits'); module.exports = socket => { const req = socket.request; - const getData = async () => { + const handleGetData = async () => { const room = await getRoom(req.session.roomId); // Handle the situation when the server crashes and any player reconnects after the time has expired // Typically, the responsibility for changing players is managed by gameHandler.js. @@ -15,23 +15,17 @@ module.exports = socket => { sendToOnePlayerData(socket.id, room); }; - const getAllRooms = async () => { + const handleGetAllRooms = async () => { let rooms = await getRooms(); - const response = []; - rooms.forEach(room => { - if (!room.isStarted && !room.isFull()) { - response.push({ - _id: room._id, - private: room.private, - name: room.name, - players: room.players, - isStarted: room.isStarted, - }); - } - }); - sendToOnePlayerRooms(socket.id, response); + sendToOnePlayerRooms(socket.id, rooms); }; - socket.on('room:data', getData); - socket.on('room:rooms', getAllRooms); + const handleCreateRoom = async data => { + createNewRoom(data); + socket.to(socket.id).emit('room:created'); + }; + + socket.on('room:data', handleGetData); + socket.on('room:rooms', handleGetAllRooms); + socket.on('room:create', handleCreateRoom); }; diff --git a/backend/models/room.js b/backend/models/room.js index c1672d4..2c7ca34 100644 --- a/backend/models/room.js +++ b/backend/models/room.js @@ -7,7 +7,7 @@ const PlayerSchema = require('./player'); const RoomSchema = new mongoose.Schema({ name: String, - private: { type: Boolean, default: true }, + private: { type: Boolean, default: false }, password: String, createDate: { type: Date, default: Date.now }, started: { type: Boolean, default: false }, diff --git a/src/components/LoginPage/AddServer/AddServer.css b/src/components/LoginPage/AddServer/AddServer.css new file mode 100644 index 0000000..4c64921 --- /dev/null +++ b/src/components/LoginPage/AddServer/AddServer.css @@ -0,0 +1,35 @@ +.refresh { + display: flex; + margin-left: auto; + justify-content: center; + align-items: center; + width: 40px; + height: 100%; + border: 1px solid white; +} +.refresh > img { + width: 20px; + height: 20px; + cursor: pointer; +} + +form { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; +} + +.private-container { + margin-left: 10px; + display: flex; + flex-direction: row; + align-items: center; + width: 100%; +} +input:disabled { + background-color: black; + color: #999; + border: 1px solid #ddd; +} diff --git a/src/components/LoginPage/AddServer/AddServer.jsx b/src/components/LoginPage/AddServer/AddServer.jsx new file mode 100644 index 0000000..054bf54 --- /dev/null +++ b/src/components/LoginPage/AddServer/AddServer.jsx @@ -0,0 +1,58 @@ +import React, { useState, useContext, useEffect } from 'react'; +import './AddServer.css'; +import Switch from '@material-ui/core/Switch'; +import { SocketContext } from '../../../App'; +const AddServer = () => { + const socket = useContext(SocketContext); + const [isPrivate, setIsPrivate] = useState(false); + const [serverName, setServerName] = useState(''); + const [password, setPassword] = useState(''); + + useEffect(() => { + socket.on('room:created', () => { + console.log('ewa'); + socket.emit('room:rooms'); + }); + }, []); + + const handleButtonClick = e => { + e.preventDefault(); + socket.emit('room:create', { + name: serverName, + private: isPrivate, + password: password, + }); + }; + + return ( +
+
+

Host A Server

+
+
+
+ setServerName(e.target.value)} + placeholder='Server Name' + /> +
+

Private

+ setIsPrivate(!isPrivate)} /> +
+ setPassword(e.target.value)} + placeholder='password' + disabled={!isPrivate} + /> + +
+
+
+ ); +}; + +export default AddServer; diff --git a/src/components/LoginPage/LoginPage.css b/src/components/LoginPage/LoginPage.css index 62099aa..3376c6f 100644 --- a/src/components/LoginPage/LoginPage.css +++ b/src/components/LoginPage/LoginPage.css @@ -1,76 +1,53 @@ .login-page-container { + display: flex; + flex-direction: row; + + justify-content: center; + align-items: flex-start; + height: 50%; + width: 100%; +} + +.lp-container { + margin: 50px; display: flex; flex-direction: column; align-items: center; justify-content: center; - height: 50vh; - width: 400px; - position: relative; + width: 500px; padding: 20px; - border-radius: 5%; - border: 5px solid white; - background-color: rgba(0, 0, 0, 0.5); -} -h1 { - margin-right: 10px; - align-self: flex-start; - top: 0; - position: absolute; color: white; } -.rooms { - width: 98%; - height: 80%; - overflow-y: scroll; - overflow-x: hidden; -} -.room { - cursor: pointer; - justify-content: space-between; + +.title-container { display: flex; flex-direction: row; + justify-content: center; align-items: center; - color: white; - width: 90%; - margin: 10px; - padding: 10px; - border: 1px solid black; -} -.room-selected { + width: 100%; + height: 40px; border: 1px solid white; + border-radius: 2px; + transform: scaleX(1.02); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + padding-left: 10px; + text-align: center; } -.room-selected, -.room:hover { - background-color: rgba(0, 0, 0, 0.5); + +.title-container > h1 { + width: 100%; + margin: 0; + padding: 0; } -.number-of-players { + +.content-container { display: flex; - flex-direction: row; - align-items: center; -} -.number-of-players > img { - margin-right: 5px; - width: 20px; - height: 20px; -} - -/* Firefox */ -* { - scrollbar-width: auto; - scrollbar-color: #ffffff rgba(0, 0, 0, 0.1); -} - -/* Chrome, Edge, and Safari */ -*::-webkit-scrollbar { - width: 8px; -} - -*::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0); -} - -*::-webkit-scrollbar-thumb { - background-color: #ffffff; - border-radius: 10px; - border: 3px none #ffffff; + flex-direction: column; + width: 100%; + padding: 10px; + background-color: rgba(0, 0, 0, 0.5); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + border-left: 1px solid black; + border-right: 1px solid black; + border-bottom: 1px solid black; } diff --git a/src/components/LoginPage/LoginPage.jsx b/src/components/LoginPage/LoginPage.jsx index 835787d..9532e2d 100644 --- a/src/components/LoginPage/LoginPage.jsx +++ b/src/components/LoginPage/LoginPage.jsx @@ -1,54 +1,16 @@ -import React, { useContext, useEffect, useState } from 'react'; -import NameInput from './NameInput/NameInput'; -import { SocketContext } from '../../App'; import './LoginPage.css'; -import userImage from '../../images/login-page/user.png'; +import AddServer from './AddServer/AddServer'; +import ServerList from './ServerList/ServerList'; +import NameInput from './NameInput/NameInput'; const LoginPage = () => { - const socket = useContext(SocketContext); - const [rooms, setRooms] = useState([]); - const [selectedRoom, setSelectedRoom] = useState(null); - - useEffect(async () => { - socket.emit('room:rooms'); - socket.on('room:rooms', data => { - data = JSON.parse(data); - console.log(data); - setRooms(data); - }); - }, []); - return ( -
-

Select room:

-
- {rooms.map(room => { - return ( -
{ - if (selectedRoom && selectedRoom == room._id) { - setSelectedRoom(null); - } else { - setSelectedRoom(room._id); - } - }} - key={room.id} - > -
-

{room.name}

- {room.players.map(player => player.name + ' ')} -
- -
- - {room.players.length}/4 -
-
- ); - })} + <> +
+ +
- -
+ ); }; + export default LoginPage; diff --git a/src/components/LoginPage/NameInput/NameInput.css b/src/components/LoginPage/NameInput/NameInput.css index f7509fe..9f6282c 100644 --- a/src/components/LoginPage/NameInput/NameInput.css +++ b/src/components/LoginPage/NameInput/NameInput.css @@ -1,11 +1,34 @@ .name-input-container { display: flex; - position: absolute; - bottom: 0; - flex-direction: row; - width: 80%; + flex-direction: column; + padding: 10px 20px 60px 20px; + width: 300px; + background: radial-gradient(circle, rgba(0, 138, 255, 1) 5%, rgba(9, 9, 121, 1) 81%); + border: 1px solid white; + border-radius: 8px; margin: 20px; } +.name-input-container > button { + margin-top: 5px; + text-align: center; + width: 100px; + align-self: center; +} +.name-input-container > input { + margin-top: 10px; +} +.name-overlay { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 1; +} input, button { padding: 0; @@ -21,7 +44,7 @@ input { border-radius: 8px; color: white; border: 1px solid #ccc; - background-color: rgba(0, 0, 0, 0.5); + background-color: rgba(0, 0, 0, 0.2); transition: border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; } @@ -37,7 +60,7 @@ button { border-radius: 8px; border: none; color: #fff; - background-color: rgba(0, 0, 0, 0.5); + background-color: rgba(0, 0, 0, 0.4); cursor: pointer; transition: background-color 0.3s ease-in-out; } diff --git a/src/components/LoginPage/NameInput/NameInput.jsx b/src/components/LoginPage/NameInput/NameInput.jsx index 491c56b..e1212e4 100644 --- a/src/components/LoginPage/NameInput/NameInput.jsx +++ b/src/components/LoginPage/NameInput/NameInput.jsx @@ -1,19 +1,44 @@ -import React, { useState, useContext } from 'react'; +import React, { useState, useContext, useEffect } from 'react'; import { SocketContext } from '../../../App'; import './NameInput.css'; -const NameInput = () => { +const NameInput = ({ isRoomPrivate, roomId }) => { const socket = useContext(SocketContext); - const [inputValue, setInputValue] = useState(''); - const handleInputChange = e => { - setInputValue(e.target.value); - }; + const [nickname, setNickname] = useState(''); + const [password, setPassword] = useState(''); + const [isPasswordWrong, setIsPasswordWrong] = useState(false); const handleButtonClick = () => { - socket.emit('player:login', { name: inputValue }); + socket.emit('player:login', { name: nickname, password: password, roomId: roomId }); }; + useEffect(() => { + socket.on('error:wrongPassword', () => { + setIsPasswordWrong(true); + }); + const keyDownHandler = event => { + if (event.key === 'Enter') { + event.preventDefault(); + handleButtonClick(); + } + }; + document.addEventListener('keydown', keyDownHandler); + return () => { + document.removeEventListener('keydown', keyDownHandler); + }; + }, []); + return ( -
- - +
+
+ setNickname(e.target.value)} /> + {isRoomPrivate ? ( + setPassword(e.target.value)} + style={{ backgroundColor: isPasswordWrong ? 'red' : null }} + /> + ) : null} + +
); }; diff --git a/src/components/LoginPage/ServerList/ServerList.css b/src/components/LoginPage/ServerList/ServerList.css new file mode 100644 index 0000000..318a008 --- /dev/null +++ b/src/components/LoginPage/ServerList/ServerList.css @@ -0,0 +1,51 @@ +th { + text-align: left; +} +img { + margin-right: 5px; + width: 20px; + height: 20px; +} +th, +td { + padding: 8px; + text-align: left; + height: 50px; +} +tr { + max-height: 50px; +} + +table { + border-collapse: collapse; + width: 100%; +} +.server-container { + display: flex; + height: 500px; + overflow: scroll; +} +.room-name { + max-width: 150px; + overflow: hidden; +} +/* Firefox */ +* { + scrollbar-width: auto; + scrollbar-color: #ffffff rgba(0, 0, 0, 0.1); +} + +/* Chrome, Edge, and Safari */ +*::-webkit-scrollbar { + background: rgba(0, 0, 0, 0); + width: 10px; +} + +*::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0); +} + +*::-webkit-scrollbar-thumb { + background-color: #ffffff; + border-radius: 10px; +} diff --git a/src/components/LoginPage/ServerList/ServerList.jsx b/src/components/LoginPage/ServerList/ServerList.jsx new file mode 100644 index 0000000..28e55a2 --- /dev/null +++ b/src/components/LoginPage/ServerList/ServerList.jsx @@ -0,0 +1,79 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { SocketContext } from '../../../App'; +import lock from '../../../images/login-page/lock.png'; +import refresh from '../../../images/login-page/refresh.png'; +import ReactLoading from 'react-loading'; + +import './ServerList.css'; +import NameInput from '../NameInput/NameInput'; + +const ServerList = () => { + const socket = useContext(SocketContext); + const [rooms, setRooms] = useState([]); + const [joining, setJoining] = useState(false); + const [clickedRoom, setClickedRoom] = useState(null); + useEffect(async () => { + socket.emit('room:rooms'); + socket.on('room:rooms', data => { + data = JSON.parse(data); + setRooms(data); + }); + }, []); + + const getRooms = () => { + setRooms(null); + socket.emit('room:rooms'); + }; + + const handleJoinClick = room => { + setClickedRoom(room); + setJoining(true); + }; + + return ( +
+
+

Server List

+
+ +
+
+
+ {rooms ? ( + + + + + + + + + + + + {rooms.map((room, index) => ( + + + + + + + + ))} + +
Server#/#Status
{room.private ? : null}{room.name}{`${room.players.length}/4`}{room.isStarted ? 'started' : 'waiting'} + +
+ ) : ( +
+ +
+ )} +
+ {joining ? ( + + ) : null} +
+ ); +}; +export default ServerList; diff --git a/src/components/navbar-components/AnimatedOverlay.jsx b/src/components/navbar-components/AnimatedOverlay.jsx index 82b1507..5fc82fa 100644 --- a/src/components/navbar-components/AnimatedOverlay.jsx +++ b/src/components/navbar-components/AnimatedOverlay.jsx @@ -14,7 +14,7 @@ const AnimatedOverlay = ({ time }) => { in={true} timeout={0} classNames='overlay' - style={{ 'animation-delay': `-${animationDelay}s` }} + style={{ animationDelay: `-${animationDelay}s` }} unmountOnExit >
diff --git a/src/images/login-page/lock.png b/src/images/login-page/lock.png new file mode 100644 index 0000000..8e56993 Binary files /dev/null and b/src/images/login-page/lock.png differ diff --git a/src/images/login-page/refresh.png b/src/images/login-page/refresh.png new file mode 100644 index 0000000..25988e6 Binary files /dev/null and b/src/images/login-page/refresh.png differ diff --git a/src/images/login-page/user.png b/src/images/login-page/user.png index 6d15e9e..0fa4040 100644 Binary files a/src/images/login-page/user.png and b/src/images/login-page/user.png differ