Skip to content
Snippets Groups Projects
Select Git revision
  • master
  • dev default
  • 101-make-font-scale-based-on-device-width
  • 77-static-loading-screen
4 results

tournament.js

Blame
  • tournament.js 20.41 KiB
    const express = require("express");
    const router = express.Router();
    const mongo = require("mongodb");
    const MongoClient = mongo.MongoClient;
    const functions = require("./functions");
    const connectionUrl = process.env.MONGO_CONNECTION_STRING;
    
    router.post("/leave", (req, res) => {
      // Removes the player from the tournament
      MongoClient.connect(
        connectionUrl,
        { useNewUrlParser: true, useUnifiedTopology: true },
        (err, client) => {
          // Unable to connect to database
          if (err) {
            res.sendStatus(500); // Internal server error
            return;
          }
    
          let tournamentid, playerid;
    
          try {
            tournamentid = mongo.ObjectID(req.body.tournamentid);
            playerid = mongo.ObjectID(req.body.playerid);
          } catch (err) {
            res.status(400).send("Invalid IDs");
            return;
          }
          if (tournamentid == null || playerid == null) {
            res.status(400).send("Missing fields");
            return;
          }
    
          const db = client.db("gameWare");
          const collection = "tournaments";
    
          db.collection(collection)
            .findOneAndUpdate(
              { _id: tournamentid, players: playerid },
              // removes the player from the current players, decrements amount of players
              {
                $pull: { players: playerid },
                $inc: { currentPlayers: -1 },
              },
              {
                returnOriginal: false,
              }
            )
            .then((updatedDocument) => {
              if (updatedDocument.value.active === true) {
                // If the tournament is active, we want all of the players rounds to be removed
                // Finally we check to see if the tournament is left with zero players, which will delete it
                db.collection("rounds")
                  .deleteMany({
                    tournamentId: tournamentid,
                    playerId: playerid,
                  })
                  .then((result) => {
                    if (parseInt(updatedDocument.value.currentPlayers) === 0) {
                      db.collection("tournaments")
                        .deleteOne({
                          _id: tournamentid,
                        })
                        .then(() => {
                          res.json({ deletedRounds: result.result.n });
                          client.close();
                          return;
                        });
                    } else {
                      res.json({ deletedRounds: result.result.n });
                      client.close();
                      return;
                    }
                  })
                  .catch((err) => {
                    console.log(err);
                    res.sendStatus(500);
                  });
              } else {
                res.json({ deletedRounds: 0 });
                client.close();
                return;
              }
            });
        }
      );
    });
    
    router.post("/new", (req, res) => {
      // Creates a new tournament
      MongoClient.connect(
        connectionUrl,
        { useNewUrlParser: true, useUnifiedTopology: true },
        (err, client) => {
          // Unable to connect to database
          if (err) {
            res.sendStatus(500); // Internal server error
            return;
          }
    
          if (
            !req.body.games ||
            !req.body.startDelay ||
            !req.body.playerId ||
            !req.body.name ||
            !req.body.timePerRound ||
            !req.body.maxPlayers ||
            !req.body.roundsPerGame
          ) {
            res.status(400).send("Missing fields");
            return;
          }
    
          const db = client.db("gameWare");
          const collection = "tournaments";
    
          const tournamentdate = new Date();
    
          const values = {};
    
          try {
            // As the games field is a string, we split it up to get an array of IDs
            const games = req.body.games
              .substring(1, req.body.games.length - 1)
              .replace(/\s/g, "")
              .split(",");
            for (let i = 0; i < games.length; i++) {
              games[i] = mongo.ObjectID(games[i]);
            }
            // Startime is the time for when the tournament proceeds with players less than max, but over two
            let startTime = new Date(
              tournamentdate.getTime() +
                parseInt(req.body.startDelay) * 24 * 60 * 60 * 1000
            );
            values.players = [mongo.ObjectID(req.body.playerId)];
            values.games = games;
            (values.name = req.body.name),
              (values.timePerRound = parseInt(req.body.timePerRound)),
              (values.maxPlayers = parseInt(req.body.maxPlayers)),
              (values.roundsPerGame = parseInt(req.body.roundsPerGame)),
              (values.currentRound = 1),
              (values.dateCreated = tournamentdate),
              (values.active = true),
              (values.totalGames = games.length * parseInt(req.body.roundsPerGame)),
              (values.currentPlayers = 1),
              (values.startTime = startTime);
          } catch (err) {
            res.status(400).send("Invalid fields!");
            return;
          }
    
          db.collection(collection).insertOne(values, (err, result) => {
            if (err) {
              res.sendStatus(500); // Internal server error
              return;
            }
            tournamentdate.setTime(
              tournamentdate.getTime() + req.body.timePerRound * 24 * 60 * 60 * 1000
            );
            // Creates the first round
            db.collection("rounds").insertOne(
              {
                tournamentId: mongo.ObjectID(result.insertedId),
                playerId: mongo.ObjectID(req.body.playerId),
                gameId: values.games[0],
                scoreValue: 0,
                roundNr: 1,
                hasPlayed: false,
                deadlineDate: tournamentdate,
                tournamentPoints: 0,
              },
              (err) => {
                if (err) {
                  res.sendStatus(500); // Internal server error
                  return;
                }
                // returns the tournament
                res.json(result.ops[0]);
                client.close();
              }
            );
          });
        }
      );
    });
    
    router.post("/join", (req, res) => {
      // Joins the oldest tournament on round 1 for a player
      MongoClient.connect(
        connectionUrl,
        { useNewUrlParser: true, useUnifiedTopology: true },
        (err, client) => {
          // Unable to connect to database
          if (err) {
            res.sendStatus(500); // Internal server error
            return;
          }
    
          if (!req.body.playerId) {
            res.sendStatus(400);
            return;
          }
          let playerId;
          try {
            playerId = mongo.ObjectID(req.body.playerId);
          } catch (err) {
            res.sendStatus(400);
            return;
          }
    
          const db = client.db("gameWare");
          const collection = "tournaments";
    
          db.collection(collection)
            .findOneAndUpdate(
              {
                // the length of the array of players must be less than the maximum amount
                $expr: { $gt: ["$maxPlayers", "$currentPlayers"] },
                currentRound: 1,
                active: true,
                // the array of players cannot already contain the player
                players: { $nin: [playerId] },
                // we dont want tournaments that have gone past their starttime
                startTime: { $gte: new Date() },
              },
              // Add the player to the array
              {
                $push: { players: playerId },
                $inc: { currentPlayers: 1 },
              },
              {
                // make sure we join the oldest tournament that matches our query.
                sort: { dateCreated: 1 },
                // the returned document should be the modified version
                returnOriginal: false,
              }
            )
            .then((updatedDocument) => {
              if (updatedDocument.value) {
                try {
                  tournamentdate = updatedDocument.value.dateCreated;
                  tournamentdate.setTime(
                    tournamentdate.getTime() +
                      updatedDocument.value.timePerRound * 24 * 60 * 60 * 1000
                  );
                  let tournyid = updatedDocument.value._id;
                  // create the first round for the newly joined player
                  db.collection("rounds").insertOne(
                    {
                      tournamentId: mongo.ObjectID(tournyid),
                      playerId: playerId,
                      gameId: mongo.ObjectID(updatedDocument.value.games[0]),
                      scoreValue: 0,
                      roundNr: 1,
                      hasPlayed: false,
                      deadlineDate: tournamentdate,
                      tournamentPoints: 0,
                    },
                    (err, result) => {
                      if (err) {
                        res.sendStatus(500); // Internal server error
                        return;
                      }
                      res.json(updatedDocument.value);
                    }
                  );
                } catch (err) {
                  res.sendStatus(500); // Server error
                }
              } else {
                res.status(404).send("No available tournaments");
              }
            });
        }
      );
    });
    
    router.get("/playeractive/:userid", (req, res) => {
      //remove method
      // Connect to database
      MongoClient.connect(
        connectionUrl,
        { useNewUrlParser: true, useUnifiedTopology: true },
        (err, client) => {
          // Unable to connect to database
          if (err) {
            res.sendStatus(500); // Internal server error
            return;
          }
    
          // Using the database gameWare and collection tournaments
          const db = client.db("gameWare");
          const collection = "tournaments";
    
          const id = mongo.ObjectId(req.params.userid);
    
          db.collection(collection)
            .find({
              _id: id,
              active: true,
            })
            .toArray((err, result) => {
              if (err) {
                res.sendStatus(500);
                return;
              }
              res.json(result);
              client.close();
            });
        }
      );
    });
    
    router.get("/specific/:id", (req, res) => {
      MongoClient.connect(
        connectionUrl,
        { useNewUrlParser: true, useUnifiedTopology: true },
        (err, client) => {
          // Unable to connect to database
          if (err) {
            res.sendStatus(500); // Internal server error
            return;
          }
          let id;
          if (!req.params.id) {
            res.sendStatus(400);
          }
          try {
            id = mongo.ObjectID(req.params.id);
          } catch (err) {
            res.sendStatus(400);
          }
          // Using the database gameWare and collection tournaments
          const db = client.db("gameWare");
          const collection = "tournaments";
    
          db.collection(collection)
            .find({
              _id: id,
            })
            .toArray((err, result) => {
              if (err) {
                res.sendStatus(500);
                client.close();
                return;
              }
              res.json(result[0]);
              client.close();
            });
        }
      );
    });
    
    router.get("/player/:userid/:active", (req, res) => {
      // Returns the tournaments of player with active field matching parameter
      // Also checks the state of all tournaments
      MongoClient.connect(
        connectionUrl,
        { useNewUrlParser: true, useUnifiedTopology: true },
        (err, client) => {
          // Unable to connect to database
          if (err) {
            res.sendStatus(500); // Internal server error
            return;
          }
    
          // Using the database gameWare and collection tournaments
          const db = client.db("gameWare");
          const collection = "tournaments";
    
          let id;
    
          try {
            id = mongo.ObjectId(req.params.userid);
          } catch {
            res.status(500).send("Invalid ID");
          }
          if (!id) {
            res.status(500).send("Missing ID");
          }
    
          db.collection(collection)
            .find({
              players: id,
              active: req.params.active === "true",
            })
            .toArray((err, result) => {
              if (err) {
                res.sendStatus(500);
                return;
              }
              if (req.params.active === "true") {
                // Checks the tournament states if want the active tournaments
                let IDs = result.map((tourny) => tourny._id); // We need the IDs
                doChecks(client, IDs).then(() => {
                  db.collection(collection)
                    .find({
                      // Now we fetch the tournaments again, as the state might have updated in the doChecks part
                      players: id,
                      active: req.params.active === "true",
                    })
                    .toArray((err, result) => {
                      if (err) {
                        res.sendStatus(500);
                        //console.log(err);
                        return;
                      }
                      res.json(result);
                      client.close();
                      return;
                    });
                });
              } else {
                res.json(result);
                client.close();
              }
            });
        }
      );
    });
    
    async function doChecks(client, ids) {
      // Calls the roundcheck method on all tournaments
      for (const tourny of ids) {
        let id = mongo.ObjectID(tourny);
        const check = await roundcheck(client, id);
      }
      return;
    }
    
    router.put("/roundcheck/:tournamentId", (req, res) => {
      // Checks the state of the tournament, involves checking if players have been timed out,
      // if tournament can move on, and if the tournament is over.
      let tournament = null;
      try {
        tournament = mongo.ObjectID(req.params.tournamentId);
      } catch (error) {
        res.status(400).send("No document with specified id was found");
        return;
      }
      if (!tournament) {
        res.status(400).send("Request body not valid");
        return;
      }
    
      MongoClient.connect(
        connectionUrl,
        { useNewUrlParser: true, useUnifiedTopology: true },
        (err, client) => {
          // Unable to connect to database
          if (err) {
            res.sendStatus(500); // Internal server error
            return;
          }
          roundcheck(client, tournament).then((result) => {
            if (result == 500) {
              res.sendStatus(500);
              return;
            } else if (result == 400) {
              res.sendStatus(400);
              return;
            }
            res.json(result);
            client.close();
            return;
          });
        }
      );
    });
    
    function roundcheck(client, tournament) {
      return new Promise((resolve, reject) => {
        functions.getCurrentRound(client, tournament).then((roundResult) => {
          // gets the current round of the tournament
          if (roundResult == 500) {
            resolve(500);
          } else if (roundResult.active === false) {
            resolve(400);
          }
          // saves information we want
          const active = roundResult.currentRound;
          const start = roundResult.startTime;
          const maxPlayers = roundResult.maxPlayers;
          const currentPlayers = roundResult.currentPlayers;
          const name = roundResult.name;
    
          functions.fetchRounds(client, tournament, active).then((rounds) => {
            // fetched the round objects of the active round
            functions
              .checkRounds(rounds, start, maxPlayers, active, currentPlayers)
              .then((resultarray) => {
                // check to find out if [0] tournament will move on, [1] which players are timed out
                // [2] which players are still in the tournament, also fetched the rounds of the timed out players [3]
                let result = resultarray[0];
                let timedOut = resultarray[1];
                let left = resultarray[2];
                let timedOutRounds = resultarray[3];
                let end = false;
    
                if ((left.length < 2 && active != 1) || left.length == 0) {
                  end = true;
                  //if there are less then two people left in the first round, or zero people left in the first round
                  //the tournament ends
                }
                if (rounds.length > 0) {
                  // If there is only one player in the tournaments first round and the starttime
                  // has expired, we end the tournament
                  if (
                    left.length < 2 &&
                    active == 1 &&
                    new Date(start) < new Date()
                  ) {
                    end = true;
                  }
                }
                functions
                  .timeOut(client, tournament, timedOut, timedOutRounds)
                  .then(
                    //updates the state of the tournament and ends it if necessary
    
                    () => {
                      functions
                        .notifyPlayers(client, timedOut, true, tournament, name)
                        .then(() => {
                          if (end) {
                            functions
                              .notifyPlayers(client, left, false, tournament, name)
                              .then(() => {
                                functions
                                  .endTournament(client, tournament)
                                  .then(() => {
                                    resolve({
                                      active: false,
                                      nextRound: false,
                                    });
                                  });
                              });
                          } else if (result) {
                            //Tournament round is complete
    
                            let eligibleRounds = [];
                            for (let i = 0; i < rounds.length; i++) {
                              let round = rounds[i];
                              if (round.hasPlayed === true) {
                                eligibleRounds.push(round);
                              }
                            } //Here we find the rounds that were actually completed
    
                            for (let i = 0; i < eligibleRounds.length; i++) {
                              let round = eligibleRounds[i];
                              functions.updateScore(
                                client,
                                round._id,
                                i + 1,
                                eligibleRounds.length
                              );
                              //And here we update the tournament score for those rounds
                            }
                            functions
                              .updateTournamentRound(client, tournament)
                              .then((result) => {
                                //updates the tournaments round if there are more rounds left
                                if (result.value) {
                                  // If the tournament has more rounds left
                                  tourny = result.value;
                                  const game =
                                    tourny.games[
                                      (tourny.currentRound - 1) %
                                        tourny.games.length
                                    ];
                                  const date = new Date();
                                  date.setTime(
                                    date.getTime() +
                                      tourny.timePerRound * 24 * 60 * 60 * 1000
                                  );
    
                                  functions
                                    .newRound(
                                      client,
                                      tournament,
                                      tourny.players,
                                      game,
                                      tourny.currentRound,
                                      date
                                    )
                                    .then(() => {
                                      resolve({
                                        active: true,
                                        nextRound: true,
                                      });
                                    });
                                  // created new rounds for all the players left
                                } else {
                                  // if the tournament now is done we simply end it
                                  functions
                                    .notifyPlayers(
                                      // alerts the players that the tournament is done
                                      client,
                                      left,
                                      false,
                                      tournament,
                                      name
                                    )
                                    .then(() => {
                                      functions
                                        .endTournament(client, tournament)
                                        .then(() => {
                                          resolve({
                                            active: false,
                                            nextRound: false,
                                          });
                                        });
                                    });
                                }
                              });
                          } else {
                            resolve({
                              active: true,
                              nextRound: false,
                            }); // Tournament still waiting for other players
                          }
                        });
                    }
                  );
              });
          });
        });
      });
    }
    
    module.exports = router;