/// <summary> /// Ping timer event to test the status of every client /// </summary> private void _pingTimer_Elapsed(object sender, ElapsedEventArgs e) { PacketBuilder pb = new PacketBuilder(PacketFamily.CONNECTION, PacketAction.PING); DateTime now = DateTime.Now.ToUniversalTime(); string nowString = now.ToString("hh.mm.ss.ffffff"); pb = pb.AddInt(nowString.Length).AddString(nowString); List <Client> disconnectedClients = new List <Client>(); lock (Clients) { foreach (Client client in Clients) { //if we're still waiting from 10s ago for the pong from the client if (client.NeedPong) { //we add it to the list to be terminated disconnectedClients.Add(client); } else { //if we received the last pong that we need, we're going to send another ping client.Send(pb.Build()); client.NeedPong = true; } } } //removing every client that failed to check in with the server foreach (Client client in disconnectedClients) { client.Stop(); } }
private void _enemyUpdateTimer_Elapsed(object sender, ElapsedEventArgs e) { foreach (Enemy enemy in Enemies) { //if the enemy moved if (enemy.IdleMove()) { PacketBuilder pb = new PacketBuilder(PacketFamily.ENEMY, PacketAction.MOVE); pb = pb.AddInt(Enemies.IndexOf(enemy)) .AddInt(enemy.X) .AddInt(enemy.Y) .AddByte((byte)enemy.Facing); //notify every client that the enemy has moved lock (Clients) { foreach (Client client in Clients) { if (client.Account != null && client.Account.State == AccountState.Playing) { client.Send(pb.Build()); } } } } } }
public void Announce(string message) { PacketBuilder pb = new PacketBuilder(PacketFamily.SERVER, PacketAction.TALK); pb = pb.AddInt(message.Length) .AddString(message); foreach (Client client in Clients) { client.Send(pb.Build()); } }
/// <summary> /// Method to handle logging out of the character /// </summary> /// <param name="character">Character to be logged out</param> public void LogOut(PlayerCharacter character) { //saves the character state character.Save(_database); character.ServerID = 0; PacketBuilder pb = new PacketBuilder(PacketFamily.PLAYER, PacketAction.LOGOUT); pb = pb.AddInt(character.ServerID); foreach (Client client in _server.Clients) { if (client.Socket.Connected) { //lets all the connected clients know that this character has logged out if (client != this) { client.Send(pb.Build()); } } } }
/// <summary> /// The purpose of this function is to provide one area where the packet data can be handled. /// I was looking to improve this approach by giving each packet its own class that handles /// that type of packet. That would improve the clarity of this class. /// </summary> /// <param name="pkt">The packet to be handled</param> private void HandlePacket(Packet pkt) { try { //packet builder for the return packet PacketBuilder pb; if (pkt.Family == PacketFamily.LOGIN) { if (pkt.Action == PacketAction.REQUEST) { //login request string user = pkt.ReadString(pkt.ReadByte()); string pass = pkt.ReadString(pkt.ReadByte()); pass = Utils.Security.GetHashString(pass + "PROCO304" + user); CheckLogin(user, pass); } } else if (pkt.Family == PacketFamily.CHARACTER) { if (pkt.Action == PacketAction.CREATE) { //for character creation, the first thing we must do is get the requested name string characterName = pkt.ReadString(pkt.ReadByte()); //and then the requested gender byte gender = pkt.ReadByte(); //and initialise whether the name is available or not bool isAvailable = true; foreach (Account account in _server.Accounts) { foreach (Character c in account.Characters) { //loop through every character known to the server if (c.Name == characterName) { //if the name already exists, then the requested name cannot be used isAvailable = false; break; } } } if (!isAvailable) { //relay back to the client to tell them that the name for the character already exists pb = new PacketBuilder(PacketFamily.CHARACTER, PacketAction.ALREADY_EXISTS); string errMsg = "Character already exists"; pb = pb.AddByte((byte)errMsg.Length).AddString(errMsg); Send(pb.Build()); } else { //if they can have the requested character name then we add it to the collection of characters PlayerCharacter character = new PlayerCharacter() { Name = characterName, Dexterity = 0, Strength = 0, Vitality = 0, Intelligence = 0, Gender = (Gender)gender, Facing = 0, EXP = 0, Health = 50, Level = 0, X = 0, Y = 0 }; //and let the client know that they have been accepted pb = new PacketBuilder(PacketFamily.CHARACTER, PacketAction.CREATED); pb = pb.AddByte((byte)characterName.Length) .AddString(characterName) .AddByte((byte)character.Gender); Send(pb.Build()); Account.Characters.Add(character); //save the new character to the database also character.CreateDatabaseEntry(_database, Account.Username); //and then put it out to the server console Logger.Log("Character Created. Name: " + character.Name); } } else if (pkt.Action == PacketAction.DELETE) { int userid = pkt.ReadByte(); PlayerCharacter player = Account.GetCharacter(userid); foreach (Account account in _server.Accounts) { PlayerCharacter toRemove = null; foreach (PlayerCharacter character in account.Characters) { if (character.Name == player.Name) { toRemove = character; } } if (toRemove != null) { account.Characters.Remove(toRemove); } } player.Delete(_database); Account.Characters.Remove(player); Send(pkt); } } else if (pkt.Family == PacketFamily.REGISTER) { if (pkt.Action == PacketAction.REQUEST) { //registering to the game //getting the username string username = pkt.ReadString(pkt.ReadByte()); //getting the password string password = pkt.ReadString(pkt.ReadByte()); //hashing the given password using the username and "PROCO304" as the salt password = Utils.Security.GetHashString(password + "PROCO304" + username); //read the given email string email = pkt.ReadString(pkt.ReadByte()); //and the given name string fullname = pkt.ReadString(pkt.ReadByte()); //output the requested account to the server console Logger.Log(String.Format("Requested username {0} password {1} email {2}", username, password, email)); //initiate the test to see if the account username already exists bool userExists = false; foreach (Account account in _server.Accounts) { if (account.Username == username) { //if the requested account name already exists in the known acccounts list then we need to reject the request //and post back to the client to tell them that the username is already taken. pb = new PacketBuilder(PacketFamily.REGISTER, PacketAction.REJECT); string errMsg = "User already exists"; pb = pb.AddByte((byte)errMsg.Length).AddString(errMsg); Send(pb.Build()); userExists = true; break; } } if (!userExists) { //if the account isn't already taken then we can accept the registration request, //create the new entry in the database and report back to the client to tell them that //their account is ready to go pb = new PacketBuilder(PacketFamily.REGISTER, PacketAction.ACCEPT); pb = pb.AddByte((byte)username.Length).AddString(username); Send(pb.Build()); Account account = new Account(username, password, email, fullname); _server.Accounts.Add(account); account.CreateDatabaseEntry(_database, Socket.Client.RemoteEndPoint.ToString().Split(":")[0]); Logger.Log("Account Created. Username: "******" has gained " + enemy.EXP + " EXP"); } enemy.Contributors = new List <PlayerCharacter>(); //reset position enemy.X = enemy.SpawnX + RNG.Next(-300, 300); enemy.Y = enemy.SpawnY + RNG.Next(-300, 300); enemy.Health = enemy.MaxHealth; //let all clients know of new poisition pb = new PacketBuilder(PacketFamily.ENEMY, PacketAction.MOVE); pb = pb.AddInt(_server.Enemies.IndexOf(enemy)) .AddInt(enemy.X) .AddInt(enemy.Y) .AddByte((byte)enemy.Facing); foreach (Client client in _server.Clients) { client.Send(pb.Build()); } } //notify all clients of new enemy hp pb = new PacketBuilder(PacketFamily.ENEMY, PacketAction.TAKE_DAMAGE) .AddInt(_server.Enemies.IndexOf(enemy)) .AddInt(enemy.Health); foreach (Client client in _server.Clients) { client.Send(pb.Build()); } } } } } else if (pkt.Family == PacketFamily.CONNECTION) { if (pkt.Action == PacketAction.PONG) { //Good client, responded to the PING request. We no longer need the pong. NeedPong = false; } } } catch (Exception e) { Logger.Warn("Packet handling error. Remote endpoint: " + Socket.Client.RemoteEndPoint + "\n\r StackTrace:" + e.StackTrace + "\n\r Message:" + e.Message); } }
public override void Update(GameTime gameTime) { _pingLabel.Update(gameTime); _camera.Update(gameTime); if (_characterSprites.Count > 0 && _camera.Focus != _characterSprites[0]) { _camera.Focus = _characterSprites[0]; } _thisPlayer.PlayerCharacter.IsAttacking = false; bool moved = false; if (!_txtIn.HasFocus) { //handling directional input if (Keyboard.GetState().IsKeyDown(Keys.A)) { _characterSprites[0].PlayerCharacter.X -= 1; _characterSprites[0].PlayerCharacter.Facing = Common.Enums.Direction.LEFT; moved = true; } else if (Keyboard.GetState().IsKeyDown(Keys.S)) { _characterSprites[0].PlayerCharacter.Y += 1; _characterSprites[0].PlayerCharacter.Facing = Common.Enums.Direction.DOWN; moved = true; } else if (Keyboard.GetState().IsKeyDown(Keys.D)) { _characterSprites[0].PlayerCharacter.X += 1; _characterSprites[0].PlayerCharacter.Facing = Common.Enums.Direction.RIGHT; moved = true; } else if (Keyboard.GetState().IsKeyDown(Keys.W)) { _characterSprites[0].PlayerCharacter.Y -= 1; _characterSprites[0].PlayerCharacter.Facing = Common.Enums.Direction.UP; moved = true; } else if (Keyboard.GetState().IsKeyUp(Keys.Enter) && _enterDown) { _txtIn.HasFocus = true; } if (Mouse.GetState().LeftButton == ButtonState.Pressed) { attackTimer += gameTime.ElapsedGameTime.Milliseconds; if (attackTimer > 500) { //sending attack command to the server, every 500 ms PacketBuilder pb = new PacketBuilder(PacketFamily.PLAYER, PacketAction.ATTACK); pb.AddInt(_gameClient.CharacterID); GameClient.NetClient.Send(pb.Build()); attackTimer = 0; _thisPlayer.PlayerCharacter.IsAttacking = true; } } else { attackTimer = 0; } // This is in place to stop server spam, otherwise every time the sprite is updated // it will send the server the characters x and y (Many times a second) if (moved) { PacketBuilder pb = new PacketBuilder(PacketFamily.PLAYER, PacketAction.MOVE); pb = new PacketBuilder(PacketFamily.PLAYER, PacketAction.MOVE) .AddInt(_characterSprites[0].PlayerCharacter.X) .AddInt(_characterSprites[0].PlayerCharacter.Y) .AddByte((byte)_characterSprites[0].PlayerCharacter.Facing) .AddInt(_gameClient.CharacterID); GameClient.NetClient.Send(pb.Build()); _serverNotifiedOfIdle = false; } //now we're settled and if the server doesn't know we've stopped we want to tell it that we have else if (!_serverNotifiedOfIdle) { PacketBuilder pb = new PacketBuilder(PacketFamily.PLAYER, PacketAction.STOP) .AddInt(_characterSprites[0].PlayerCharacter.X) .AddInt(_characterSprites[0].PlayerCharacter.Y) .AddByte((byte)_characterSprites[0].PlayerCharacter.Facing) .AddInt(_gameClient.CharacterID); GameClient.NetClient.Send(pb.Build()); _serverNotifiedOfIdle = true; } } else { if (Keyboard.GetState().IsKeyUp(Keys.Enter) && _enterDown) { if (_txtIn.Text != "") { //sending a chat message to the server PacketBuilder pb = new PacketBuilder(PacketFamily.PLAYER, PacketAction.TALK); pb = pb.AddInt(_txtIn.Text.Length) .AddString(_txtIn.Text) .AddInt(_gameClient.CharacterID); GameClient.NetClient.Send(pb.Build()); //debugging command to test health bars, will be removed if game goes live if (_txtIn.Text.Split(' ')[0] == "SetHP") { _characterSprites[0].PlayerCharacter.Health = Convert.ToInt32(_txtIn.Text.Split(' ')[1]); } _txtIn.Text = ""; } _txtIn.HasFocus = false; } else if (Keyboard.GetState().IsKeyDown(Keys.Escape)) { _txtIn.HasFocus = false; } } _characterSprites[0].PlayerCharacter.IsIdle = !moved; _characterSprites[0].PlayerCharacter.IsWalking = moved; foreach (PlayerCharacterSprite characterSprite in _characterSprites) { characterSprite.Update(gameTime, _camera); } foreach (EnemySprite enemySprite in _enemySprites) { enemySprite.Update(gameTime, _camera); } foreach (Label lbl in _lblMessages) { lbl.Update(gameTime); } _enterDown = Keyboard.GetState().IsKeyDown(Keys.Enter); _txtIn.Update(gameTime); base.Update(gameTime); }
public void Main() { while (ShouldReceive) { try { if (Stream == null) { return; } //the main function that receives packet data byte[] buffer = new byte[2]; int readTotal = 0; //reading the length of the packet Stream.Read(buffer, 0, 2); if (buffer[0] == 0) { continue; } byte length = buffer[0]; if (length < 2 && PACKET_DEBUG) { //we're not interested in any packets less than 2. //Could happen if a client other than a DansWorld client tries to connect. Logger.Error("Packet of length less than 2 received"); continue; } //initiating a new byte array of the packet length byte[] data = new byte[length]; //while there's still data to read while (readTotal < length) { //filling the byte array with the data read from the stream int read = Stream.Read(data, 0, length); if (read == 0 && PACKET_DEBUG) { Logger.Warn("Packet seems empty.."); return; } readTotal += read; } StringBuilder sb = new StringBuilder(); sb.Append("Packet Data: "); for (int i = 0; i < data.Length; i++) { sb.Append("{" + data[i] + "} "); } if (PACKET_DEBUG) { Logger.Log(sb.ToString()); } //building up the received packet data using the received byte array PacketBuilder pb = new PacketBuilder().AddBytes(data); Packet pkt = pb.Build(); //if we have an accepted login packet if (pkt.Family == PacketFamily.LOGIN) { if (pkt.Action == PacketAction.ACCEPT) { //debug output _gameClient.DisplayMessage("Login accepted"); //we're going to set the state of the client to logged in _gameClient.SetState(GameExecution.GameState.LoggedIn); //while we still have players to read the data of while (pkt.ReadPosition < pkt.RawData.Length) { int nameLength = pkt.ReadByte(); if (nameLength > 0) { PlayerCharacter c = new PlayerCharacter() { Name = pkt.ReadString(nameLength), Level = pkt.ReadByte(), Gender = (Gender)pkt.ReadByte() }; _gameClient.AddPlayerCharacter(c); } } } else { //if the login wasn't accepted, the person should get the reason as to why _gameClient.DisplayMessage(pkt.ReadString(pkt.Length - 2)); } } else if (pkt.Family == PacketFamily.REGISTER) { if (pkt.Action == PacketAction.ACCEPT) { //registration was complete, we can now go back to the main menu to log in to the created account _gameClient.SetState(GameExecution.GameState.MainMenu); _gameClient.DisplayMessage("Account created! Username: "******"" : p.Name)); } } //server sent an announcement else if (pkt.Family == PacketFamily.SERVER) { if (pkt.Action == PacketAction.TALK) { string message = pkt.ReadString(pkt.ReadInt()); _gameClient.DisplayMessage(message, "SERVER"); } } //if we got a ping request, we'll send back a pong request to satisfy the server else if (pkt.Family == PacketFamily.CONNECTION) { if (pkt.Action == PacketAction.PING) { PacketBuilder pBuilder = new PacketBuilder(PacketFamily.CONNECTION, PacketAction.PONG); DateTime now = DateTime.Now.ToUniversalTime(); DateTime fromServer = DateTime.ParseExact(pkt.ReadString(pkt.ReadInt()), "hh.mm.ss.ffffff", CultureInfo.InvariantCulture); TimeSpan t = now.Subtract(fromServer); string nowString = now.ToShortTimeString(); pBuilder = pBuilder.AddInt(nowString.Length).AddString(nowString); Send(pBuilder.Build()); _gameClient.ShowPing(t.Milliseconds); } } else if (pkt.Family == PacketFamily.ENEMY) { //an enemy has been added to the game if (pkt.Action == PacketAction.WELCOME) { Enemy enemy = new Enemy(); enemy.ID = pkt.ReadInt(); enemy.Name = pkt.ReadString(pkt.ReadByte()); enemy.Facing = (Direction)pkt.ReadByte(); enemy.X = pkt.ReadInt(); enemy.Y = pkt.ReadInt(); enemy.Vitality = pkt.ReadInt(); enemy.Level = pkt.ReadByte(); enemy.MaxHealth = pkt.ReadInt(); enemy.Health = pkt.ReadInt(); enemy.SpriteID = pkt.ReadInt(); enemy.ServerID = pkt.ReadByte(); _gameClient.AddEnemy(enemy); } //an enemy moved else if (pkt.Action == PacketAction.MOVE) { int id = pkt.ReadInt(); int x = pkt.ReadInt(); int y = pkt.ReadInt(); byte facing = pkt.ReadByte(); foreach (Enemy enemy in _gameClient.GetEnemies()) { if (enemy.ServerID == id) { enemy.X = x; enemy.Y = y; enemy.Facing = (Direction)facing; break; } } } //an enemy took damage else if (pkt.Action == PacketAction.TAKE_DAMAGE) { int id = pkt.ReadInt(); int hp = pkt.ReadInt(); foreach (Enemy enemy in _gameClient.GetEnemies()) { if (enemy.ID == id) { enemy.Health = hp; break; } } } } } catch (Exception e) { Logger.Error(e.Message + " Stack " + e.StackTrace); continue; } } }