public string JoinGame(JoinRequest joinRequest) { lock (sync) { string userToken = joinRequest.UserToken; int timeLimit = joinRequest.TimeLimit; //A user token is valid if it is non - null and identifies a user. Time must be between 5 and 120. if (userToken == null || !users.ContainsKey(userToken) || timeLimit < 5 || timeLimit > 120) { SetStatus(Forbidden); return(null); } // Check if user is already in pending game. else if (games[gameID].Player1Token == userToken || games[gameID].Player2Token == userToken) { SetStatus(Conflict); return(null); } // If player 1 is taken, user is player 2 if (games[gameID].Player1Token != null && games[gameID].Player2Token == null) { string GameID = gameID.ToString(); games[gameID].Player2Token = userToken; StartPendingGame(timeLimit); SetStatus(Created); return(GameID); } // if player 2 is taken, user is player 1 else if (games[gameID].Player2Token != null && games[gameID].Player1Token == null) { string GameID = gameID.ToString(); games[gameID].Player1Token = userToken; StartPendingGame(timeLimit); SetStatus(Created); return(GameID); } // if user is first to enter pending game else { string GameID = gameID.ToString(); games[gameID].Player1Token = userToken; games[gameID].TimeLimit = timeLimit; SetStatus(Accepted); return(GameID); } } }
/// <summary> /// Attempts to join a game with user userToken and timeLimit /// /// If UserToken is invalid, TimeLimit is less than 5, or TimeLimit is over 120, responds /// with status 403 (Forbidden). /// /// Otherwise, if UserToken is already a player in the pending game, responds with status /// 409 (Conflict). /// /// Otherwise, if there is already one player in the pending game, adds UserToken as the /// second player. The pending game becomes active. The active game's time limit is the integer /// average of the time limits requested by the two players. Returns the new active game's /// GameID (which should be the same as the old pending game's GameID). Responds with /// status 201 (Created). /// /// Otherwise, adds UserToken as the first player of a new pending game, and the TimeLimit as /// the pending game's requested time limit. Returns the pending game's GameID. Responds with /// status 202 (Accepted). /// </summary> public GameIDResponse JoinGame(JoinRequest request, out HttpStatusCode status) { int timeLimit = request.TimeLimit; // make sure TimeLimit is within the expected bounds if (!(timeLimit >= 5 && timeLimit <= 120)) { status = Forbidden; return(null); } //null checks if (request is null || request.UserToken is null) { status = Forbidden; return(null); } // the UserID that'll be used throughout this method string userID = request.UserToken.Trim(); // make sure UserToken is within the expected bounds if (userID == null || userID.Length == 0 || userID.Length > 36) { status = Forbidden; return(null); } // open connection to database using (SqlConnection connection = new SqlConnection(BoggleDB)) { connection.Open(); // execute all commands within a single transaction using (SqlTransaction transaction = connection.BeginTransaction()) { // todo: make sure GameID starts at 1, not 0 // (otherwise, this should be changed, because this might actually end up as 0 when creating a game) int gameID = -1; // find out whether there's a pending game using (SqlCommand command = new SqlCommand("select GameID, Player1, TimeLimit from Games where Player2 is null", connection, transaction)) { // the reader that will determine whether there's a pending game, and if so, what its gameID is using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { gameID = (int)reader["GameID"]; string player1 = (string)reader["Player1"]; // averages the time between player1's requested time and this user's requested time int player1RequestedTime = (int)reader["TimeLimit"]; timeLimit = (timeLimit + player1RequestedTime) / 2; // player is already searching for a game if (player1 == userID) { status = Conflict; reader.Close(); transaction.Commit(); return(null); } } } } if (gameID == -1) { using (SqlCommand command = new SqlCommand("insert into Games (Player1, TimeLimit) output inserted.GameID values(@Player1, @TimeLimit)", connection, transaction)) { command.Parameters.AddWithValue("@Player1", userID); command.Parameters.AddWithValue("@TimeLimit", timeLimit); SqlParameter outputparam = new SqlParameter("@GameID", SqlDbType.Int) { Direction = ParameterDirection.Output }; command.Parameters.Add(outputparam); // execute command, and get back the primary key (GameID) try { gameID = (int)command.ExecuteScalar(); } //If an exception was thrown, a foreign key pair was violated catch (SqlException) { status = Forbidden; transaction.Commit(); return(null); } status = Accepted; } } else { using (SqlCommand command = new SqlCommand("update Games set Player2 = @Player2, Board = @Board, TimeLimit = @TimeLimit, StartTime = @StartTime where GameID = @GameID", connection, transaction)) { command.Parameters.AddWithValue("@Player2", userID); command.Parameters.AddWithValue("@Board", StaticBoard.GenerateBoggleBoard()); command.Parameters.AddWithValue("@TimeLimit", timeLimit); command.Parameters.AddWithValue("@StartTime", DateTime.UtcNow); command.Parameters.AddWithValue("@GameID", gameID); try { if (command.ExecuteNonQuery() != 1) { throw new Exception("SQL query failed"); } } //If Player2 is not a registered user catch (SqlException) { status = Forbidden; transaction.Commit(); return(null); } status = Created; } } transaction.Commit(); return(new GameIDResponse { GameID = gameID }); } } }
public gameID Join(JoinRequest Request) { if (Request.UserToken == null || Request.TimeLimit < 5 || Request.TimeLimit > 120) { SetStatus(Forbidden); return(null); } using (SqlConnection conn = new SqlConnection(BoggleDB)) { conn.Open(); using (SqlTransaction trans = conn.BeginTransaction()) { //Checking if UserToken exists in Users table. using (SqlCommand command = new SqlCommand("select UserID from Users where UserID = @UserID", conn, trans)) { command.Parameters.AddWithValue("@UserID", Request.UserToken); using (SqlDataReader reader = command.ExecuteReader()) { if (!reader.HasRows) { SetStatus(Forbidden); trans.Commit(); return(null); } } } bool player1 = false; int oldTime = 0; //Checking if the UserToken is already in the pending game. using (SqlCommand command = new SqlCommand("select Player1, TimeLimit from Games where Player1 is not null and Player2 is null", conn, trans)) { using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { if ((string)reader["Player1"] == Request.UserToken) { SetStatus(Conflict); trans.Commit(); return(null); } if ((int?)reader["TimeLimit"] != null) { player1 = true; oldTime = (int)reader["TimeLimit"]; } } } } gameID ID = new gameID(); if (player1) { using (SqlCommand command = new SqlCommand("update Games set Player2 = @UserID, Board = @Board, TimeLimit = @TimeLimit, StartTime = @StartTime output inserted.GameID where Player2 is null", conn, trans)) { command.Parameters.AddWithValue("@UserID", Request.UserToken); command.Parameters.AddWithValue("@Board", new BoggleBoard().ToString()); command.Parameters.AddWithValue("@TimeLimit", (Request.TimeLimit + oldTime) / 2); command.Parameters.AddWithValue("@StartTime", DateTime.Now); ID.GameID = command.ExecuteScalar().ToString(); SetStatus(Created); trans.Commit(); return(ID); } } else { using (SqlCommand command = new SqlCommand("insert into Games (Player1, TimeLimit) output inserted.GameID values (@UserID, @TimeLimit)", conn, trans)) { command.Parameters.AddWithValue("@UserID", Request.UserToken); command.Parameters.AddWithValue("@TimeLimit", Request.TimeLimit); ID.GameID = command.ExecuteScalar().ToString(); SetStatus(Accepted); trans.Commit(); return(ID); } } } } }