/// <summary> /// Updates the world at the current FPS. /// </summary> private void Update() { lock (serverWorld) { serverWorld.UpdateWorld(); serverWorld.CheckCollision(); foreach (SocketState state in clients.Values.ToList()) { Networking.Send(state.TheSocket, serverWorld.GetWorld() + "\n"); } } }
/// <summary> /// "This is a ("callMe") delegate that implements the server's part of the initial /// handshake." /// </summary> /// <param name="state">The client's socket information</param> private static void ReceiveName(SocketState state) { lock (World) { // Create a new Ship with the given name and a new unique ID Ship newShip = new Ship(); Random rand = new Random(); newShip.ID = ShipID; newShip.loc = new Vector2D(rand.Next(-(UniverseSize / 2), UniverseSize / 2), rand.Next(-(UniverseSize / 2), UniverseSize / 2)); newShip.dir = new Vector2D(rand.Next(-180, 180), rand.Next(-180, 180)); newShip.thrust = false; newShip.name = state.sb.ToString().Trim(); newShip.hp = StartingHitPoints; newShip.score = 0; newShip.alive = true; newShip.velocity = new Vector2D(0, 0); newShip.dir.Normalize(); newShip.projFired = 0; newShip.projHit = 0; // Add the new ship to World World.Ships.Add(newShip.ID, newShip); // Alter ShipID for next incoming Ship ShipID++; // Setting the state's ID to match the newly added Ship state.ID = newShip.ID; // Prepare the startup info to the client(ID and world size) state.sb.Clear(); state.sb.Append(newShip.ID + "\n"); state.sb.Append(UniverseSize + "\n"); state.callMe = HandleData; // Send the starup info to the client Networking.Send(state.theSocket, state.sb.ToString()); // Add the client's socket to the list of all clients Clients.Add(state); Console.WriteLine("A new client has connected to the server..."); // "Then ask the client for data" Networking.GetData(state); } }
//Send the walls to the player on initial connect private void Sendwalls(SocketState state) { StringBuilder sb = new StringBuilder(); lock (world) { foreach (Wall w in world.GetWalls()) { sb.Append(JsonConvert.SerializeObject(w) + "\n"); } } Networking.Send(state.TheSocket, sb.ToString()); //Console.WriteLine(sb.ToString()); sb.Clear(); }
/// <summary> /// This method updates the state of the world. /// </summary> private static void Update() { StringBuilder sb = new StringBuilder(); //lock the world lock (world) { //for all star in star collection we appenfd to sb. foreach (Star s in world.starCollection.Values) { sb.Append(s.ToString() + "\n"); } //method to update ships. updateships(sb); //methods that handles collisons. Collisons(); //method that updates projctile. updateProjectile(sb); } lock (world) { //copy of the list to remove it from the ss. List <SocketState> copy = new List <SocketState>(list); foreach (SocketState ss in list) { //if the client window is closed. if ((Networking.Send(ss.theSocket, sb.ToString())) == false) { lock (world) { int id = ss.ID; //set the ships hp tp zero. world.shipCollection[ss.ID].setShipHp(0); //and set ship disconnected to true. world.shipCollection[ss.ID].setDisconnected(true); //remove the ship from the ss. copy.Remove(ss); } } } //after removing ship from the client we set list back to copy. list = copy; } }
private void NewPlayer(SocketState state) { string worldSizeString = worldSize.ToString(); string playerIDString = state.ID.ToString(); if (state.ErrorOccured) { return; } lock (playerSockets) { playerSockets[state.ID] = state; } Networking.Send(state.TheSocket, playerIDString + "\n"); Networking.Send(state.TheSocket, worldSizeString + "\n"); state.OnNetworkAction = ProcessMessage; Networking.GetData(state); }
/// <summary> /// Callback method for after the server receives the name /// </summary> /// <param name="sock"></param> static void ReceiveName(SocketState sock) { //Create the players tank and assign its name and ID Tank mainPlayer = new Tank(); mainPlayer.name = sock.GetData().Trim(); mainPlayer.ID = (int)sock.ID; //Add the tank to both tank classes lock (tanks) { allTanksEver.Add(mainPlayer.ID, mainPlayer); tanks.Add(mainPlayer.ID, mainPlayer); } //Assign the health of the tank and spawn the tank mainPlayer.hitPoints = StartingHitPoints; tankRespawn(mainPlayer); //Change the callback to CommandRequestHandler and clear the sock's data sock.OnNetworkAction = CommandRequestHandler; sock.ClearData(); //Get the startup data, serialize it, and send it to the client string startupData = mainPlayer.ID + "\n" + serverWorld.worldSize + "\n"; foreach (Wall w in walls.Values) { startupData += JsonConvert.SerializeObject(w) + "\n"; } startupData += GetWorldData(); Networking.Send(sock.TheSocket, startupData); //Add the client to the connectedClients list and ask for more data lock (connectedClients) { connectedClients.Add(sock.ID, sock); } Console.WriteLine(mainPlayer.name + " has Joined the Game"); mainPlayer.joined = true; Networking.GetData(sock); }
/// <summary> /// Callback to receive player name. /// </summary> /// <param name="state"></param> private void ReceivePlayerName(SocketState state) { // If a Socket disconnects, return. if (state.ErrorOccured) { return; } // Add tank to the world if it doesnt exist yet if (!clients.ContainsKey(state.ID)) { lock (serverWorld) { serverWorld.AddTank(state.ID, state.GetData()); } } state.OnNetworkAction = HandleRequests; // Send start-up info; Lock the socket to avoid race conditions lock (state.TheSocket) { if (!clients.ContainsKey(state.ID)) { Networking.Send(state.TheSocket, $"{ state.ID }\n{ WORLD_SIZE }\n"); Networking.Send(state.TheSocket, serverWorld.GetWalls() + "\n"); } } // Add client to clients list lock (clients) { if (!clients.ContainsKey(state.ID)) { clients.Add(state.ID, state); Console.WriteLine($"Player({ state.ID }): { state.GetData().TrimEnd('\n') } Has Joined The Game!"); } } Networking.GetData(state); }
/// <summary> /// Gets the player's name and sends startup info /// </summary> /// <param name="s"></param> private void HandlePlayerName(SocketState s) { string data = s.GetData(); string[] parts = Regex.Split(data, @"(?<=[\n])"); // Check if a full message has been received if (parts.Length > 1) { // Generate a new tank for the client Tank t = new Tank((int)s.ID, parts[0].Substring(0, parts[0].Length - 1), new TankWars.Vector2D(50, 50)); spawnTank(t); lock (world) { // Add the tank to the world world.setTankData(t); s.RemoveData(0, parts[0].Length); //Send the player's id Networking.Send(s.TheSocket, s.ID + "\n"); // Send world size Networking.Send(s.TheSocket, world.UniverseSize + "\n"); // Send all the walls foreach (Wall w in world.Walls.Values) { Networking.Send(s.TheSocket, JsonConvert.SerializeObject(w) + '\n'); } } // Add the tank to the dictionary so it can start receiving frames lock (clients) { clients.Add(s, t); } s.OnNetworkAction = HandleClientCommands; } Networking.GetData(s); }
/// <summary> /// This methods recieves the name and does the first handshake with the /// client. /// </summary> /// <param name="ss"></param> public static void RecieveName(SocketState ss) { string name = ""; //totalData uses string builder to store all the string from the ss. string totalData = ss.sb.ToString(); //this method splits totalData by nextline. String[] parts = totalData.Split('\n'); //the first string sent is the players name. name = parts[0]; Ship ship; //lock the world lock (world) { //calls generate new ship from world and sets it to ship. ship = world.generatenewShip(name); //sets ss.ID to ships id. ss.setSocketStateID(ship.getShipId()); } ss.callMe = callBack; //lock the list and add the socketstate. lock (list) { list.Add(ss); } int universe = UniverseSize; //removes the name from the socketstate. ss.sb.Remove(0, parts[0].Length + 1); //sends data to the specific socket. Networking.Send(ss.theSocket, ss.ID.ToString() + "" + "\n" + universe + "\n"); //calling get data to get more data. Networking.GetData(ss); }
/// <summary> /// Sends the client the world according to where every object is /// </summary> static void Update() { //Lists to hold objects that will be removed List <SocketState> disconectedClients = new List <SocketState>(); List <Object> expiredObjects = new List <object>(); lock (projectiles) { //For every object in projectiles, Find where it is and update its location foreach (Projectile p in projectiles.Values) { //Calculate the object's new location p.orientation.Normalize(); Vector2D projAngle = new Vector2D(p.orientation.GetX() * ProjectileSpeed, p.orientation.GetY() * ProjectileSpeed); p.location = p.location + projAngle; //If the projectile has collided with anything or is dead, add it to the expired objects list if (collisionCheck(p.location.GetX(), p.location.GetY())) { p.died = true; expiredObjects.Add(p); } if (p.location.GetX() > serverWorld.worldSize / 2 || p.location.GetY() > serverWorld.worldSize / 2 || p.location.GetX() < -serverWorld.worldSize / 2 || p.location.GetY() < -serverWorld.worldSize / 2) { p.died = true; expiredObjects.Add(p); } } } lock (tanks) { //For every object in Tanks, find it's status and update the object foreach (Tank t in tanks.Values) { //If the time to respawn counter is still going, drop it down by one if (t.timeToRespawn > 0) { t.timeToRespawn--; } //If the timer is over, respawn the tank else if (t.hitPoints == 0 && !t.disconnected) { tankRespawn(t); } //If the status of the tank is dead, set it to be alive if (t.died) { t.died = false; } //If the tank is disconnected however, is has died if (t.disconnected) { t.died = true; t.hitPoints = 0; } t.shotTimer--; lock (beams) { //Check to see if any beams currently on screen have hit a tank foreach (Beam b in beams.Values) { //If the beam has hit a tank, the tank is dead if (Intersects(b.origin, b.direction, t.location, 30) && t.hitPoints > 0) { t.hitPoints = 0; t.died = true; t.timeToRespawn = RespawnRate; tanks[b.tankID].tankShotsHit++; tanks[b.tankID].score++; } } } lock (projectiles) { //Check to see if any projectiles on screen have hit a tank foreach (Projectile p in projectiles.Values) { //If the projectile hit a tank then lower the hitpoints by one for that tank if (p.tankID != t.ID && objectCollisionCheck(t.location, p.location) && t.hitPoints > 0 && !p.died) { p.died = true; expiredObjects.Add(p); t.hitPoints -= 1; tanks[p.tankID].tankShotsHit++; //If the tank has no hitpoints left, it has died if (t.hitPoints == 0) { t.died = true; t.timeToRespawn = RespawnRate; tanks[p.tankID].score++; } } } } lock (powerups) { //Check to see if any powerups on screen have hit a tank foreach (Powerup p in powerups.Values) { //If a powerup has hit a tank, increase the beam amount for that tank and destroy the powerup if (objectCollisionCheck(t.location, p.location) && t.hitPoints > 0 && !p.died) { Random rand = new Random(); p.died = true; p.respawnTimer = rand.Next(10, MaxPowerupDelay); t.railgunShots++; } } } } } int powerupRespawnCount = 0; lock (powerups) { //Check all powerups to see if they can respawn foreach (Powerup p in powerups.Values) { //If the timer is up for a powerup to respawn, add one to the count if (p.respawnTimer == 0) { p.respawnTimer = -1; powerupRespawnCount++; expiredObjects.Add(p); } p.respawnTimer--; } } lock (powerups) { //Respawn all powerups for (int i = 0; i < powerupRespawnCount; i++) { powerRespawn(); } } //Send the data to all connected clients, if the send fails, add that client to the //disconnectedClients list to be removed later String dataToSend = GetWorldData(); lock (connectedClients) { foreach (SocketState sock in connectedClients.Values) { if (!Networking.Send(sock.TheSocket, dataToSend)) { lock (disconectedClients) { disconectedClients.Add(sock); } } } } lock (connectedClients) { //Remove the disconnected client and destroy its tank foreach (SocketState sock in disconectedClients) { connectedClients.Remove(sock.ID); tanks[(int)sock.ID].died = true; tanks[(int)sock.ID].hitPoints = 0; tanks[(int)sock.ID].disconnected = true; } } lock (beams) { //Destroy all active beams beams.Clear(); } lock (expiredObjects) { //Remove all expired objects from their respective lists foreach (Object o in expiredObjects) { if (o is Projectile) { lock (projectiles) { Projectile p = (Projectile)o; projectiles.Remove(p.ID); } } if (o is Powerup) { lock (powerups) { Powerup p = (Powerup)o; powerups.Remove(p.ID); } } } expiredObjects.Clear(); } }
/// <summary> /// This part sets up a tank with the player name and sends the startup info to the client including the world size, player ID, /// and walls. /// </summary> /// <param name="ss">Socket state for the connection</param> private static void SendStartupInfo(SocketState ss) { if (ss.ErrorOccured == true) { Console.WriteLine("Error occured while accepting: \"" + ss.ErrorMessage + "\""); return; } //Gets the name and ID from the socket and removes the name from the socket string tankName = ss.GetData(); int tankID = (int)ss.ID; ss.RemoveData(0, tankName.Length); lock (TheWorld) { /*This sets up the tank, sets the cooldown frames so it can fire, adds a filler command to the dictionary, and * spawns the tank at a random location.*/ Tank t = new Tank(tankName.Substring(0, tankName.Length - 1), tankID); TheWorld.UpdateTank(t); TheWorld.TankSetCooldownFrames(t.ID, FramesPerShot); TheWorld.UpdateCommand(tankID, new ControlCommands()); SpawnTank(t); Console.WriteLine("Player(" + tankID + ") " + "\"" + t.Name + "\" joined"); } //Changes the delegate ss.OnNetworkAction = ReceiveCommandData; //Sends the tank ID and the world size string message = tankID + "\n" + UniverseSize.ToString() + "\n"; if (!Networking.Send(ss.TheSocket, message)) { Console.WriteLine("Error occured while sending data"); } //Sends the walls to the client lock (TheWorld) { StringBuilder wallMessage = new StringBuilder(); foreach (Wall w in TheWorld.Walls.Values) { wallMessage.Append(JsonConvert.SerializeObject(w) + "\n"); } if (!Networking.Send(ss.TheSocket, wallMessage.ToString())) { Console.WriteLine("Error occured while sending data"); } } //Adds the socket state to the list of connections SocketConnections.Add(ss); Networking.GetData(ss); }
/// <summary> /// Method for sending the data to the client sockets. It goes through the list of tanks, powerups, projectiles, and beams /// and appends them to a stringbuilder to send. It also handles sockets that have disconnected. /// </summary> private static void SendDataToSockets() { lock (TheWorld) { foreach (SocketState s in SocketConnections.ToList()) { if (!s.TheSocket.Connected) { int tankID = (int)s.ID; //Makes sure that the dictionary contains the right key. If not, the tank must have died and rage quit if (TheWorld.Tanks.ContainsKey(tankID)) { Console.WriteLine("Player(" + tankID + ") " + "\"" + TheWorld.Tanks[(int)s.ID].Name + "\" disconnected"); TheWorld.TankDisconnect(tankID); TheWorld.TankKill(tankID); } if (TheWorld.DeadTanks.ContainsKey(tankID)) { Console.WriteLine("Player(" + tankID + ") " + "\"" + TheWorld.DeadTanks[(int)s.ID].Name + "\" disconnected"); TheWorld.TankDeadRemove(tankID); } SocketConnections.Remove(s); } } foreach (SocketState s in SocketConnections.ToList()) { StringBuilder frameMessage = new StringBuilder(); lock (TheWorld) { foreach (Tank t in TheWorld.Tanks.Values) { frameMessage.Append(JsonConvert.SerializeObject(t) + "\n"); } foreach (PowerUp p in TheWorld.PowerUps.Values) { frameMessage.Append(JsonConvert.SerializeObject(p) + "\n"); } foreach (Projectile p in TheWorld.Projectiles.Values) { frameMessage.Append(JsonConvert.SerializeObject(p) + "\n"); } foreach (Beam b in TheWorld.Beams.Values) { frameMessage.Append(JsonConvert.SerializeObject(b) + "\n"); } } if (!Networking.Send(s.TheSocket, frameMessage.ToString())) { Console.WriteLine("Error occured while sending data"); } } } }
private void SendData() { while (true) { List <SocketState> playerSocketCopy = new List <SocketState>(playerSockets.Values); StringBuilder sb = new StringBuilder(); List <long> listToRemove = new List <long>(); var prev = DateTime.Now; //For every socket connected to this server send a frame foreach (SocketState state in playerSocketCopy) { foreach (Tank t in world.GetTanks()) { t.projectileDelay += 1; } if (powerUpTracker < powerupCount) { timeSinceLastPowerUp += FPS; } if (timeSinceLastPowerUp > randPowerupTime) { SpawnPowerup(); randPowerupTime = rand.Next(0, maxPowerupSpawnTime); timeSinceLastPowerUp = 0; } //Seems innefficient to do this for every tank but.. TODO lock (world) { foreach (Tank t in world.GetTanks()) { sb.Append(JsonConvert.SerializeObject(t) + "\n"); if (t.IsDead()) { RespawnTank(t); } if (t.IsDisconnected()) { listToRemove.Add(t.GetID()); } } foreach (long ID in listToRemove) { world.RemoveTankByID((int)ID); } listToRemove.Clear(); foreach (Projectile p in world.GetProjectiles()) { MoveProjectile(p); sb.Append(JsonConvert.SerializeObject(p) + "\n"); if (p.IsDead()) { listToRemove.Add(p.GetID()); } } foreach (long ID in listToRemove) { world.RemoveProjectile(ID); } listToRemove.Clear(); foreach (Beam b in world.GetBeams()) { sb.Append(JsonConvert.SerializeObject(b) + "\n"); listToRemove.Add(b.GetID()); } foreach (long ID in listToRemove) { world.RemoveBeam(ID); } listToRemove.Clear(); foreach (Powerup up in world.GetPowerups()) { sb.Append(JsonConvert.SerializeObject(up) + "\n"); if (up.IsDead()) { listToRemove.Add(up.GetID()); } } if (!working) { foreach (long ID in listToRemove) { world.RemovePowerup(ID); powerUpTracker--; } } } listToRemove.Clear(); List <SocketState> mySockets = new List <SocketState>(playerSockets.Values); foreach (SocketState player in mySockets) { Networking.Send(player.TheSocket, sb.ToString()); } //Console.WriteLine(sb.ToString()); sb.Clear(); } //managing the framerate for when this loop repeats var now = DateTime.Now; if (now <= prev.AddMilliseconds(FPS)) { TimeSpan timeSpan = prev.AddMilliseconds(FPS) - now; System.Threading.Thread.Sleep((int)timeSpan.TotalMilliseconds); } prev = DateTime.Now; } }
/// <summary> /// Invoked every iteration through the frame loop. /// Update World, then sends it to each client. /// </summary> private static void Update() { // Updated world StringBuilder sb = new StringBuilder(); lock (World) { // For all Stars in World foreach (Star star in World.Stars.Values) { // Append each Star sb.Append(JsonConvert.SerializeObject(star) + "\n"); // Destroy all Ships colliding with Star foreach (Ship ship in World.Ships.Values) { Vector2D distance = star.loc - ship.loc; if (distance.Length() < StarSize) { ship.hp = 0; if (GAMEMODE_RandomDeadlyStar) { ship.score--; } ship.alive = false; DeadShips.Add(ship); } } // Destroy all Projectiles colliding with Star foreach (Projectile p in World.Projectiles.Values) { Vector2D distance = star.loc - p.loc; if (distance.Length() < StarSize) { p.alive = false; DeadProjectiles.Add(p.ID); } } } // For all Ships in World foreach (Ship ship in World.Ships.Values) { // Thrust Ship if (ship.up) { ship.thrust = true; ship.velocity += Acceleration(Thrust(ship.dir), Gravity(ship.loc)); ship.loc += ship.velocity; ship.up = false; } // Non-thrusting Ship else { ship.velocity += Gravity(ship.loc); ship.loc += ship.velocity; ship.thrust = false; } // Rotate Left if (ship.left) { ship.dir = RotateLeft(ship.dir); ship.left = false; } // Rotate right if (ship.right) { ship.dir = RotateRight(ship.dir); ship.right = false; } // Fire projectile if (ship.space && ship.alive) { // Begin spacing projectiles ship.projectileTimer++; if (ship.projectileTimer >= ProjectileFiringDelay) { Projectile p = new Projectile(); p.ID = ProjectileID; p.loc = ship.loc; p.dir = ship.dir; p.alive = true; p.owner = ship.ID; p.velocity = (p.dir * ProjectileSpeed) + ship.velocity; World.Projectiles.Add(p.ID, p); ship.projFired++; ship.projectileTimer = 0; ProjectileID++; } ship.space = false; } // If Ship exits World's bounds if (ship.loc.GetX() >= UniverseSize / 2 || ship.loc.GetX() <= -(UniverseSize / 2) || ship.loc.GetY() >= UniverseSize / 2 || ship.loc.GetY() <= -(UniverseSize / 2)) { // Wrap around Ship ship.loc = WrapAround(ship.loc); } // Destroy all Projectiles colliding with Ship and vise-versa foreach (Projectile p in World.Projectiles.Values) { Vector2D distance = ship.loc - p.loc; if (distance.Length() < ShipSize && ship.ID != p.owner && ship.alive) { p.alive = false; ship.hp--; ShipHits.Add(World.Ships[p.owner]); if (ship.hp <= 0) { ship.alive = false; DeadShips.Add(ship); ScoringShips.Add(p.owner); } DeadProjectiles.Add(p.ID); } } sb.Append(JsonConvert.SerializeObject(ship) + "\n"); } // For all Projectiles in World foreach (Projectile p in World.Projectiles.Values) { // Update projectile location with constant velocity p.loc += p.velocity; // If Projectile exits World's bounds if (p.loc.GetX() >= UniverseSize / 2 || p.loc.GetX() <= -(UniverseSize / 2) || p.loc.GetY() >= UniverseSize / 2 || p.loc.GetY() <= -(UniverseSize / 2)) { // Destroy Projectile p.alive = false; DeadProjectiles.Add(p.ID); } sb.Append(JsonConvert.SerializeObject(p) + "\n"); } // All Projectiles-to-destroy are removed from World foreach (int i in DeadProjectiles) { World.Projectiles.Remove(i); } DeadProjectiles.Clear(); // All Ships-to-score have score increased foreach (int i in ScoringShips) { Ship s = World.Ships.Values.ElementAt(i); s.score++; } ScoringShips.Clear(); // All Ships-to-destroy are removed from World and respawned foreach (Ship ship in DeadShips) { if (ship.hp <= 0) { // Incremental value has a further effect on respawn rate ship.respawnTimer += 20; } if (ship.respawnTimer >= RespawnDelay * TimePerFrame) { ship.respawnTimer = 0; ResetShip(ship); ResurrectedShips.Add(ship); } } // Respawn dead Ship foreach (Ship ship in ResurrectedShips) { DeadShips.Remove(ship); } ResurrectedShips.Clear(); // Add hit projectiles to Ship foreach (Ship ship in ShipHits) { World.Ships[ship.ID].projHit++; } ShipHits.Clear(); // Each client is updated with appended World information foreach (SocketState state in Clients) { Networking.Send(state.theSocket, sb.ToString()); } } }