/// <summary> /// Send a web brower and html page stating all the current game statistics of all games ever played. /// <param name="state">The state obj containing the callback to end the socket.</param> /// </summary> private static void SendScores(PreservedState state) { // Begin generating the information for the html page. String html = "<h1>Scores</h1><table border = 1><tr><td><b>Player</b></td><td><b>Max Mass</b></td><td><b>Rank</b></td><td><b>Cubes Eaten</b></td><td><b>Time Alive (MM:SS)</b></td></tr>"; // Access our database using (MySqlConnection conn = new MySqlConnection(DbConnectionString)) { try { conn.Open(); MySqlCommand command = conn.CreateCommand(); // Generate teh command of what information we want from the database. command.CommandText = "select Name, Maximum_Mass, Rank, Number_Of_Cubes_Eaten, Lifespan from Player_Games"; // Retrive the information from the database and attach it to the string going to the web browser. using (MySqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { html += "<tr><td><a href = \"http://localhost:11100/games?player=" + reader["Name"] + "\">" + reader["Name"] + "</a>" + "</td><td>" + reader["Maximum_Mass"] + "</td><td>" + reader["Rank"] + "</td><td>" + reader["Number_Of_Cubes_Eaten"] + "</td><td>" + ToDateFormat(reader["Lifespan"].ToString()) + "</td></tr>"; } html += "</table>"; } } // Send the error html page if there is an issue. catch (Exception e) { SendErrorMessage(state); } } // Send the information to the webpage and close the socket. Network.Send(state.socket, HttpResponseHeader); Network.Send(state.socket, html, (Object) => { Console.WriteLine("Sent HTML"); state.socket.Close(); return; }); }
/// <summary> /// Callback for receiving client name. /// </summary> /// <param name="ss">The ss.</param> private static void ReceiveName(SocketState ss) { // Extract name from received data string name = ss.Builder.ToString(); name = name.Remove(name.Length - 1); Console.WriteLine("Received name: " + name); Network.Send(ss.Socket, ss.ID + "\n" + world.WorldSize + "\n"); clients.Add(ss); Ship newShip = new Ship(ss.ID, name, random.Next(universeSize + 1) - (universeSize / 2), random.Next(universeSize + 1) - (universeSize / 2)); // Don't start ship in star's location while (Math.Abs(newShip.Loc.GetX()) < starSize && Math.Abs(newShip.Loc.GetX()) < starSize) { newShip.Loc = new Vector2D(random.Next(universeSize + 1) - (universeSize / 2), random.Next(universeSize + 1) - (universeSize / 2)); } world.AddShip(newShip); ss.CallMe = ReceiveMoveRequest; Network.GetData(ss); }
/// <summary> /// This is a delegate callback that handles the server's side of the initial handshake. /// It receives the player's name and sends the client startup data, then requests direciton /// change information from the client. /// </summary> /// <param name="client">The client.</param> private void ReceivePlayerName(SocketState client) { // Get player's name. string playerName = client.Messages[0]; // Create snake object corresponding to new player. CreateNewPlayerSnake(playerName); // Change callback to method that handles direction change requests. client.Callback = ReceiveDirection; // Store the assigned ID into the client state. client.ID = playerCount; // Send startup information to client. Network.Send(client.Socket, playerCount + "\n" + boardWidth + "\n" + boardHeight + "\n"); playerCount++; // Can't have the server modifying the clients list if it's braodcasting a message. lock (clients) { clients.Add(client); } try { Network.GetData(client); } catch (SocketException e) { Debug.WriteLine("Could not receive from client due to disconnect. " + e); client.Socket.Shutdown(SocketShutdown.Both); client.Socket.Close(); } }
/// <summary> /// Updates the frame. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="ElapsedEventArgs"/> instance containing the event data.</param> private void UpdateFrame(object sender, ElapsedEventArgs e) { lock (world) { List <SocketState> clientsToRemove = new List <SocketState>(); // A list to temporarily store clients that have disconnected. List <Snake.Snake> snakesToDie = new List <Snake.Snake>(); // A list to temporarily store snakes that must die for the updated frame. List <Food> eatenFood = new List <Food>(); // Update the position of every snake. Pass the list to update it with newly dead snakes. UpdateSnakePositions(snakesToDie, eatenFood); // Kill the snakes in the list. List <Snake.Snake> deadSnakes = KillSnakes(snakesToDie); // If the dead snake list is not empty, populate food where they died. world.RecycleSnakes(snakesToDie, snakeRecycleRate, ref playerCount); // Send the relevant information out to all clients. foreach (SocketState client in clients) { if (!client.Socket.Connected) { clientsToRemove.Add(client); continue; } try { foreach (KeyValuePair <int, Snake.Snake> el in world.Snakes) { string message = JsonConvert.SerializeObject(el.Value); Network.Send(client.Socket, message + "\n"); } foreach (Snake.Snake el in deadSnakes) { string message = JsonConvert.SerializeObject(el); Network.Send(client.Socket, message + "\n"); } foreach (KeyValuePair <int, Food> el in world.Food) { string message = JsonConvert.SerializeObject(el.Value); Network.Send(client.Socket, message + "\n"); } foreach (Food el in eatenFood) { string message = JsonConvert.SerializeObject(el); Network.Send(client.Socket, message + "\n"); } } catch (SocketException ex) { Debug.WriteLine("Unable to establish further communication with the client. Client disconnected: " + ex); client.Socket.Shutdown(SocketShutdown.Both); client.Socket.Close(); clientsToRemove.Add(client); } } // Remove the clients that are no longer in use. RemoveDisconnectedClients(clientsToRemove); } //Check to see if the limit on number of food has been reached, if not add food }
protected void ReceiveWebRequest(NetworkState state) { try { string msg = _Encoding.GetString(state.Buffer); string[] splitMsg = msg.Split(new char[] { '\r', '\n', ' ' }); int idx; for (idx = 0; idx < splitMsg.Length && splitMsg[idx] != "GET"; idx++) { ; } if (idx + 2 >= splitMsg.Length) { throw new InvalidOperationException("The Web client is stupid."); } if (splitMsg[idx + 2] != "HTTP/1.1") { throw new InvalidOperationException("The Web client is decrepit."); } idx++; string[] splitCmd = splitMsg[idx].Split('?'); //Put in the HTML tag and start the table StringBuilder response = new StringBuilder("<HTML>\n"); response.Append(GetHtmlHeader()); switch (splitCmd[0]) { case "/": case "/scores": response.Append(GetTableTitle("Scores:")); response.Append(GetPlayerScores()); break; case "/games": if (splitCmd.Length < 2) { response.Append(GetErrorPage(splitCmd[0])); } else { response.Append(GetTableTitle("Games:")); response.Append(GetPlayerGames(splitCmd[1])); } break; case "/eaten": if (splitCmd.Length < 2) { response.Append(GetErrorPage(splitMsg[idx])); } else { response.Append(GetTableTitle("Players eaten:")); response.Append(GetEatenPlayers(splitCmd[1])); } break; case "/highscores": response.Append(GetTableTitle("High Scores:")); response.Append(GetHighscores()); break; case "/current": response.Append(GetCurrentPlayers()); break; default: //response = "<html> There was an error with command \"" + splitCmd[0] + "\".</html>"; response.Append(GetErrorPage(splitCmd[0])); break; } response.Append("</HTML>"); Network.Send(state.Socket, response + "\r\n\r\n", SendCompleteCallback); } catch (Exception e) { //Strictly speaking, if something goes wrong, I don't really care. Console.WriteLine(e.Message); } }
/// <summary> /// Updates the world. /// </summary> private static void UpdateWorld() { while (true) { // Spin until delay is reached while (updateStopwatch.ElapsedMilliseconds < msPerFrame) { } updateStopwatch.Restart(); lock (world) { // Move ships foreach (Ship ship in world.GetShips()) { // Only update connected players if (IsConnected(ship.id)) { if (ship.ToTurn == 1) { ship.Dir.Rotate(turningRate); } if (ship.ToTurn == -1) { ship.Dir.Rotate(-turningRate); } Vector2D acceleration = new Vector2D(0, 0); Vector2D thrust = new Vector2D(ship.Dir); if (ship.Thrust) { acceleration = acceleration + (thrust * engineStrength); } foreach (Star star in world.GetStars()) { Vector2D g = star.Loc - ship.Loc; g.Normalize(); acceleration = acceleration + (g * star.Mass); } ship.Velocity = ship.Velocity + acceleration; ship.Loc = ship.Loc + ship.Velocity; } else { if (ship.Connected) { Console.WriteLine("Client disconnected: " + ship.Name); ship.Connected = false; } } } // Move stars if mode is set to MovingStars if (mode.Equals("MovingStars")) { foreach (Star star in world.GetStars()) { int xPos = random.Next(2); double xDir = random.NextDouble() / 10; if (xPos == 1) { xDir *= -1; } int yPos = random.Next(2); double yDir = random.NextDouble() / 10; if (yPos == 1) { yDir *= -1; } Vector2D acceleration = new Vector2D(xDir, yDir); star.Velocity = star.Velocity + acceleration; star.Loc = star.Loc + star.Velocity; if (star.Velocity.Length() > 10) { double diff = star.Velocity.Length() - 10; star.Velocity = star.Velocity - new Vector2D(diff, diff); } } } // Wrap locations WrapAround(); // Move projectiles foreach (Projectile projectile in world.GetProjectiles()) { if (projectile.Alive) { projectile.Loc = projectile.Loc + (projectile.Dir * projectileSpeed); } else { world.RemoveProjectile(projectile.id); } } // Check for collisions CollisionChecks(); // Generate world string worldStringBuilder.Clear(); foreach (Ship ship in world.GetShips()) { if (IsConnected(ship.id)) { worldStringBuilder.Append(JsonConvert.SerializeObject(ship) + "\n"); } } foreach (Projectile projectile in world.GetProjectiles()) { worldStringBuilder.Append(JsonConvert.SerializeObject(projectile) + "\n"); } foreach (Star star in world.GetStars()) { worldStringBuilder.Append(JsonConvert.SerializeObject(star) + "\n"); } } String worldString = worldStringBuilder.ToString(); // Send updated world to all clients foreach (SocketState client in clients) { if (client.Socket.Connected) { Network.Send(client.Socket, worldString); } } } }
/// <summary> /// This method is the "heartbeat" of the server. It's executed several times per second, as specified by the UPDATES_PER_SECOND member variable. /// It creates new food and viruses, atrophies player cubes, and broadcasts to all clients the most recent state of the world. /// </summary> private static void Update() { // Lock the world so that we can update it. lock (world) { // Create more food, if needed. if (world.foodCountInWorld < world.MAX_FOOD) { world.CreateFood(world.NEW_FOOD_AMT_PER_UPDATE, false); } // Create more viruses, if needed. if (world.virusCountInWorld < world.MAX_VIRUSES) { int virusesNeeded = world.MAX_VIRUSES - world.virusCountInWorld; world.CreateVirus(virusesNeeded, false); } // if any cubes have recently been exploded by a virus, start timers for them to re-merge if (world.virusMergeTimersToStart.Count > 0) { lock (mergeTimerLocker) { foreach (int teamID in world.virusMergeTimersToStart) { StartMergeTimer(teamID); } } world.virusMergeTimersToStart.Clear(); } // Clear out any old data in the StringBuilder allWorldCubes.Clear(); //List<double> TeamMasses = new List<double>(); //int Rank = 1; //foreach (int teamid in world.TeamStatistics.Keys) //{ // if (world.TeamStatistics[teamid].CurrentMass > world.TeamStatistics[teamid].MaximumMass) // { // world.TeamStatistics[teamid].MaximumMass = world.TeamStatistics[teamid].CurrentMass; // } // TeamMasses.Add(world.TeamStatistics[teamid].CurrentMass); //double largestMass = world.TeamStatistics[teamid].MaximumMass; //int largestMassId = teamid; //foreach (int teamidRank in world.TeamStatistics.Keys) //{ // if (world.TeamStatistics[teamidRank].MaximumMass > largestMass) // { // largestMass = world.TeamStatistics[teamidRank].MaximumMass; // largestMassId = teamidRank; // } //} // TeamMasses.Sort(); //} //foreach (int teamID in world.TeamStatistics.Keys) //{ // PlayerSessionStats session = world.TeamStatistics[teamID]; // if (TeamMasses.Count > 0 && TeamMasses[TeamMasses.Count - 1] == session.CurrentMass) // session.HighestRankAchieved = 1; // else if (TeamMasses.Count > 1 && TeamMasses[TeamMasses.Count - 2] == session.CurrentMass) // session.HighestRankAchieved = 2; // else if (TeamMasses.Count > 2 && TeamMasses[TeamMasses.Count - 3] == session.CurrentMass) // session.HighestRankAchieved = 3; // else if (TeamMasses.Count > 3 && TeamMasses[TeamMasses.Count - 4] == session.CurrentMass) // session.HighestRankAchieved = 4; // else if (TeamMasses.Count > 4 && TeamMasses[TeamMasses.Count - 5] == session.CurrentMass) // session.HighestRankAchieved = 5; //} // Create a list of all the current teams that exist masses. List <double> teamMasses = new List <double>(); // Loop through the sessions and get all the teams masses. foreach (int teamid in world.TeamStatistics.Keys) { // Update their mazimum mass value if needed. if (world.TeamStatistics[teamid].CurrentMass > world.TeamStatistics[teamid].MaximumMass) { world.TeamStatistics[teamid].MaximumMass = world.TeamStatistics[teamid].CurrentMass; } // Add each teams current mass to the list. teamMasses.Add(world.TeamStatistics[teamid].CurrentMass); } // Sort all the teams masses. teamMasses.Sort(); // Loop through and compare all the the current team masses with each other to determine the top 5 players // The list is sorted in acsending order so the top 5 largest players are in the bottom of the list. foreach (int teamid in world.TeamStatistics.Keys) { // If the team's mass is equal to the mass of the last item in the list, its the largest cube and therefore in 1st place. if (world.TeamStatistics[teamid].CurrentMass == teamMasses[teamMasses.Count - 1]) { if (world.TeamStatistics[teamid].HighestRankAchieved > 1) { world.TeamStatistics[teamid].HighestRankAchieved = 1; } } // If the team's mass is equal to the mass of the second to last item in the list, its the largest cube and therefore in 2nd place. else if (world.TeamStatistics[teamid].CurrentMass == teamMasses[teamMasses.Count - 2]) { if (world.TeamStatistics[teamid].HighestRankAchieved > 2) { world.TeamStatistics[teamid].HighestRankAchieved = 2; } } // If the team's mass is equal to the mass of the third to last item in the list, its the largest cube and therefore in 3rd place. else if (world.TeamStatistics[teamid].CurrentMass == teamMasses[teamMasses.Count - 3]) { if (world.TeamStatistics[teamid].HighestRankAchieved > 3) { world.TeamStatistics[teamid].HighestRankAchieved = 3; } } // If the team's mass is equal to the mass of the fourth to last item in the list, its the largest cube and therefore in 4th place. else if (world.TeamStatistics[teamid].CurrentMass == teamMasses[teamMasses.Count - 4]) { if (world.TeamStatistics[teamid].HighestRankAchieved > 4) { world.TeamStatistics[teamid].HighestRankAchieved = 4; } } // If the team's mass is equal to the mass of the fifth to last item in the list, its the largest cube and therefore in 5th place. else if (world.TeamStatistics[teamid].CurrentMass == teamMasses[teamMasses.Count - 5]) { if (world.TeamStatistics[teamid].HighestRankAchieved > 5) { world.TeamStatistics[teamid].HighestRankAchieved = 5; } } } // Perform mass attrition on player cubes, update DB if any players have died, // and send a String of all the recently modified cubes to all clients. foreach (Cube cube in world.CubesChangedSinceLastUpdate) { if (cube.food == false) { // If any players have died, update the game Database with their session stats if (cube.uid == cube.team_id && cube.Mass == 0 && !DeadPlayersUIDs.Contains(cube.uid)) { DeadPlayersUIDs.Add(cube.uid); UpdateDbWithDeadPlayerStats(cube); } // Update the attrition value for the player cubes if they have grown or shrunk. if (cube.Mass > world.RAPID_ATTRITION_THRESHOLD) { cube.Mass -= world.HIGH_ATTRITION_PER_UPDATE; } else if (cube.Mass > world.ATTRITION_LOWER_MASS_LIMIT) { cube.Mass -= world.NORMAL_ATTRITION_PER_UPDATE; } } // Serialize the cube so that it can be sent as a string. allWorldCubes.Append(JsonConvert.SerializeObject(cube) + '\n'); } // Empty the set for the next update. world.CubesChangedSinceLastUpdate.Clear(); // Send the recently modified cubes to all connected clients. lock (clientSocketsLocker) { foreach (Socket socket in ClientSockets.Keys) { Network.Send(socket, allWorldCubes.ToString()); } } } }
/// <summary> /// A Callback that's called when a new client connects. /// Gets the client's desired player name, sends the client /// their player and the world, and then starts listening /// for move and split requests. /// </summary> /// <param name="state">The state object of the newly added player, it contains its socket, callback and str builder.</param> static void ReceivePlayerName(PreservedState state) { // Pull the player name from the state obj str builder and then clear it. String playerName = state.strBuilder.ToString(); state.strBuilder.Clear(); // Remove the \n from the end of the player name. playerName = playerName.Substring(0, playerName.Length - 1); // Declare team id and player cube out here so it can be used in all the locks. int team_id; Cube playerCube; // Lock the world in order to generate information to build the player cube. lock (world) { // Create the player's cube atributes Point playerXY = world.GetPlayerSpawnLoc(); int argb_color = world.SetCubeColor(); double mass = world.INITIAL_PLAYER_MASS; int playerUid = world.GetNextUID(); team_id = playerUid; // Generate the player cube and add it to the world, new players list, and teams list. playerCube = new Cube(playerXY.X, playerXY.Y, argb_color, playerUid, team_id, false, playerName, mass); world.AddOrUpdateCube(playerCube); // Create a team for the player world.Teams.Add(team_id, new List <Cube>() { playerCube }); // Start tracking the player's team's stats PlayerSessionStats session = new PlayerSessionStats(); session.MaximumMass = world.INITIAL_PLAYER_MASS; session.CurrentMass = world.INITIAL_PLAYER_MASS; world.TeamStatistics.Add(team_id, session); } // Lock the client sockets set inorder to add the newly created players socket to it. lock (clientSocketsLocker) { ClientSockets.Add(state.socket, team_id); } // Serialize the player cube to send it to the client. String playerCubeStr = JsonConvert.SerializeObject(playerCube) + "\n"; // Send player their cube Network.Send(state.socket, playerCubeStr); // Update the callback in order to handle more players potentially adding. state.callBack = HandleClientGameRequests; // Since there are now players in the game, set this to false so updates can happen. noPlayersJoined = false; // Send player the current state of the world lock (world) { StringBuilder worldCubes = new StringBuilder(); foreach (Cube cube in world.Cubes) { worldCubes.Append(JsonConvert.SerializeObject(cube) + '\n'); } Network.Send(state.socket, worldCubes.ToString()); } // Request more data from the server. Network.i_want_more_data(state); }
/// <summary> /// /// /// Send a web brower and html page stating all the game statistics of a certain game session. /// <param name="state">The state obj containing the callback to end the socket.</param> /// </summary> private static void SendEatenCubes(PreservedState state, String req) { // The game session id requested by the browser string id = ""; // Make sure url has valid format, and pull out the id try { if (req.Substring(0, 9) != "eaten?id=") { SendErrorMessage(state); return; } id = req.Substring(req.IndexOf('=') + 1); } // If there is an error send the error html page to the browser catch (Exception e) { SendErrorMessage(state); return; } // begin generating the string to be sent to the web browser. String html = "<h1>Cubes Eaten During Games Session " + id + "</h1>"; html += "<table border = 1><tr><td><b>Name</b></td><td><b>Players Eaten</b></td><td><b>Max Mass</b></td><td><b>Rank</b></td><td><b>Cubes Eaten</b></td><td><b>Players Eaten</b></td><td><b>Time Alive (mm:ss)</b></td><td><b>Time Of Death</b></td></tr>"; String namesOfEatenPlayers = ""; // Begin requesting information from the database by opening the connection. using (MySqlConnection conn = new MySqlConnection(DbConnectionString)) { try { conn.Open(); MySqlCommand command = conn.CreateCommand(); // First, get the names of players eaten, if any exist command.CommandText = "SELECT Players_Eaten FROM Names_Of_Players_Eaten WHERE Names_Of_Players_Eaten.Session_ID = " + id; // Retrive all the eaten players. using (MySqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { namesOfEatenPlayers += reader["Players_Eaten"]; } } // Then, get all the rest of the info for the session from the main table command.CommandText = "SELECT Name, Maximum_Mass, Rank, Number_Of_Cubes_Eaten, Number_Of_Player_Cubes_Eaten, Lifespan, Time_Of_Death FROM Player_Games WHERE Player_Games.ID = " + id; using (MySqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { html += "<tr><td>" + reader["Name"] + "</td><td>" + namesOfEatenPlayers + "</td><td>" + reader["Maximum_Mass"] + "</td><td>" + reader["Rank"] + "</td><td>" + reader["Number_Of_Cubes_Eaten"] + "</td><td>" + reader["Number_Of_Player_Cubes_Eaten"] + "</td><td>" + ToDateFormat(reader["Lifespan"].ToString()) + "</td><td>" + reader["Time_Of_Death"] + "</td></tr>"; } html += "</table>"; } } // If there is an error send the error html page to the browser catch (Exception e) { SendErrorMessage(state); return; } } // Send the information to the webpage and close the socket. Network.Send(state.socket, HttpResponseHeader); Network.Send(state.socket, html, (Object) => { Console.WriteLine("Sent HTML"); state.socket.Close(); return; }); }
/// <summary> /// /// Send a web brower and html page stating all the game statistics of a certain player for all games they have played. /// <param name="state">The state obj containing the callback to end the socket.</param> /// </summary> private static void SendGamesByPlayer(PreservedState state, String req) { try { // Retrive the palyer name from the request of whose information we need to pull. If it doesn't exist send an error message. if (req.Substring(0, 13) != "games?player=") { SendErrorMessage(state); return; } // Retrieve the player name and begin generating the string to be sent to the web browser. string player = req.Substring(req.IndexOf('=') + 1); String html = "<h1>Games for " + player + "</h1>"; html += "<table border = 1><tr><td><b>Max Mass</b></td><td><b>Rank</b></td><td><b>Cubes Eaten</b></td><td><b>Players Eaten</b></td><td><b>Time Alive</b></td><td><b>Time Of Death</b></td></tr>"; // Access our database using (MySqlConnection conn = new MySqlConnection(DbConnectionString)) { try { conn.Open(); MySqlCommand command = conn.CreateCommand(); // Generate the command for the desired information we seek from the database. command.CommandText = "select Maximum_Mass, Rank, Number_Of_Cubes_Eaten, Number_Of_Player_Cubes_Eaten, Lifespan, Time_Of_Death from Player_Games where Name = \"" + player + "\""; // Retrieve that inforamtion from the database using (MySqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { html += "<tr><td>" + reader["Maximum_Mass"] + "</td><td>" + reader["Rank"] + "</td><td>" + reader["Number_Of_Cubes_Eaten"] + "</td><td>" + reader["Number_Of_Player_Cubes_Eaten"] + "</td><td>" + ToDateFormat(reader["Lifespan"].ToString()) + "</td><td>" + reader["Time_Of_Death"] + "</td></tr>"; } html += "</table><br><a href = \"http://localhost:11100/scores\">" + "Go To All Scores" + "</a>"; } } // If there is an error send the error html page to the browser catch (Exception e) { SendErrorMessage(state); return; } } // Send the information to the webpage and close the socket. Network.Send(state.socket, HttpResponseHeader); Network.Send(state.socket, html, (Object) => { Console.WriteLine("Sent HTML"); state.socket.Close(); return; }); } // If there is an error send the error html page to the browser catch (Exception e) { SendErrorMessage(state); } }