refactored backend

This commit is contained in:
Wenszel 2023-11-25 19:30:07 +01:00
parent ffaaf0b6f3
commit a1b39a3a45
14 changed files with 231 additions and 163 deletions

View File

@ -0,0 +1,14 @@
const CONNECTION_URI = require('../credentials.js');
module.exports = function (mongoose) {
mongoose.set('useFindAndModify', false);
mongoose
.connect(CONNECTION_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log('MongoDB Connected…');
})
.catch(err => console.error(err));
};

26
backend/config/socket.js Normal file
View File

@ -0,0 +1,26 @@
const socketManager = require('../socket/socketManager');
const registerPlayerHandlers = require('../handlers/playerHandler');
const registerRoomHandlers = require('../handlers/roomHandler');
const registerGameHandlers = require('../handlers/gameHandler');
const { sessionMiddleware, wrap } = require('../config/session');
module.exports = function (server) {
socketManager.initialize(server);
socketManager.getIO().engine.on('initial_headers', (headers, req) => {
if (req.cookieHolder) {
headers['set-cookie'] = req.cookieHolder;
delete req.cookieHolder;
}
});
socketManager.getIO().use(wrap(sessionMiddleware));
socketManager.getIO().on('connection', socket => {
registerPlayerHandlers(socket);
registerRoomHandlers(socket);
registerGameHandlers(socket);
if (socket.request.session.roomId) {
const roomId = socket.request.session.roomId.toString();
socket.join(roomId);
socket.emit('player:data', JSON.stringify(socket.request.session));
}
});
};

View File

@ -0,0 +1,35 @@
const Room = require('../models/room');
const { sendToPlayersData } = require('../socket/emits');
const getRoom = async roomId => {
return await Room.findOne({ _id: roomId }).exec();
};
const getRooms = async () => {
return await Room.find().exec();
};
const updateRoom = async room => {
return await Room.findOneAndUpdate({ _id: room._id }, room).exec();
};
const getJoinableRoom = async () => {
return await Room.findOne({ full: false, started: false }).exec();
};
const createNewRoom = () => {
const room = new Room();
return room;
};
const findPlayer = async sessionID => {
const player = await Room.findOne({ 'players.sessionID': sessionID }).exec();
console.log(player);
return await Room.findOne({ 'players.sessionID': sessionID }).exec();
};
Room.watch().on('change', async data => {
sendToPlayersData(await getRoom(data.documentKey._id));
});
module.exports = { getRoom, getRooms, updateRoom, getJoinableRoom, createNewRoom, findPlayer };

View File

@ -1,86 +1,32 @@
const Room = require('../schemas/room'); const { getRoom, updateRoom } = require('../controllers/roomController');
const { sendToPlayersRolledNumber } = require('../socket/emits');
const { getPawnPositionAfterMove } = require('../utils/functions'); const { getPawnPositionAfterMove } = require('../utils/functions');
const { rollDice, isMoveValid } = require('./handlersFunctions');
module.exports = (io, socket) => { module.exports = socket => {
const req = socket.request; const req = socket.request;
const handleMovePawn = async pawnId => { const handleMovePawn = async pawnId => {
const room = await getRoom(); const room = await getRoom(req.session.roomId);
const pawn = room.getPawn(pawnId); const pawn = room.getPawn(pawnId);
if (isMoveValid(pawn, room)) { if (isMoveValid(req.session, pawn, room)) {
const newPositionOfMovedPawn = getPawnPositionAfterMove(room.rolledNumber, pawn); const newPositionOfMovedPawn = getPawnPositionAfterMove(room.rolledNumber, pawn);
room.changePositionOfPawn(pawn, newPositionOfMovedPawn); room.changePositionOfPawn(pawn, newPositionOfMovedPawn);
room.beatPawns(newPositionOfMovedPawn, req.session.color); room.beatPawns(newPositionOfMovedPawn, req.session.color);
handleChangeOfPlayer(room); room.changeMovingPlayer();
await updateRoom(room);
} }
}; };
const handleRollDice = async () => { const handleRollDice = async () => {
const rolledNumber = rollDice(); const rolledNumber = rollDice();
const room = await updateRoom({ rolledNumber: rolledNumber }); sendToPlayersRolledNumber(req.session.roomId, rolledNumber);
if (!canPlayerMove(room, rolledNumber)) { const room = await updateRoom({ _id: req.session.roomId, rolledNumber: rolledNumber });
handleChangeOfPlayer(room); const player = room.getPlayer(req.session.playerId);
} if (!player.canMove(room, rolledNumber)) {
};
const rollDice = () => {
const rolledNumber = Math.ceil(Math.random() * 6);
sendToPlayersRolledNumber(rolledNumber);
return rolledNumber;
};
const canPlayerMove = (room, rolledNumber) => {
const playerPawns = room.getPlayerPawns(req.session.color);
for (const pawn of playerPawns) {
if (pawn.canMove(rolledNumber)) return true;
}
return false;
};
const isMoveValid = (pawn, room) => {
if (req.session.color !== pawn.color) {
return false;
}
if (req.session.playerId !== room.getCurrentlyMovingPlayer()._id.toString()) {
return false;
}
return true;
};
const handleChangeOfPlayer = async room => {
room.changeMovingPlayer(); room.changeMovingPlayer();
room.timeoutID = setTimeout(makeRandomMove, 15000, room);
await updateRoom(room); await updateRoom(room);
};
const makeRandomMove = async room => {
if (room.rolledNumber === null) room.rolledNumber = rollDice();
const pawnsThatCanMove = room.getPawnsThatCanMove()
if (pawnsThatCanMove.length > 0) {
const randomPawn = pawnsThatCanMove[Math.floor(Math.random() * pawnsThatCanMove.length)];
room.movePawn(randomPawn);
} }
await handleChangeOfPlayer(room);
};
Room.watch().on('change', async () => {
sendToPlayersData(await getRoom());
});
const getRoom = async () => {
return await Room.findOne({ _id: req.session.roomId }).exec();
};
const updateRoom = async room => {
return await Room.findOneAndUpdate({ _id: req.session.roomId }, room).exec();
};
const sendToPlayersRolledNumber = rolledNumber => {
io.to(req.session.roomId).emit('game:roll', rolledNumber);
};
const sendToPlayersData = room => {
io.to(req.session.roomId).emit('room:data', JSON.stringify(room));
}; };
socket.on('game:roll', handleRollDice); socket.on('game:roll', handleRollDice);

View File

@ -0,0 +1,35 @@
const { sendToPlayersRolledNumber } = require('../socket/emits');
const rollDice = () => {
const rolledNumber = Math.ceil(Math.random() * 6);
return rolledNumber;
};
const makeRandomMove = async roomId => {
const { updateRoom, getRoom } = require('../controllers/roomController');
const room = await getRoom(roomId);
if (room.rolledNumber === null) {
room.rolledNumber = rollDice();
sendToPlayersRolledNumber(room._id.toString(), room.rolledNumber);
}
const pawnsThatCanMove = room.getPawnsThatCanMove();
if (pawnsThatCanMove.length > 0) {
const randomPawn = pawnsThatCanMove[Math.floor(Math.random() * pawnsThatCanMove.length)];
room.movePawn(randomPawn);
}
room.changeMovingPlayer();
await updateRoom(room);
};
const isMoveValid = (session, pawn, room) => {
if (session.color !== pawn.color) {
return false;
}
if (session.playerId !== room.getCurrentlyMovingPlayer()._id.toString()) {
return false;
}
return true;
};
module.exports = { rollDice, makeRandomMove, isMoveValid };

View File

@ -1,32 +1,31 @@
const RoomModel = require('../schemas/room'); const { getRoom, updateRoom, getJoinableRoom, createNewRoom, findPlayer } = require('../controllers/roomController');
const { colors } = require('../utils/constants'); const { colors } = require('../utils/constants');
module.exports = (io, socket) => { module.exports = socket => {
const req = socket.request; const req = socket.request;
const handleLogin = async data => { const handleLogin = async data => {
const room = await RoomModel.findOne({ full: false, started: false }); if (await findPlayer(req.sessionID)) return;
const room = await getJoinableRoom();
if (room) { if (room) {
addPlayerToExistingRoom(room, data); addPlayerToExistingRoom(room, data);
} else { } else {
createNewRoom(data); addNewRoom(data);
} }
}; };
const handleReady = async () => { const handleReady = async () => {
const { roomId, playerId } = req.session; const room = await getRoom(req.session.roomId);
const room = await RoomModel.findOne({ _id: roomId }); room.getPlayer(req.session.playerId).changeReadyStatus();
room.getPlayer(playerId).changeReadyStatus();
if (room.canStartGame()) { if (room.canStartGame()) {
room.startGame(); room.startGame();
} }
await RoomModel.findOneAndUpdate({ _id: roomId }, room); await updateRoom(room);
io.to(roomId).emit('room:data', JSON.stringify(room));
}; };
const createNewRoom = async data => { const addNewRoom = async data => {
const room = new RoomModel(); const room = createNewRoom();
room.addPlayer(data.name); room.addPlayer(data.name, req.sessionID);
await room.save(); await room.save();
reloadSession(room); reloadSession(room);
}; };
@ -36,7 +35,7 @@ module.exports = (io, socket) => {
if (room.isFull()) { if (room.isFull()) {
room.startGame(); room.startGame();
} }
await RoomModel.findOneAndUpdate({ _id: room._id }, room); await updateRoom(room);
reloadSession(room); reloadSession(room);
}; };

View File

@ -1,39 +1,37 @@
const RoomModel = require('../schemas/room'); const { getRooms, getRoom, updateRoom } = require('../controllers/roomController');
const { sendToOnePlayerRooms, sendToOnePlayerData, sendToPlayersData } = require('../socket/emits');
module.exports = (io, socket) => { module.exports = socket => {
const req = socket.request; const req = socket.request;
const getData = async () => { const getData = async () => {
let room = await RoomModel.findOne({ _id: 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.
if (room.nextMoveTime <= Date.now()) { if (room.nextMoveTime <= Date.now()) {
room.changeMovingPlayer(); room.changeMovingPlayer();
await RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room); await updateRoom(room);
io.to(req.session.roomId).emit('room:data', JSON.stringify(room));
} else {
io.to(socket.id).emit('room:data', JSON.stringify(room));
} }
sendToOnePlayerData(socket.id, room);
}; };
const getRooms = async () => { const getAllRooms = async () => {
let rooms = await RoomModel.find({}); let rooms = await getRooms();
const response = []; const response = [];
rooms.forEach(room => { rooms.forEach(room => {
if (!room.isStarted && !room.isFull()) { if (!room.isStarted && !room.isFull()) {
response.push({ response.push({
_id: room._id, _id: room._id,
private: room.private,
name: room.name, name: room.name,
players: room.players, players: room.players,
isStarted: room.isStarted, isStarted: room.isStarted,
}); });
} }
}); });
io.to(socket.id).emit('room:rooms', JSON.stringify(response)); sendToOnePlayerRooms(socket.id, response);
}; };
socket.on('room:data', getData); socket.on('room:data', getData);
socket.on('room:rooms', getRooms); socket.on('room:rooms', getAllRooms);
}; };

View File

@ -3,6 +3,7 @@ const mongoose = require('mongoose');
const Schema = mongoose.Schema; const Schema = mongoose.Schema;
const PlayerSchema = new Schema({ const PlayerSchema = new Schema({
sessionID: String,
name: String, name: String,
color: String, color: String,
ready: { type: Boolean, default: false }, ready: { type: Boolean, default: false },
@ -13,4 +14,12 @@ PlayerSchema.methods.changeReadyStatus = function () {
this.ready = !this.ready; this.ready = !this.ready;
}; };
PlayerSchema.methods.canMove = function (room, rolledNumber) {
const playerPawns = room.getPlayerPawns(this.color);
for (const pawn of playerPawns) {
if (pawn.canMove(rolledNumber)) return true;
}
return false;
};
module.exports = PlayerSchema; module.exports = PlayerSchema;

View File

@ -1,12 +1,14 @@
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const { colors } = require('../utils/constants'); const { colors } = require('../utils/constants');
const { getPawnPositionAfterMove, getStartPositions } = require('../utils/functions'); const { getPawnPositionAfterMove, getStartPositions } = require('../utils/functions');
const Schema = mongoose.Schema; const { makeRandomMove } = require('../handlers/handlersFunctions');
const PawnSchema = require('./pawn'); const PawnSchema = require('./pawn');
const PlayerSchema = require('./player'); const PlayerSchema = require('./player');
const RoomSchema = new Schema({ const RoomSchema = new mongoose.Schema({
name: String, name: String,
private: { type: Boolean, default: true },
password: String,
createDate: { type: Date, default: Date.now }, createDate: { type: Date, default: Date.now },
started: { type: Boolean, default: false }, started: { type: Boolean, default: false },
full: { type: Boolean, default: false }, full: { type: Boolean, default: false },
@ -38,7 +40,7 @@ RoomSchema.methods.changeMovingPlayer = function () {
this.nextMoveTime = Date.now() + 15000; this.nextMoveTime = Date.now() + 15000;
this.rolledNumber = null; this.rolledNumber = null;
if (this.timeoutID) clearTimeout(this.timeoutID); if (this.timeoutID) clearTimeout(this.timeoutID);
this.timeoutID = null; this.timeoutID = setTimeout(makeRandomMove, 15000, this._id.toString());
}; };
RoomSchema.methods.movePawn = function (pawn) { RoomSchema.methods.movePawn = function (pawn) {
@ -67,7 +69,7 @@ RoomSchema.methods.startGame = function () {
this.nextMoveTime = Date.now() + 15000; this.nextMoveTime = Date.now() + 15000;
this.players.forEach(player => (player.ready = true)); this.players.forEach(player => (player.ready = true));
this.players[0].nowMoving = true; this.players[0].nowMoving = true;
this.timeoutID = setTimeout(makeRandomMove, 15000, this); this.timeoutID = setTimeout(makeRandomMove, 15000, this._id.toString());
}; };
RoomSchema.methods.isFull = function () { RoomSchema.methods.isFull = function () {
@ -81,9 +83,10 @@ RoomSchema.methods.getPlayer = function (playerId) {
return this.players.find(player => player._id.toString() === playerId.toString()); return this.players.find(player => player._id.toString() === playerId.toString());
}; };
RoomSchema.methods.addPlayer = function (name) { RoomSchema.methods.addPlayer = function (name, id) {
if (this.full) return; if (this.full) return;
this.players.push({ this.players.push({
sessionID: id,
name: name, name: name,
ready: false, ready: false,
color: colors[this.players.length], color: colors[this.players.length],
@ -106,6 +109,6 @@ RoomSchema.methods.getCurrentlyMovingPlayer = function () {
return this.players.find(player => player.nowMoving === true); return this.players.find(player => player.nowMoving === true);
}; };
const RoomModel = mongoose.model('Room', RoomSchema); const Room = mongoose.model('Room', RoomSchema);
module.exports = RoomModel; module.exports = Room;

View File

@ -1,13 +1,11 @@
const express = require('express'); const express = require('express');
const cors = require('cors'); const cors = require('cors');
const cookieParser = require('cookie-parser'); const cookieParser = require('cookie-parser');
const { sessionMiddleware, wrap } = require('./controllers/serverController');
const registerPlayerHandlers = require('./handlers/playerHandler');
const registerRoomHandlers = require('./handlers/roomHandler');
const registerGameHandlers = require('./handlers/gameHandler');
const PORT = 8080;
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const CONNECTION_URI = require('./credentials.js'); const { sessionMiddleware } = require('./config/session');
const PORT = 8080;
const app = express(); const app = express();
app.use(cookieParser()); app.use(cookieParser());
@ -26,63 +24,10 @@ app.use(
); );
app.use(sessionMiddleware); app.use(sessionMiddleware);
mongoose.set('useFindAndModify', false); const server = app.listen(PORT);
mongoose
.connect(CONNECTION_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log('MongoDB Connected…');
})
.catch(err => console.error(err));
const server = app.listen(PORT, () => { require('./config/database')(mongoose);
console.log('Server runs on port ' + PORT); require('./config/socket')(server);
});
const io = require('socket.io')(server, {
cors: {
origin: 'http://localhost:3000',
credentials: true,
},
allowRequest: (req, callback) => {
const fakeRes = {
getHeader() {
return [];
},
setHeader(key, values) {
req.cookieHolder = values[0];
},
writeHead() {},
};
sessionMiddleware(req, fakeRes, () => {
if (req.session) {
fakeRes.writeHead();
req.session.save();
}
callback(null, true);
});
},
});
io.engine.on('initial_headers', (headers, req) => {
if (req.cookieHolder) {
headers['set-cookie'] = req.cookieHolder;
delete req.cookieHolder;
}
});
io.use(wrap(sessionMiddleware));
io.on('connection', socket => {
registerPlayerHandlers(io, socket);
registerRoomHandlers(io, socket);
registerGameHandlers(io, socket);
if (socket.request.session.roomId) {
const roomId = socket.request.session.roomId.toString();
socket.join(roomId);
socket.emit('player:data', JSON.stringify(socket.request.session));
}
});
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
app.use(express.static('/app/build')); app.use(express.static('/app/build'));

19
backend/socket/emits.js Normal file
View File

@ -0,0 +1,19 @@
const socketManager = require('./socketManager');
const sendToPlayersRolledNumber = (id, rolledNumber) => {
socketManager.getIO().to(id).emit('game:roll', rolledNumber);
};
const sendToPlayersData = room => {
socketManager.getIO().to(room._id.toString()).emit('room:data', JSON.stringify(room));
};
const sendToOnePlayerData = (id, room) => {
socketManager.getIO().to(id).emit('room:data', JSON.stringify(room));
};
const sendToOnePlayerRooms = (id, rooms) => {
socketManager.getIO().to(id).emit('room:rooms', JSON.stringify(rooms));
};
module.exports = { sendToPlayersData, sendToPlayersRolledNumber, sendToOnePlayerData, sendToOnePlayerRooms };

View File

@ -0,0 +1,39 @@
const { sessionMiddleware } = require('../config/session');
const socketManager = {
io: null,
initialize(server) {
this.io = require('socket.io')(server, {
cors: {
origin: 'http://localhost:3000',
credentials: true,
},
allowRequest: (req, callback) => {
const fakeRes = {
getHeader() {
return [];
},
setHeader(key, values) {
req.cookieHolder = values[0];
},
writeHead() {},
};
sessionMiddleware(req, fakeRes, () => {
if (req.session) {
fakeRes.writeHead();
req.session.save();
}
callback(null, true);
});
},
});
},
getIO() {
if (!this.io) {
throw new Error('Socket.io not initialized');
}
return this.io;
},
};
module.exports = socketManager;