private List <string> ExtractSocketData(SocketState socketState, int numLinesToRead = int.MaxValue) { lock (socketState) { List <string> socketData = new List <string>(); int numLinesRead = 0; foreach (string s in GetSocketDataSplitByNewlines(socketState)) { if (string.IsNullOrWhiteSpace(s)) { // sometimes the regex split creates empty strings, ignore them and continue looping continue; } if (!StringEndsWithNewline(s)) { // the rest of the message is incomplete, break the loop to leave the extra data alone break; } socketData.Add(s); socketState.RemoveData(0, s.Length); numLinesRead++; if (numLinesRead >= numLinesToRead) { break; } } return(socketData); } }
/// <summary> /// Given the data that has arrived so far, /// potentially from multiple receive operations, /// determine if we have enough to make a complete message, /// and process it (print it and broadcast it to other clients). /// </summary> /// <param name="sender">The SocketState that represents the client</param> private void ReceiveClientData(SocketState state) { lock (state) { string totalData = state.GetData(); //Console.WriteLine("Connected with stateID: " + state.ID); string[] parts = Regex.Split(totalData, @"(?<=[\n])"); // Loop until we have processed all messages. // We may have received more than one. foreach (string p in parts) { // Ignore empty strings added by the regex splitter if (p.Length == 0) { continue; } // The regex splitter will include the last string even if it doesn't end with a '\n', // So we need to ignore it if this happens. if (p[p.Length - 1] != '\n') { break; } // Process the message sent by client ProcessMessage(p, state); // Remove it from the SocketState's growable buffer state.RemoveData(0, p.Length); } Networking.GetData(state); // continue to receive data } }
/// <summary> /// This method processes the data received through the socket by splitting it and deserializing /// each string. /// </summary> /// <param name="ss">Socket state representing the connection</param> private void ProcessData(SocketState ss) { //Splits the string but keeps the '\n' characters string totalData = ss.GetData(); string[] parts = Regex.Split(totalData, @"(?<=[\n])"); lock (TheWorld) { foreach (string p in parts) { //This is to ignore empty strings if (p.Length == 0) { continue; } //This is so it ignores the last string if it doesn't end in '\n' if (p[p.Length - 1] != '\n') { break; } //Calls a method to deserialize the data and then removes the data from the buffer UpdateObject(p); ss.RemoveData(0, p.Length); } } }
/// <summary> /// This method receives the startup data sent by the server (world size and player ID) /// </summary> /// <param name="ss">Socket state representing the connection</param> private void ReceiveStartingData(SocketState ss) { if (ss.ErrorOccured == true) { ErrorEvent("Unable to receive tank ID and world size"); if (ss.TheSocket.Connected) { ss.TheSocket.Close(); } return; } //Splits the data and stores it in a string array string[] startingInfo = Regex.Split(ss.GetData(), @"\n"); //Parses and stores the ID and world size tankID = Int32.Parse(startingInfo[0]); TheWorld.worldSize = Int32.Parse(startingInfo[1]); //Removes the ID and world size from the socket string builder and processes the other data received ss.RemoveData(0, tankID.ToString().Length + TheWorld.worldSize.ToString().Length + 2); ProcessData(ss); //Changes the OnNetworkAction to the next method that will be called every frame ss.OnNetworkAction = ReceiveFrameData; Networking.GetData(ss); }
/// <summary> /// Private helper method to setup player ID, world size and JSON walls /// that are only sent once. /// </summary> /// <param name="state"></param> private void ProcessMessages(SocketState state) { string totalData = state.GetData(); string[] parts = Regex.Split(totalData, @"(?<=[\n])"); // Loop until we have processed all messages. // We may have received more than one. foreach (string p in parts) { // Ignore empty strings added by the regex splitter if (p.Length == 0) { continue; } // The regex splitter will include the last string even if it doesn't end with a '\n', // So we need to ignore it if this happens. if (p[p.Length - 1] != '\n') { break; } // First two messages are of type integer; Setup ID and world size int n; bool isInt = int.TryParse(p, out n); if (isInt) { if (PlayerID == null) { PlayerID = n; } else { if (theWorld.size == 0) { theWorld.size = n; } // Call our connected callback and use size to load in background Connected(); } continue; } // Skipping incomplete JSONS if (p[0] != '{' || !p.EndsWith("\n")) { continue; } // Load and parse the incoming JSON LoadObject(p); // Then remove it from the SocketState's growable buffer state.RemoveData(0, p.Length); } }
/// <summary> /// This method will implement changes created from Control Commands /// This is an event loop that will constantly receive messages from the socket states /// </summary> /// <param name="connection"></param> private void GetActionDataFromClient(SocketState connectionToClient) { //Checks if error occured. This means connection broke and so player is disconnected from server if (connectionToClient.ErrorOccured) { //Show that player has disconnected Console.WriteLine("Player " + connections[connectionToClient].ToString() + " has disconnected"); //Set tank stats relevant to disconnecting lock (connections) { int tankID = connections[connectionToClient]; serverWorld.Tanks[tankID].HasDisconnected = true; serverWorld.Tanks[tankID].HasDied = true; connections.Remove(connectionToClient); return; } } //Get JSON information from the socket state string wholeData = connectionToClient.GetData(); string completeMessage; //Make sure data is complete if (!wholeData.EndsWith("\n")) { //find the last instance of the newline and split the string at that point int completedPoint = wholeData.LastIndexOf('\n'); completeMessage = wholeData.Substring(0, completedPoint); } //Message is complete and we can move forward as normal else { completeMessage = wholeData; } //Split string by newline string[] movementUpdates = completeMessage.Split('\n'); foreach (string command in movementUpdates) { //Skip over empty strings if (command == "") { continue; } //Process command ControlCommand newCommand = JsonConvert.DeserializeObject <ControlCommand>(command); //Update tank based on control command UpdateTankState(newCommand, connectionToClient); } //Remove old data connectionToClient.RemoveData(0, completeMessage.Length); //Begin loop again Networking.GetData(connectionToClient); }
private void ServeHttpRequest(SocketState state) { if (state.ErrorOccured) { return; } string request = state.GetData(); state.RemoveData(0, request.Length); string response = BuildHtmlResponse(request); Networking.SendAndClose(state.TheSocket, response); }
/// <summary> /// callback method to continuously receive data from server and send data to server /// </summary> /// <param name="state"></param> private void ReceiveWorld(SocketState state) { HandleConnectionError(state); if (state.ErrorOccured) { return; } string totalData = state.GetData(); string[] parts = Regex.Split(totalData, @"(?<=[\n])"); // Loop until we have processed all messages. // We may have received more than one. foreach (string p in parts) { // Ignore empty strings added by the regex splitter if (p.Length == 0) { continue; } // The regex splitter will include the last string even if it doesn't end with a '\n', // So we need to ignore it if this happens. if (p[p.Length - 1] != '\n') { break; } ProcessMessage(p); if (UpdateArrived != null) { UpdateArrived(); } // Then remove it from the SocketState's growable buffer state.RemoveData(0, p.Length); } Networking.GetData(state); // Need to call this in order to get new string info into state control.Moving = moveArray[moveIndex]; // control's moving is set // Convert data need to send to server to JSON string string sendMessage = JsonConvert.SerializeObject(control) + "\n"; Networking.Send(state.TheSocket, sendMessage); // send data to server }
/// <summary> /// Process the data in current state buffer, the method will find all tokens end with \n, and make sure all data being processed(ToDo) is complete. /// It also saves incomplete data in the SocketState buffer and waits for the next incoming receive the complete the partial data /// </summary> /// <param name="state"></param> /// <param name="ToDo"> A action that will be taken for each token(end with \n) in the data</param>> private static string[] ProcessData(SocketState state) { string data = state.GetData(); string[] jArray = data.Split('\n'); if (data.Last() != '\n') // Meaning there is incomplete data in current buffer { // Keep partial data state.RemoveData(0, data.LastIndexOf('\n') + 1); // Set the last element to empty string jArray[jArray.Length - 1] = ""; } else { //All data are complete, clear the whole buffer state.ClearData(); } return(jArray); }
private void ReceivePlayerName(SocketState state) { lock (state) { if (state.ErrorOccured) { RemoveClient(state.ID); return; } string[] lines = state.GetData().Split('\n'); playerName = lines[0]; // save player name PlayerNames.Add(state.ID, playerName); state.RemoveData(0, playerName.Length); } Console.WriteLine(playerName + " joined the game."); // send ID and world size to client string sendMessage = state.ID + "\n" + theServerWorld.worldSize + "\n"; Networking.Send(state.TheSocket, sendMessage); // Save the client state // Need to lock here because clients can disconnect at any time lock (clients) { clients[state.ID] = state; } SendWalls(state); // send walls info to the client WallCollisionSize(); // extend the wall detection size Tank tank = new Tank((int)state.ID, playerName); RespawnTank(tank); theServerWorld.Tanks.Add((int)state.ID, tank); state.OnNetworkAction = ReceiveClientData; // Continue the event loop that receives messages from this client Networking.GetData(state); }
/// <summary> /// This method receives a socket state from receive message, getting the socket's message /// and updating the world's models using Json messages. /// </summary> /// <param name="socket">Socket containing message data</param> private void ProcessMessage(SocketState socket) { // Get string data from socket state string JsonMessage = socket.GetData(); //This is a string that will contain all the complete data i.e. no partial Json strings at the end string completeMessage; //If the Json message does not end with newline, it means partial Json message at end so //we take the part that we can process if (!JsonMessage.EndsWith("\n")) { //find the last instance of the newline and split the string at that point int completedPoint = JsonMessage.LastIndexOf('\n'); completeMessage = JsonMessage.Substring(0, completedPoint); } //Message is complete and we can move forward as normal else { completeMessage = JsonMessage; } // Separate objects within Json message using new line string[] parsedMessage = completeMessage.Split('\n'); JObject curObj; JToken curToken; // Loop through each Json segment and identify its type to update model. // Once object type has been found pass in the Json message along with an int // value that references it's type within the UpdateWorldModel method. foreach (string curMessage in parsedMessage) { //Skip any strings that are empty so to not throw error if (curMessage == "") { continue; } //Parse the Json object and compare to other objects curObj = JObject.Parse(curMessage); // Check if object is tank curToken = curObj["tank"]; if (curToken != null) { UpdateWorldModel(curMessage, 0); tankInfoReceived = true; continue; } //Update world model with walls curToken = curObj["wall"]; if (curToken != null) { UpdateWorldModel(curMessage, 1); continue; } // Check if object is projectile curToken = curObj["proj"]; if (curToken != null) { UpdateWorldModel(curMessage, 2); continue; } // Check if object is PowerUp curToken = curObj["power"]; if (curToken != null) { UpdateWorldModel(curMessage, 3); continue; } // Check if object is Beam curToken = curObj["beam"]; if (curToken != null) { UpdateWorldModel(curMessage, 4); continue; } } //Send the player ID to view for drawing purposes //Only sends once we receive tank data about oursleves if (tankInfoReceived) { PlayerIDGiven(playerID); SendTankUpdate(selfTank); } //Clear old data socket.RemoveData(0, completeMessage.Length); //Notify the View to redraw the world UpdateWorld(); }
/// <summary> /// Processes the message received from the server. If it is a json object, it updates the world accordingly. /// Otherwise, it is either the player id or world size, and thus sets those accordingly. /// </summary> private void ProcessMessage(SocketState state) { if (state.ErrorOccured) { return; } //Gets data and parts from data string totalData = state.GetData(); string[] parts = Regex.Split(totalData, @"(?<=[\n])"); foreach (string s in parts) { //If the part has a length of 0, then it is not a complete message if (s.Length <= 0) { continue; } //If the part does not end with newline, then the message is incomplete if (s[s.Length - 1] != '\n') { break; } //If the part is a json, deserialize if (s[0] == '{') { lock (world) { //Get the json object out of the part JObject obj = JObject.Parse(s); JToken type; //Wall is not loaded if (!world.WallsLoaded) { type = obj["wall"]; if (type != null) { Wall w = JsonConvert.DeserializeObject <Wall>(s); world.AddWall(w.ID, w.p1, w.p2); } else { //As soon as we reach a JSON that isn't a wall, the walls are loaded world.LoadWalls(); WorldLoaded(); } } //If it a tank, update the world type = obj["tank"]; if (type != null) { Tank t = JsonConvert.DeserializeObject <Tank>(s); world.UpdateTank(t.ID, t.location, t.orientation, t.aiming, t.name, t.hitPoints, t.score, t.died, t.disconnected); } //Projectile type = obj["proj"]; if (type != null) { Projectile p = JsonConvert.DeserializeObject <Projectile>(s); world.UpdateProjectile(p.ID, p.location, p.orientation, p.owner, p.died); } //Powerup type = obj["power"]; if (type != null) { Powerup p = JsonConvert.DeserializeObject <Powerup>(s); world.UpdatePowerup(p.ID, p.location, p.died); } //Beam type = obj["beam"]; if (type != null) { Beam b = JsonConvert.DeserializeObject <Beam>(s); BeamFired(b); } } } //If it is not a json object, then it must be the world size or player id else { //If player id is not set, then the part is the player id if (PlayerID < 0) { PlayerID = Int32.Parse(s); IDLoaded(); } //Otherwise, the part must be the world else { world = new World(Int32.Parse(s)); } } lock (state) { //Remove the processed part state.RemoveData(0, s.Length); } //If OnUpdate is set, call it if (OnUpdate != null) { OnUpdate(); } } }
/// <summary> /// Processes the message received from the server. If it is a json object, it updates the world accordingly. /// Otherwise, it is either the player id or world size, and thus sets those accordingly. /// </summary> private void ProcessMessage(SocketState state) { if (state.ErrorOccured) { return; } //Gets data and parts from data string totalData = state.GetData(); string[] parts = Regex.Split(totalData, @"(?<=[\n])"); foreach (string s in parts) { //If the part has a length of 0, then it is not a complete message if (s.Length <= 0) { continue; } //If the part does not end with newline, then the message is incomplete if (s[s.Length - 1] != '\n') { break; } //If the part is a json, deserialize if (s[0] == '{') { lock (world) { //Get the json object out of the part JObject obj = JObject.Parse(s); JToken type; //Commands type = obj["controlcommand"]; if (type != null) { ControlCommand c = JsonConvert.DeserializeObject <ControlCommand>(s); //do thing } } } //If it is not a json object, then it must be the player's name else { lock (world) { users.Add(state, userCount); //Change position to random world.UpdateTank(userCount++, new Vector2D(0, 0), new Vector2D(0, 0), new Vector2D(0, 0), s, 3, 0, false, false); } } lock (state) { //Remove the processed part state.RemoveData(0, s.Length); } } }
/// <summary> /// Process any buffered messages separated by '\n' /// Then inform the view /// </summary> /// <param name="state"></param> private void ReceiveWorld(SocketState state) { //Gets the data from the state and splits it by a new line string totalData = state.GetData(); string[] parts = Regex.Split(totalData, @"(?<=[\n])"); // Loop until we have processed all messages. // We may have received more than one. //Gets the player number, which should only be once int playerNumber = 0; havePlayerNum = int.TryParse(parts[0], out playerNumber); parts[0] = ""; if (playerNumber != 0) { playerNum = playerNumber; } //Gets the dimensions of the world that should only happen once int dim = 0; haveDimension = int.TryParse(parts[1], out dim); parts[1] = ""; if (dim != 0) { worldDimension = dim; world = new World(worldDimension); } //Iterates through all the data given by the server foreach (string p in parts) { // Ignore empty strings added by the regex splitter if (p.Length == 0) { continue; } // The regex splitter will include the last string even if it doesn't end with a '\n', // So we need to ignore it if this happens. if (p[p.Length - 1] != '\n') { break; } //Locks with a world so that we process information in a single thread lock (world) { //Parses the object with the JSON JObject jObject = JObject.Parse(p); //Converts the JSON object to a token based on the name of the string JToken projToken = jObject["proj"]; JToken beamToken = jObject["beam"]; JToken tankToken = jObject["tank"]; JToken wallToken = jObject["wall"]; JToken powerToken = jObject["power"]; //If the projToken is not null, i.e. if the JSON string passed was a projectile, then it goes in this condition if (projToken != null) { //Deserializes the string and converts it to a projectile Projectile proj = JsonConvert.DeserializeObject <Projectile>(p); //Adds the projectile to the world world.SetProjectile(proj.GetID(), proj); //If projectile is dead, removes the projectile from the world if (proj.GetDead() == true) { world.GetProjectile().Remove(proj.GetID()); } } //If the beamToken is not null, i.e. if the JSON string passed was a beam, then it goes in this condition if (beamToken != null) { //Deserializes the string and converts it to a beam Beams b = JsonConvert.DeserializeObject <Beams>(p); //Adds the beam in the world's beam dictionary world.SetBeams(b.GetBeamID(), b); } //If the tankToken is not null, i.e. if the JSON string passed was a tank, then it goes in this condition if (tankToken != null) { //Deserializes the string and converts it to a tank Tank t = JsonConvert.DeserializeObject <Tank>(p); //Sets the color of the tank based on the tank's ID t.SetColor(t.GetID()); //Adds the tank to the world's tank dictionary world.SetTanks(t.GetID(), t); //If the hitpoints of the tank are 0, then it remove it from the dictionary if (t.GetHitPoints() == 0) { world.GetTanks().Remove(t.GetID()); } //If the tank gets disconnected, then it remove it from the dictionary if (t.GetDisconnected()) { world.GetTanks().Remove(t.GetID()); } //If the tank is dead, then it remove it from the dictionary if (t.GetDead()) { world.GetTanks().Remove(t.GetID()); } } //If the wallToken is not null, i.e. if the JSON string passed was a wall, then it goes in this condition if (wallToken != null) { //Deserializes the string and converts it to a wall Wall w = JsonConvert.DeserializeObject <Wall>(p); //Adds the wall to the world's wall dictionary world.SetWalls(w.GetID(), w); } //If the powerToken is not null, i.e. if the JSON string passed was a powerup, then it goes in this condition if (powerToken != null) { //Deserializes the string and converts it to a powerup Powerups power = JsonConvert.DeserializeObject <Powerups>(p); //Adds the powerup to the world's powerup dictionary world.SetPowerups(power.GetID(), power); //If the powerup is dead, then it removes it from the dictionary if (power.GetDead()) { world.GetPowerups().Remove(power.GetID()); } } } // Then remove it from the SocketState's growable buffer state.RemoveData(0, p.Length); } if (UpdateArrived != null) { // inform the view to redraw UpdateArrived(); } //Inform the server Process(); }
/// <summary> /// Processes the message received from the server. If it is a json object, it updates the world accordingly. /// Otherwise, it is either the player id or world size, and thus sets those accordingly. /// </summary> private void ProcessMessage(SocketState state) { if (state.ErrorOccured) { return; } //Gets data and parts from data string totalData = state.GetData(); string[] parts = Regex.Split(totalData, @"(?<=[\n])"); foreach (string s in parts) { //If the part has a length of 0, then it is not a complete message if (s.Length <= 0) { continue; } //If the part does not end with newline, then the message is incomplete if (s[s.Length - 1] != '\n') { break; } //If the part is a json, deserialize if (s[0] == '{') { lock (controls) { //Get the json object out of the part JObject obj = JObject.Parse(s); JToken type; //Commands type = obj["moving"]; if (type != null) { ControlCommand c = JsonConvert.DeserializeObject <ControlCommand>(s); if (!controls.ContainsKey(users[state])) { controls.Add(users[state], c); } else { controls[users[state]] = c; } } } } //If it is not a json object, then it must be the player's name else { lock (world) { Random r = new Random(); Vector2D RandLoc = new Vector2D(r.Next(-world.GetSize() / 2 + 16, world.GetSize() / 2 - 16), r.Next(-world.GetSize() / 2 + 16, world.GetSize() / 2 - 16)); while (CheckTankWallCollision(RandLoc)) { RandLoc = new Vector2D(r.Next(-world.GetSize() / 2 + 16, world.GetSize() / 2 - 16), r.Next(-world.GetSize() / 2 + 16, world.GetSize() / 2 - 16)); } world.UpdateTank((int)state.ID, RandLoc, new Vector2D(0, 1), new Vector2D(0, 1), s, 3, 0, false, false); } } lock (state) { //Remove the processed part state.RemoveData(0, s.Length); } } }