added joining to room

This commit is contained in:
Wenszel 2023-11-28 18:51:50 +01:00
parent a1b39a3a45
commit f318afe071
16 changed files with 356 additions and 160 deletions

View File

@ -17,8 +17,9 @@ const getJoinableRoom = async () => {
return await Room.findOne({ full: false, started: false }).exec(); return await Room.findOne({ full: false, started: false }).exec();
}; };
const createNewRoom = () => { const createNewRoom = data => {
const room = new Room(); const room = new Room(data);
room.save();
return room; return room;
}; };

View File

@ -1,17 +1,15 @@
const { getRoom, updateRoom, getJoinableRoom, createNewRoom, findPlayer } = require('../controllers/roomController'); const { getRoom, updateRoom } = require('../controllers/roomController');
const { colors } = require('../utils/constants'); const { colors } = require('../utils/constants');
module.exports = socket => { module.exports = socket => {
const req = socket.request; const req = socket.request;
const handleLogin = async data => { const handleLogin = async data => {
if (await findPlayer(req.sessionID)) return; const room = await getRoom(data.roomId);
const room = await getJoinableRoom(); if (room.isFull()) return socket.emit('error:changeRoom');
if (room) { if (room.started) return socket.emit('error:changeRoom');
if (room.private && room.password !== data.password) return socket.emit('error:wrongPassword');
addPlayerToExistingRoom(room, data); addPlayerToExistingRoom(room, data);
} else {
addNewRoom(data);
}
}; };
const handleReady = async () => { const handleReady = async () => {
@ -23,13 +21,6 @@ module.exports = socket => {
await updateRoom(room); 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) => { const addPlayerToExistingRoom = async (room, data) => {
room.addPlayer(data.name); room.addPlayer(data.name);
if (room.isFull()) { if (room.isFull()) {

View File

@ -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'); const { sendToOnePlayerRooms, sendToOnePlayerData, sendToPlayersData } = require('../socket/emits');
module.exports = socket => { module.exports = socket => {
const req = socket.request; const req = socket.request;
const getData = async () => { const handleGetData = async () => {
const room = await getRoom(req.session.roomId); const room = await getRoom(req.session.roomId);
// Handle the situation when the server crashes and any player reconnects after the time has expired // 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. // Typically, the responsibility for changing players is managed by gameHandler.js.
@ -15,23 +15,17 @@ module.exports = socket => {
sendToOnePlayerData(socket.id, room); sendToOnePlayerData(socket.id, room);
}; };
const getAllRooms = async () => { const handleGetAllRooms = async () => {
let rooms = await getRooms(); let rooms = await getRooms();
const response = []; sendToOnePlayerRooms(socket.id, rooms);
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);
}; };
socket.on('room:data', getData); const handleCreateRoom = async data => {
socket.on('room:rooms', getAllRooms); createNewRoom(data);
socket.to(socket.id).emit('room:created');
};
socket.on('room:data', handleGetData);
socket.on('room:rooms', handleGetAllRooms);
socket.on('room:create', handleCreateRoom);
}; };

View File

@ -7,7 +7,7 @@ const PlayerSchema = require('./player');
const RoomSchema = new mongoose.Schema({ const RoomSchema = new mongoose.Schema({
name: String, name: String,
private: { type: Boolean, default: true }, private: { type: Boolean, default: false },
password: String, password: String,
createDate: { type: Date, default: Date.now }, createDate: { type: Date, default: Date.now },
started: { type: Boolean, default: false }, started: { type: Boolean, default: false },

View File

@ -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;
}

View File

@ -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 (
<div className='lp-container'>
<div className='title-container'>
<h1>Host A Server</h1>
</div>
<div className='content-container'>
<form>
<input
type='text'
value={serverName}
onChange={e => setServerName(e.target.value)}
placeholder='Server Name'
/>
<div className='private-container'>
<p>Private</p>
<Switch checked={isPrivate} color='primary' onChange={() => setIsPrivate(!isPrivate)} />
</div>
<input
type='text'
value={password}
onChange={e => setPassword(e.target.value)}
placeholder='password'
disabled={!isPrivate}
/>
<button onClick={handleButtonClick}>Host</button>
</form>
</div>
</div>
);
};
export default AddServer;

View File

@ -1,76 +1,53 @@
.login-page-container { .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; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 50vh; width: 500px;
width: 400px;
position: relative;
padding: 20px; 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; color: white;
} }
.rooms {
width: 98%; .title-container {
height: 80%;
overflow-y: scroll;
overflow-x: hidden;
}
.room {
cursor: pointer;
justify-content: space-between;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center;
align-items: center; align-items: center;
color: white; width: 100%;
width: 90%; height: 40px;
margin: 10px;
padding: 10px;
border: 1px solid black;
}
.room-selected {
border: 1px solid white; 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 { .title-container > h1 {
background-color: rgba(0, 0, 0, 0.5); width: 100%;
margin: 0;
padding: 0;
} }
.number-of-players {
.content-container {
display: flex; display: flex;
flex-direction: row; flex-direction: column;
align-items: center; width: 100%;
} padding: 10px;
.number-of-players > img { background-color: rgba(0, 0, 0, 0.5);
margin-right: 5px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
width: 20px; border-left: 1px solid black;
height: 20px; border-right: 1px solid black;
} border-bottom: 1px solid black;
/* 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;
} }

View File

@ -1,54 +1,16 @@
import React, { useContext, useEffect, useState } from 'react';
import NameInput from './NameInput/NameInput';
import { SocketContext } from '../../App';
import './LoginPage.css'; 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 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 ( return (
<>
<div className='login-page-container'> <div className='login-page-container'>
<h1>Select room:</h1> <ServerList />
<div className='rooms'> <AddServer />
{rooms.map(room => {
return (
<div
className={selectedRoom && selectedRoom == room._id ? 'room-selected room' : 'room'}
onClick={() => {
if (selectedRoom && selectedRoom == room._id) {
setSelectedRoom(null);
} else {
setSelectedRoom(room._id);
}
}}
key={room.id}
>
<div>
<p>{room.name}</p>
{room.players.map(player => player.name + ' ')}
</div>
<div className='number-of-players'>
<img src={userImage} alt='' />
<span> {room.players.length}/4 </span>
</div>
</div>
);
})}
</div>
<NameInput />
</div> </div>
</>
); );
}; };
export default LoginPage; export default LoginPage;

View File

@ -1,11 +1,34 @@
.name-input-container { .name-input-container {
display: flex; display: flex;
position: absolute; flex-direction: column;
bottom: 0; padding: 10px 20px 60px 20px;
flex-direction: row; width: 300px;
width: 80%; 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; 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, input,
button { button {
padding: 0; padding: 0;
@ -21,7 +44,7 @@ input {
border-radius: 8px; border-radius: 8px;
color: white; color: white;
border: 1px solid #ccc; 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; transition: border-color 0.3s ease-in-out, background-color 0.3s ease-in-out;
} }
@ -37,7 +60,7 @@ button {
border-radius: 8px; border-radius: 8px;
border: none; border: none;
color: #fff; color: #fff;
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.4);
cursor: pointer; cursor: pointer;
transition: background-color 0.3s ease-in-out; transition: background-color 0.3s ease-in-out;
} }

View File

@ -1,20 +1,45 @@
import React, { useState, useContext } from 'react'; import React, { useState, useContext, useEffect } from 'react';
import { SocketContext } from '../../../App'; import { SocketContext } from '../../../App';
import './NameInput.css'; import './NameInput.css';
const NameInput = () => { const NameInput = ({ isRoomPrivate, roomId }) => {
const socket = useContext(SocketContext); const socket = useContext(SocketContext);
const [inputValue, setInputValue] = useState(''); const [nickname, setNickname] = useState('');
const handleInputChange = e => { const [password, setPassword] = useState('');
setInputValue(e.target.value); const [isPasswordWrong, setIsPasswordWrong] = useState(false);
};
const handleButtonClick = () => { 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 ( return (
<div className="name-input-container"> <div className='name-overlay'>
<input placeholder='Enter name' type='text' onChange={handleInputChange} /> <div className='name-input-container' style={{ height: isRoomPrivate ? '100px' : '50px' }}>
<input placeholder='Nickname' type='text' onChange={e => setNickname(e.target.value)} />
{isRoomPrivate ? (
<input
placeholder='Room password'
type='text'
onChange={e => setPassword(e.target.value)}
style={{ backgroundColor: isPasswordWrong ? 'red' : null }}
/>
) : null}
<button onClick={handleButtonClick}>JOIN</button> <button onClick={handleButtonClick}>JOIN</button>
</div> </div>
</div>
); );
}; };

View File

@ -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;
}

View File

@ -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 (
<div className='lp-container'>
<div className='title-container'>
<h1>Server List</h1>
<div className='refresh'>
<img src={refresh} onClick={getRooms}></img>
</div>
</div>
<div className='server-container content-container'>
{rooms ? (
<table className='rooms'>
<thead>
<tr>
<th></th>
<th>Server</th>
<th>#/#</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
{rooms.map((room, index) => (
<tr key={index}>
<td>{room.private ? <img src={lock} /> : null}</td>
<td className='room-name'>{room.name}</td>
<td>{`${room.players.length}/4`}</td>
<td>{room.isStarted ? 'started' : 'waiting'}</td>
<td>
<button onClick={() => handleJoinClick(room)}>Join</button>
</td>
</tr>
))}
</tbody>
</table>
) : (
<div style={{ alignSelf: 'center' }}>
<ReactLoading type='spinningBubbles' color='white' height={50} width={50} />
</div>
)}
</div>
{joining ? (
<NameInput roomId={clickedRoom._id} isRoomPrivate={clickedRoom.private} />
) : null}
</div>
);
};
export default ServerList;

View File

@ -14,7 +14,7 @@ const AnimatedOverlay = ({ time }) => {
in={true} in={true}
timeout={0} timeout={0}
classNames='overlay' classNames='overlay'
style={{ 'animation-delay': `-${animationDelay}s` }} style={{ animationDelay: `-${animationDelay}s` }}
unmountOnExit unmountOnExit
> >
<div className='overlay'></div> <div className='overlay'></div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB