/// <summary> /// Calculates the Y velocity accounting for jumping and /// animates accordingly. /// </summary> /// <remarks> /// During the accent of a jump, the Y velocity is completely /// overridden by a power curve. During the decent, gravity takes /// over. The jump velocity is controlled by the jumpTime field /// which measures time into the accent of the current jump. /// </remarks> /// <param name="velocityY"> /// The player's current velocity along the Y axis. /// </param> /// <returns> /// A new Y velocity if beginning or continuing a jump. /// Otherwise, the existing Y velocity. /// </returns> protected float DoJump(float velocityY, GameTime gameTime) { // If the player wants to jump if (IsJumping) { // Begin or continue a jump if ((!WasJumping && IsOnGround) || JumpTime > 0.0f) { if (JumpTime == 0) JumpDirection = GravityDirection; //Set the direction this jump started from if (JumpTime == 0 && IsMine) { //Send message we are now jumping PlayerStateMessage msg = new PlayerStateMessage(this); msg.IsJumping = true; Game.NetManager.Send(msg); } JumpTime += (float)gameTime.ElapsedGameTime.TotalSeconds; //Incriment jump timer } //If we are in the ascent of the jump if (0.0f < JumpTime && JumpTime <= MaxJumpTime) { //Fully override the vertical velocity with a power curve that gives players more control over the top of the jump velocityY = (JumpDirection == GravityDirection.Up || JumpDirection == GravityDirection.Left ? -1 : 1) * (JumpLaunchVelocity * (1.0f - (float)Math.Pow(JumpTime / MaxJumpTime, JumpControlPower))); } else { if (JumpTime > 0 && IsMine) { //Tell others we are falling now PlayerStateMessage msg = new PlayerStateMessage(this); msg.IsJumping = false; Game.NetManager.Send(msg); } JumpTime = 0.0f; // Reached the apex of the jump IsJumping = false; } } else { //Tell others we have landed if (JumpTime > 0 && IsMine) Game.NetManager.Send(new PlayerStateMessage(this)); // Continues not jumping or cancels a jump in progress JumpTime = 0.0f; } WasJumping = IsJumping; return velocityY; }
/// <summary> /// Handles a data message (The bulk of all messages recieved, containing player movements, block places, etc) /// </summary> private void HandleDataMessage(NetIncomingMessage im) { MessageTypes messageType = (MessageTypes)im.ReadByte(); //Find the type of data message sent switch (messageType) { case MessageTypes.Lobby: //Lobby list packet { if (Game.CurrentGameState == GameState.Lobby) { LobbyScreen screen = MainWindow.ScreenManager.Current as LobbyScreen; LobbyMessage msg = new LobbyMessage(im); screen.Name = msg.ServerName; screen.Description = msg.Description; screen.Intro = msg.Intro; screen.Online = msg.Online; screen.Rooms = msg.Rooms; screen.Lobby.LoadRooms(); } break; } case MessageTypes.PlayerJoin: //Player join message { PlayerJoinMessage user = new PlayerJoinMessage(im); //Add player to map Map.Players.Add(new Player(Map, Map.Spawn, user.Username, user.ID) { Tint = user.Color }); //Add user to userlist (MainWindow.ScreenManager.Current as GameScreen).PlayerList.Items.Add(user.Username); if (user.ID != Game.MyID && recievedInit) //Broadcast join message to chat { (MainWindow.ScreenManager.Current as GameScreen).SystemChat(user.Username + " [color:LightGray]has[/color] [color:LightGreen]joined.[/color]"); } if (user.Me) { //Let game know of it's own player Game.MyID = user.ID; Game.MyIndex = (byte)Map.Players.IndexOf(Map.Players.First(x => x.ID == Game.MyID)); Game.Me.Tint = Game.MyColor; } break; } case MessageTypes.PlayerLeave: //Player leave message { PlayerLeaveMessage user = new PlayerLeaveMessage(im); //Remove player if (user.ID != Game.MyID) { Player player = (Player)Map.PlayerFromID(user.ID); Map.Players.Remove((Player)Map.PlayerFromID(user.ID)); (MainWindow.ScreenManager.Current as GameScreen).PlayerList.Items.Remove(player.Username); (MainWindow.ScreenManager.Current as GameScreen).SystemChat(player.Username + " [color:LightGray]has[/color] [color:IndianRed]left.[/color]"); //Rebuild indexes for (int i = 0; i < Map.Players.Count; i++) { Map.Players[i].Index = i; if (Map.Players[i].ID == Game.MyID) Game.MyIndex = (byte)i; } } break; } case MessageTypes.PlayerStatus: //Player move message { if (Game.CurrentGameState == GameState.Game) { PlayerStateMessage user = new PlayerStateMessage(im); Player player = (Player)Map.PlayerFromID(user.ID); player.SimulationState.Position = user.Position.ToVector2(); player.SimulationState.Velocity = user.Velocity.ToVector2(); player.SimulationState.Movement = user.Movement.ToVector2(); if (!recievedInit) //If we have not recieved init (meaning we are not in game yet), set the initial positions directly so interpolation doesn't mess with them player.DisplayState.Position = player.SimulationState.Position; player.VirtualJump = user.IsJumping; } break; } case MessageTypes.Block: //Block place message { if (Game.CurrentGameState == GameState.Game) { BlockMessage msg = new BlockMessage(im); BlockType block = BlockType.FromID(msg.BlockID); if (Map.CanPlaceBlock(msg.X, msg.Y, msg.Z, block)) Map.Tiles[msg.X, msg.Y, msg.Z].Block = block; } break; } case MessageTypes.PlayerSmiley: //Smiley change message { if (Game.CurrentGameState == GameState.Game) { PlayerSmileyMessage msg = new PlayerSmileyMessage(im); Player p = (Player)Map.PlayerFromID(msg.ID); p.Smiley = msg.Smiley; } break; } case MessageTypes.PlayerMode: //Player mode change message { if (Game.CurrentGameState == GameState.Game) { PlayerModeMessage mode = new PlayerModeMessage(im); Map.PlayerFromID(mode.ID).Mode = (PlayerMode)mode.Mode; } break; } case MessageTypes.Chat: //Chat message { if (Game.CurrentGameState == GameState.Game) { ChatMessage chat = new ChatMessage(im); (MainWindow.ScreenManager.Current as GameScreen).AddChat(Map.PlayerFromID(chat.ID).Username, chat.Message); } break; } case MessageTypes.Init: //Initialization message with world data { Game.Map = new Bricklayer.Client.World.Map(game, 1, 1); InitMessage msg = new InitMessage(im, Map); Game.Map.Minimap = new Minimap(Game.Map, msg.Width, msg.Height) { Position = new Microsoft.Xna.Framework.Vector2(8, 8) }; Game.Map.CreateCamera(); Game.CurrentGameState = GameState.Game; (MainWindow.ScreenManager.Current as GameScreen).Show(); (MainWindow.ScreenManager.Current as GameScreen).SystemChat("Connected to [color:LightGray]" + Game.Host + ":" + Game.Port + "[/color]"); recievedInit = true; break; } } }
/// <summary> /// Handles all actions for recieving data messages (Such as movement, block placing, etc) /// </summary> /// <param name="inc">The incoming message</param> private void ProcessDataMessage(NetIncomingMessage inc) { Player sender = Server.PlayerFromRUI(inc.SenderConnection.RemoteUniqueIdentifier, true); Map map = null; if (sender != null) map = (Map)sender.Map; MessageTypes type = (MessageTypes)Enum.Parse(typeof(MessageTypes), inc.ReadByte().ToString()); switch (type) { //When a player request to recieve a message case MessageTypes.Request: { MessageTypes request = new RequestMessage(inc).RequestType; //Request to recieve lobby data if (request == MessageTypes.Lobby) { List<LobbySaveData> rooms = new List<LobbySaveData>(); foreach (Map m in Maps) { rooms.Add(LobbySaveData.FromMap(m)); } LobbyMessage msg = new LobbyMessage(Server.Config.Name, Server.Config.Decription, Server.Config.Intro, NetworkManager.NetServer.ConnectionsCount, rooms); NetManager.Send(msg, inc.SenderConnection); } break; } //When a player exits to the lobby case MessageTypes.PlayerLeave: { PlayerLeaveMessage user = new PlayerLeaveMessage(inc); user.ID = sender.ID; Log.WriteLine(LogType.Room, "{0} has left: {1}", sender.Username, sender.Map.Name); //Remove player map.Players.Remove(sender); RebuildIndexes(map); //Send to players NetManager.Broadcast(sender.Map, new PlayerLeaveMessage(sender.ID)); IO.SaveMap((Bricklayer.Server.World.Map)sender.Map); sender = null; break; } //When a player moves case MessageTypes.PlayerStatus: { PlayerStateMessage state = new PlayerStateMessage(inc); state.ID = sender.ID; //Clamp position in bounds state.Position = new Point((int)MathHelper.Clamp(state.Position.X, Tile.Width, (map.Width * Tile.Width) - (Tile.Width * 2)), (int)MathHelper.Clamp(state.Position.Y, Tile.Height, (map.Height * Tile.Height) - (Tile.Height * 2))); sender.SimulationState.Position = state.Position.ToVector2(); sender.SimulationState.Velocity = state.Velocity.ToVector2(); sender.SimulationState.Movement = state.Movement.ToVector2(); sender.IsJumping = state.IsJumping; NetManager.BroadcastExcept(state, sender); break; } //When a player places a block case MessageTypes.Block: { BlockMessage state = new BlockMessage(inc); BlockType block = BlockType.FromID(state.BlockID); //Verify Block (Make sure it is in bounds and it has changed, no use sending otherwise) //TODO: Punish players spamming invalid data (Because official client should never send it) if (map.InBounds(state.X, state.Y, state.Z) && map.Tiles[state.X, state.Y, state.Z].Block.ID != block.ID) { map.Tiles[state.X, state.Y, state.Z].Block = block; NetManager.Broadcast(sender.Map, state); } break; } //When a player sends a chat message case MessageTypes.Chat: { ChatMessage chat = new ChatMessage(inc); chat.ID = sender.ID; //Verify length if (chat.Message.Length > ChatMessage.MaxLength) chat.Message = chat.Message.Truncate(ChatMessage.MaxLength); NetManager.BroadcastExcept(chat, sender); //Log message Log.WriteLine(LogType.Chat, ConsoleColor.Gray, "<{0}> {1}", sender.Username, chat.Message); break; } //When a player changes smiley case MessageTypes.PlayerSmiley: { PlayerSmileyMessage smiley = new PlayerSmileyMessage(inc); smiley.ID = sender.ID; if (sender.Smiley != smiley.Smiley) { sender.Smiley = smiley.Smiley; NetManager.BroadcastExcept(smiley, sender); } break; } //When a player changes mode (Ex: godmode to normal case MessageTypes.PlayerMode: { PlayerModeMessage mode = new PlayerModeMessage(inc); mode.ID = sender.ID; if (sender.Mode != mode.Mode) { sender.Mode = mode.Mode; NetManager.BroadcastExcept(mode, sender); } break; } //When a player requests to join a room case MessageTypes.JoinRoom: { if (sender == null) //If the sender isn't null, then they are already in a room { JoinRoomMessage msg = new JoinRoomMessage(inc); int newMap = Maps.IndexOf(Server.MapFromID(msg.ID)); LoginMessage login = Server.Logins[inc.SenderConnection.RemoteUniqueIdentifier]; //Fetch stored login from dictionary Maps[newMap].Players.Add(new Player(Maps[newMap], Maps[newMap].Spawn, login.Username, inc.SenderConnection.RemoteUniqueIdentifier, Server.FindEmptyID(Maps[newMap])) { Tint = login.Color }); sender = Server.PlayerFromRUI(inc.SenderConnection.RemoteUniqueIdentifier, true); NetManager.Send(new InitMessage(sender.Map), sender); //Send message to player notifing he is connected and ready NetManager.Send(new PlayerJoinMessage(sender.Username, sender.ID, true, sender.Tint), sender); //Log message Log.WriteLine(LogType.Room, "{0} joined: {1}", login.Username, Maps[newMap].Name); //Send message to everyone notifying of new user NetManager.BroadcastExcept(new PlayerJoinMessage(sender.Username, sender.ID, false, sender.Tint), sender); //Let new player know of all existing players and their states (Mode, Position, Smiley) foreach (Player player in sender.Map.Players) { if (player.ID != sender.ID) { NetManager.Send(new PlayerJoinMessage(player.Username, player.ID, false, player.Tint), sender); NetManager.Send(new PlayerStateMessage(player), sender); if (player.Mode != PlayerMode.Normal) NetManager.Send(new PlayerModeMessage(player), sender); if (player.Smiley != SmileyType.Default) NetManager.Send(new PlayerSmileyMessage(player, player.Smiley), sender); } } } break; } //When a player requests to make a room case MessageTypes.CreateRoom: //If the sender isn't null, then they are already in a room { if (sender == null) { CreateRoomMessage msg = new CreateRoomMessage(inc); Map newMap = Server.CreateMap(msg.Name, msg.Description); LoginMessage login = Server.Logins[inc.SenderConnection.RemoteUniqueIdentifier]; //Fetch stored login from dictionary newMap.Players.Add(new Player(newMap, newMap.Spawn, login.Username, inc.SenderConnection.RemoteUniqueIdentifier, Server.FindEmptyID(newMap)) { Tint = login.Color }); sender = Server.PlayerFromRUI(inc.SenderConnection.RemoteUniqueIdentifier, true); //Send message to player notifing he is connected and ready NetManager.Send(new InitMessage(sender.Map), sender); NetManager.Send(new PlayerJoinMessage(sender.Username, sender.ID, true, sender.Tint), sender); //Log message Log.WriteLine(LogType.Room, "{0} created room: {1}", login.Username, newMap.Name); } break; } } }