using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Text; using System.Threading; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using SpacePew.Extensions; using Lidgren.Network; using System.Collections; using SpacePew.Models; using SpacePew.Common; using System.Net; namespace SpacePew.Networking { public class UdpClient : UdpBase { private string _playerName; private readonly NetClient _client; private Level _level; private readonly MainGame _game; private int _colorIndex; private IPEndPoint _masterServerEndpoint; private UdpClient() { } public UdpClient(MainGame game) { _game = game; _masterServerEndpoint = NetUtility.Resolve("spacepew.wodanaz.se", Constants.MasterServerPort); // TODO: Fixa upp masterserver någonstans EntitiesToSend = new List(); var configuration = new NetPeerConfiguration("SpacePew"); configuration.EnableMessageType(NetIncomingMessageType.DiscoveryRequest); configuration.EnableMessageType(NetIncomingMessageType.DiscoveryResponse); configuration.EnableMessageType(NetIncomingMessageType.UnconnectedData); configuration.EnableMessageType(NetIncomingMessageType.NatIntroductionSuccess); _client = new NetClient(configuration); _client.Start(); } public List EntitiesToSend { get; set; } public NetClient CurrentClient { get { return _client; } } private Player _localPlayer; public Player LocalPlayer { get { return _localPlayer; } set { _localPlayer = value; } } private readonly List _players = new List(); public IList Players { get { return _players; } } public bool IsSessionAlive { get { return _client != null && _client.ConnectionStatus == NetConnectionStatus.Connected; } } public void JoinSession(string host, string playerName) { JoinSession(new IPEndPoint(System.Net.Dns.GetHostAddresses(host)[0], SpacePew.Common.Constants.GameServerPort), playerName); } public void JoinSession(IPEndPoint endpoint, string playerName) { _playerName = playerName; var hailMessage = _client.CreateMessage(playerName); _client.Connect(endpoint, hailMessage); while (_client.ServerConnection == null) { // wait } System.Threading.Thread.Sleep(500); // TODO: nya lidgren är dumt i huvet, grotta i vad skiten sätter i någon tråd se'n System.Diagnostics.Trace.WriteLine("Got server connection..."); WriteLevelRequest(); WaitForLevel(); WriteJoiningMessage(); WaitForJoin(); if (_level.OggVorbisSong != null) { new Thread(_level.PlayLevelSong).Start(); } } public void ExitSession(string reason) { var message = _client.CreateMessage(); message.Write((int)UdpNetworkPacketType.PlayerDisconnecting); message.Write(_playerName); _client.SendMessage(message, NetDeliveryMethod.ReliableOrdered); _client.Disconnect(reason); } public void SendMessage(NetworkMessage message) { var netMessage = _client.CreateMessage(); netMessage.Write((int)UdpNetworkPacketType.MessageSent); netMessage.Write(message.Color.ToVector3()); netMessage.Write(message.Sent.ToString(CultureInfo.InvariantCulture)); netMessage.Write(message.Message); netMessage.Write(message.IsChatMessage); _client.SendMessage(netMessage, NetDeliveryMethod.UnreliableSequenced); } public void RequestScoreBoard() { var message = _client.CreateMessage(); message.Write((int)UdpNetworkPacketType.RequestingScoreboard); _client.SendMessage(message, NetDeliveryMethod.UnreliableSequenced); } private void ReadMessage(NetIncomingMessage netMessage) { var color = new Color(netMessage.ReadVector3()); var sent = DateTime.Parse(netMessage.ReadString(), CultureInfo.InvariantCulture); string message = netMessage.ReadString(); bool isChatMessage = netMessage.ReadBoolean(); NetworkMessenger.DisplayMessage(new NetworkMessage() { Color = color, Sent = sent, Message = message, IsChatMessage = isChatMessage}); } public void SendDeath(IEntity killedBy) { var message = _client.CreateMessage(); message.Write((int)UdpNetworkPacketType.PlayerDying); message.Write(_playerName); message.Write(killedBy.Owner); // TODO: server side score board _client.SendMessage(message, NetDeliveryMethod.ReliableUnordered); } private void WriteLevelRequest() { Trace.WriteLine("WriteLevelRequest"); var message = _client.CreateMessage(); message.Write((int)UdpNetworkPacketType.LevelRequest); _client.SendMessage(message, NetDeliveryMethod.ReliableUnordered); System.Diagnostics.Trace.WriteLine("Sent level request..."); } private void WriteJoiningMessage() { Trace.WriteLine("WriteJoiningMessage"); var message = _client.CreateMessage(); message.Write((int)UdpNetworkPacketType.PlayerJoining); message.Write(_playerName); _client.SendMessage(message, NetDeliveryMethod.ReliableUnordered); } private void WaitForJoin() { System.Diagnostics.Trace.WriteLine("WaitForJoin"); bool hasAnswer = false; while (!hasAnswer) // wait for answer { NetIncomingMessage message; while ((message = _client.ReadMessage()) != null) { if (message.MessageType == NetIncomingMessageType.Data) { var packetType = (UdpNetworkPacketType)message.ReadInt32(); if (packetType == UdpNetworkPacketType.PlayerJoined) { string owner = message.ReadString(); int colorIndex = message.ReadInt32(); Color color = _playerColors[colorIndex]; _colorIndex = Array.IndexOf(_playerColors, color); Vector2 pos = message.ReadVector2(); Vector2 velocity = message.ReadVector2(); float angle = message.ReadFloat(); int collisionSize = message.ReadInt32(); var collisionData = message.ReadBytes(collisionSize); _level.CollisionData = (bool[])ByteArrayToObject(collisionData); _level.BuildLevelFromCollisionData(); Player p = Player.CreatePlayer(pos, owner); p.Color = color; p.Position = pos; p.Velocity = velocity; p.Angle = angle; _localPlayer = p; _players.Add(p); hasAnswer = true; } } } } } public void Update() { NetIncomingMessage message; while ((message = _client.ReadMessage()) != null) { ReadBuffer(message); } if (EntitiesToSend.Count > 0) { WriteServerShots(); } WritePlayerUpdatePacket(); } private void WriteServerShots() { SendEntities(EntitiesToSend); } private void WritePlayerUpdatePacket() { var message = _client.CreateMessage(); message.Write((int)UdpNetworkPacketType.PlayerUpdate); message.Write(_localPlayer.Owner); message.Write(_colorIndex); message.Write(_localPlayer.Position); message.Write(_localPlayer.Velocity); message.Write(_localPlayer.Angle); message.Write(_localPlayer.Health); _client.SendMessage(message, NetDeliveryMethod.UnreliableSequenced); } private void ReadBuffer(NetIncomingMessage message) { switch (message.MessageType) { case NetIncomingMessageType.Data: { var packetType = (UdpNetworkPacketType)message.ReadInt32(); switch (packetType) { case UdpNetworkPacketType.PlayerUpdate: ReadPlayers(message); break; case UdpNetworkPacketType.EntitiesCreated: ReadEntities(message); break; case UdpNetworkPacketType.MessageReceived: ReadMessage(message); break; case UdpNetworkPacketType.PlayerDied: ReadPlayerDied(message); break; case UdpNetworkPacketType.SendingScoreBoard: ReadScoreBoard(message); break; case UdpNetworkPacketType.EntityCreated: ReadEntity(message); break; case UdpNetworkPacketType.PlayerDisconnected: RemovePlayer(message); break; case UdpNetworkPacketType.LevelResponse: ReadLevel(message); break; } } break; } _client.Recycle(message); } private void WaitForLevel() { System.Diagnostics.Trace.WriteLine("WaitForLevel"); while (_level == null) // wait for level { NetIncomingMessage message; while ((message = _client.ReadMessage()) != null) { if (message.MessageType == NetIncomingMessageType.Data) { var packetType = (UdpNetworkPacketType)message.ReadInt32(); if (packetType == UdpNetworkPacketType.LevelResponse) { ReadLevel(message); } } } } } private static ulong _levelLength; private static ulong _levelReceived; private static FileStream _levelWriteStream; private static int _levelTimeStarted; string levelPath = string.Empty; private void ReadLevel(NetIncomingMessage message) { int chunkLen = message.LengthBytes; if (_levelLength == 0) { _levelLength = message.ReadUInt64(); string filename = message.ReadString(); var downloadLevelFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Downloads\\Levels\\"); levelPath = Path.Combine(downloadLevelFolder, filename); _levelWriteStream = new FileStream(levelPath, FileMode.Create, FileAccess.Write, FileShare.None); _levelTimeStarted = Environment.TickCount; return; } byte[] all = message.ReadBytes(message.LengthBytes - 4); // offset for UdpNetworkPacketType _levelReceived += (ulong)all.Length; _levelWriteStream.Write(all, 0, all.Length); //int v = (int)(((float)_levelReceived / (float)_levelLength) * 100.0f); //int passed = Environment.TickCount - _levelTimeStarted; //double psec = (double)passed / 1000.0; //double bps = (double)_levelReceived / psec; //var passedText = NetUtility.ToHumanReadable((long)bps) + " per second"; if (_levelReceived >= _levelLength) { Trace.WriteLine("Got level"); _levelWriteStream.Flush(); _levelWriteStream.Close(); _levelWriteStream.Dispose(); _level = LevelLoader.LoadLevel(levelPath, _game.Content, _game.GraphicsDevice); _game.Level = _level; } } private void RemovePlayer(NetIncomingMessage message) { string name = message.ReadString(); Player player = _players.Find(p => p.Owner == name); if (player != null) { EntityFactory.Instance.RemoveEntity(player); NetworkMessenger.DisplayMessage(new NetworkMessage() { Color = player.Color, Message = string.Format("{0} has left.", player.Owner), Sent = DateTime.Now }); _players.Remove(player); } } private void ReadScoreBoard(NetIncomingMessage message) { int count = message.ReadInt32(); var scoreBoard = new List(); for (int i = 0; i < count; i++) { var item = new ScoreBoardItem { Name = message.ReadString(), Kills = message.ReadInt32(), Deaths = message.ReadInt32(), Joined = DateTime.Parse(message.ReadString(), CultureInfo.InvariantCulture), Ping = message.ReadInt64() }; scoreBoard.Add(item); } ScoreBoard.CurrentScoreBoard = scoreBoard; } private void ReadPlayerDied(NetIncomingMessage message) { string playerName = message.ReadString(); Player player = _players.Find(p => p.Owner == playerName); if (player != null) { _game.AddExplosion(player.Position, 1f); player.Kill(); } } private void ReadEntities(NetIncomingMessage message) { int count = message.ReadInt32(); for (int i = 0; i < count; i++) { string entityType = message.ReadString(); string owner = message.ReadString(); Vector2 pos = message.ReadVector2(); Vector2 velocity = message.ReadVector2(); float angle = message.ReadFloat(); EntityFactory.Instance.CreateEntity(Type.GetType(entityType), owner, pos, velocity, angle); } } private void ReadEntity(NetIncomingMessage message) { string entityType = message.ReadString(); string owner = message.ReadString(); Vector2 pos = message.ReadVector2(); Vector2 velocity = message.ReadVector2(); float angle = message.ReadFloat(); EntityFactory.Instance.CreateEntity(Type.GetType(entityType), owner, pos, velocity, angle); } private void ReadPlayers(NetIncomingMessage message) { string owner = message.ReadString(); int colorIndex = message.ReadInt32(); Color color = _playerColors[colorIndex]; Vector2 pos = message.ReadVector2(); Vector2 velocity = message.ReadVector2(); float angle = message.ReadFloat(); if (owner == LocalPlayer.Owner) return; Player player = _players.Find(p => p.Owner == owner); if (player == null) { Player p = Player.CreatePlayer(new Vector2(300, 280), owner); p.Position = pos; p.Color = color; p.Velocity = velocity; p.Angle = angle; p.Owner = owner; _players.Add(p); NetworkMessenger.DisplayMessage(new NetworkMessage() { Color = p.Color, Message = string.Format("{0} has joined.", p.Owner), Sent = DateTime.Now }); } else { int index = _players.IndexOf(player); player.Owner = owner; player.Color = color; player.Position = pos; player.Velocity = velocity; player.Angle = angle; _players[index] = player; } } public void SendEntities(List entities) { var message = _client.CreateMessage(); message.Write((int)UdpNetworkPacketType.EntitiesCreated); message.Write(entities.Count); lock (EntitiesToSend) { for (int i = entities.Count - 1; i >= 0; i--) { message.Write(entities[i].GetType().ToString()); message.Write(_localPlayer.Owner); message.Write(entities[i].Position); message.Write(entities[i].Velocity); message.Write(entities[i].Angle); } } EntitiesToSend.Clear(); _client.SendMessage(message, NetDeliveryMethod.UnreliableSequenced); } public void SendEntity(IEntity entity) { var message = _client.CreateMessage(); message.Write((int)UdpNetworkPacketType.EntityCreated); message.Write(entity.GetType().ToString()); message.Write(_localPlayer.Owner); message.Write(entity.Position); message.Write(entity.Velocity); message.Write(entity.Angle); _client.SendMessage(message, NetDeliveryMethod.UnreliableSequenced); } public void GetServerList() { var listRequest = _client.CreateMessage(); listRequest.Write((byte)UdpNetworkPacketType.RequestHostList); _client.SendUnconnectedMessage(listRequest, _masterServerEndpoint); } public void RequestNATIntroduction(long hostid) { if (hostid == 0) { return; } if (_masterServerEndpoint == null) throw new Exception("Must connect to master server first!"); IPAddress mask; var message = _client.CreateMessage(); message.Write((byte)UdpNetworkPacketType.RequestIntroduction); message.Write(new IPEndPoint(NetUtility.GetMyAddress(out mask), _client.Port)); message.Write(hostid); message.Write(_playerName); _client.SendUnconnectedMessage(message, _masterServerEndpoint); } } }