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 { rollDice, isMoveValid } = require('./handlersFunctions');
module.exports = (io, socket) => {
module.exports = socket => {
const req = socket.request;
const handleMovePawn = async pawnId => {
const room = await getRoom();
const room = await getRoom(req.session.roomId);
const pawn = room.getPawn(pawnId);
if (isMoveValid(pawn, room)) {
if (isMoveValid(req.session, pawn, room)) {
const newPositionOfMovedPawn = getPawnPositionAfterMove(room.rolledNumber, pawn);
room.changePositionOfPawn(pawn, newPositionOfMovedPawn);
room.beatPawns(newPositionOfMovedPawn, req.session.color);
handleChangeOfPlayer(room);
room.changeMovingPlayer();
await updateRoom(room);
}
};
const handleRollDice = async () => {
const rolledNumber = rollDice();
const room = await updateRoom({ rolledNumber: rolledNumber });
if (!canPlayerMove(room, rolledNumber)) {
handleChangeOfPlayer(room);
}
};
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 => {
sendToPlayersRolledNumber(req.session.roomId, rolledNumber);
const room = await updateRoom({ _id: req.session.roomId, rolledNumber: rolledNumber });
const player = room.getPlayer(req.session.playerId);
if (!player.canMove(room, rolledNumber)) {
room.changeMovingPlayer();
room.timeoutID = setTimeout(makeRandomMove, 15000, 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);

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');
module.exports = (io, socket) => {
module.exports = socket => {
const req = socket.request;
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) {
addPlayerToExistingRoom(room, data);
} else {
createNewRoom(data);
addNewRoom(data);
}
};
const handleReady = async () => {
const { roomId, playerId } = req.session;
const room = await RoomModel.findOne({ _id: roomId });
room.getPlayer(playerId).changeReadyStatus();
const room = await getRoom(req.session.roomId);
room.getPlayer(req.session.playerId).changeReadyStatus();
if (room.canStartGame()) {
room.startGame();
}
await RoomModel.findOneAndUpdate({ _id: roomId }, room);
io.to(roomId).emit('room:data', JSON.stringify(room));
await updateRoom(room);
};
const createNewRoom = async data => {
const room = new RoomModel();
room.addPlayer(data.name);
const addNewRoom = async data => {
const room = createNewRoom();
room.addPlayer(data.name, req.sessionID);
await room.save();
reloadSession(room);
};
@ -36,7 +35,7 @@ module.exports = (io, socket) => {
if (room.isFull()) {
room.startGame();
}
await RoomModel.findOneAndUpdate({ _id: room._id }, room);
await updateRoom(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 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
// Typically, the responsibility for changing players is managed by gameHandler.js.
if (room.nextMoveTime <= Date.now()) {
room.changeMovingPlayer();
await RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room);
io.to(req.session.roomId).emit('room:data', JSON.stringify(room));
} else {
io.to(socket.id).emit('room:data', JSON.stringify(room));
await updateRoom(room);
}
sendToOnePlayerData(socket.id, room);
};
const getRooms = async () => {
let rooms = await RoomModel.find({});
const getAllRooms = 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,
});
}
});
io.to(socket.id).emit('room:rooms', JSON.stringify(response));
sendToOnePlayerRooms(socket.id, response);
};
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 PlayerSchema = new Schema({
sessionID: String,
name: String,
color: String,
ready: { type: Boolean, default: false },
@ -13,4 +14,12 @@ PlayerSchema.methods.changeReadyStatus = function () {
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;

View File

@ -1,12 +1,14 @@
const mongoose = require('mongoose');
const { colors } = require('../utils/constants');
const { getPawnPositionAfterMove, getStartPositions } = require('../utils/functions');
const Schema = mongoose.Schema;
const { makeRandomMove } = require('../handlers/handlersFunctions');
const PawnSchema = require('./pawn');
const PlayerSchema = require('./player');
const RoomSchema = new Schema({
const RoomSchema = new mongoose.Schema({
name: String,
private: { type: Boolean, default: true },
password: String,
createDate: { type: Date, default: Date.now },
started: { type: Boolean, default: false },
full: { type: Boolean, default: false },
@ -38,7 +40,7 @@ RoomSchema.methods.changeMovingPlayer = function () {
this.nextMoveTime = Date.now() + 15000;
this.rolledNumber = null;
if (this.timeoutID) clearTimeout(this.timeoutID);
this.timeoutID = null;
this.timeoutID = setTimeout(makeRandomMove, 15000, this._id.toString());
};
RoomSchema.methods.movePawn = function (pawn) {
@ -67,7 +69,7 @@ RoomSchema.methods.startGame = function () {
this.nextMoveTime = Date.now() + 15000;
this.players.forEach(player => (player.ready = true));
this.players[0].nowMoving = true;
this.timeoutID = setTimeout(makeRandomMove, 15000, this);
this.timeoutID = setTimeout(makeRandomMove, 15000, this._id.toString());
};
RoomSchema.methods.isFull = function () {
@ -81,9 +83,10 @@ RoomSchema.methods.getPlayer = function (playerId) {
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;
this.players.push({
sessionID: id,
name: name,
ready: false,
color: colors[this.players.length],
@ -106,6 +109,6 @@ RoomSchema.methods.getCurrentlyMovingPlayer = function () {
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 cors = require('cors');
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 CONNECTION_URI = require('./credentials.js');
const { sessionMiddleware } = require('./config/session');
const PORT = 8080;
const app = express();
app.use(cookieParser());
@ -26,63 +24,10 @@ app.use(
);
app.use(sessionMiddleware);
mongoose.set('useFindAndModify', false);
mongoose
.connect(CONNECTION_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log('MongoDB Connected…');
})
.catch(err => console.error(err));
const server = app.listen(PORT);
const server = app.listen(PORT, () => {
console.log('Server runs on port ' + PORT);
});
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));
}
});
require('./config/database')(mongoose);
require('./config/socket')(server);
if (process.env.NODE_ENV === 'production') {
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;