//---------------------------------------------------------------------------------- /// <summary> /// Receive message processing. /// </summary> /// <param name="msg"></param> //---------------------------------------------------------------------------------- public void ReceivePacket(IAsyncResult msg) { NetworkDataPacket packet = null; IPEndPoint endPoint = new IPEndPoint(0, 0); byte[] data = m_udpClient.EndReceive(msg, ref endPoint); packet = Deserialize(data); packet.IPAddress = endPoint.Address; packet.NetworkID = m_UdpClients[endPoint.Address].NetworkID; //if packet is sent InOrder then an ACK needs to be sent. if (packet.SendMethod == NetworkSendMethod.InOrder) { SendAck(endPoint.Address); } //we divert it at this point based on if its a system for the EntityController ie. Delete or Create or an entity message if (packet.MessageType == MessageType.SystemMessage) { ProcessSystemMessage(packet); } else { if (ReceiveCallback != null) { ReceiveCallback(packet); } } m_udpClient.BeginReceive(ReceivePacket, null); }
//---------------------------------------------------------------------------------- /// <summary> /// Process system message for the EntityController. /// </summary> /// <param name="packet">received packet data.</param> /// <returns></returns> //---------------------------------------------------------------------------------- private bool ProcessSystemMessage(NetworkDataPacket packet) { bool bResult = false; Type systemType = typeof(HybridClient); //using reflection. MethodInfo method = systemType.GetMethod( packet.MethodInvoke, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); //if we find the message call...then we handle it. it not we pass it on to the local device for further handling. if (method != null) { method.Invoke(this, new object[] { packet }); bResult = true; } else { if (SystemMessageReceivedCallback != null) { SystemMessageReceivedCallback(packet); } } return(bResult); }
//---------------------------------------------------------------------------------- /// <summary> /// Update ACK mask when an ACK is received. /// </summary> /// <param name="packet"></param> //---------------------------------------------------------------------------------- private void ReceiveAck(NetworkDataPacket packet) { int nMaskIndex = m_UdpClients[packet.IPAddress].NetworkID; //OR the flags together. m_AckClientsMask |= nMaskIndex; }
//---------------------------------------------------------------------------------- /// <summary> /// For the receiving client to send an ACK when a packet is sent in order. /// </summary> /// <param name="address">Address to send the ACK back to.</param> //---------------------------------------------------------------------------------- private void SendAck(IPAddress address) { NetworkDataPacket packet = new NetworkDataPacket(); packet.MessageType = MessageType.SystemMessage; packet.MethodInvoke = "ReceiveAck"; packet.SendMethod = NetworkSendMethod.FireForget; SendPacket(packet, address); }
//---------------------------------------------------------------------------------- /// <summary> /// Serialize packet in binary format. /// </summary> /// <param name="packet">packet to be serialized</param> /// <returns></returns> //---------------------------------------------------------------------------------- private byte[] SerializePacket(NetworkDataPacket packet) { //We reset the buffer back to the start. try { m_SendMemoryBuffer.Position = 0; m_formatter.Serialize(m_SendMemoryBuffer, packet); m_SendMemoryBuffer.Flush(); } catch (System.Exception ex) { Debug.Assert(false, "a network packet failed to serialize"); } return(Compress(m_SendMemoryBuffer.ToArray())); }
//---------------------------------------------------------------------------------- /// <summary> /// Put received queue ready for processing. /// </summary> /// <param name="packet">received packet</param> //---------------------------------------------------------------------------------- public void QueueMessage(NetworkDataPacket packet) { if (m_bQueueReceivedMessages) { lock (m_ReceiveListLock) m_ReceivedPackets.Add(packet); } else { if (MessageReceived != null) { MessageReceived(packet); } } }
//---------------------------------------------------------------------------------- /// <summary> /// Broadcast to any available clients looking to connect. /// </summary> /// <param name="packet">packet data.</param> //---------------------------------------------------------------------------------- private void LookingForPeers(NetworkDataPacket packet) { //check if connections are allowed if (m_bAllowConnections) { byte[] data; //find first available connection index. int nMaskIndex = FindAvailableIndex(); //ensure the index return is a valid index entry. if (nMaskIndex != InvalidIndex) { NetworkDataPacket replyPacket = new NetworkDataPacket(); UdpClient newClient = new UdpClient(); newClient.Connect(new IPEndPoint(packet.IPAddress, m_nUdpPort)); m_UdpClients.Add(packet.IPAddress, new ClientConnectedInfo(nMaskIndex, newClient, packet.IPAddress) ); //we lock here to avoid race conditions with the packet proc. lock (m_connectionLock) m_ConnectedClientsMask |= nMaskIndex; if (ClientConnectedCallback != null) { ClientConnectedCallback(nMaskIndex); } //set up the reply packet replyPacket.MessageType = MessageType.SystemMessage; replyPacket.SendMethod = NetworkSendMethod.FireForget; replyPacket.MethodInvoke = "PeerConnectionReply"; data = SerializePacket(replyPacket); newClient.BeginSend(data, data.Length, SendCompleteCallback, newClient); } } }
//---------------------------------------------------------------------------------- /// <summary> /// Process messages that are meant for the EntityController. /// </summary> /// <param name="packet"></param> //---------------------------------------------------------------------------------- public void ProcessSystemMessage(NetworkDataPacket packet) { Type type = typeof(NetworkEngine); //use reflection to find method. MethodInfo method = type.GetMethod( packet.MethodInvoke, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); if (method != null) { method.Invoke(this, new object[] { packet }); } else { Debug.Assert(false, "Failed to find a system message for " + packet.MethodInvoke); } }
//---------------------------------------------------------------------------------- /// <summary> /// sends network data packet message across the network. /// </summary> /// <param name="packet">the packet to be sent.</param> /// <param name="address">the address of the client that the packet is to be sent to.</param> //---------------------------------------------------------------------------------- public void SendPacket(NetworkDataPacket packet, IPAddress address = null) { IPEndPoint endPoint = null; ClientConnectedInfo clientInfo; byte[] data; if (address != null) { endPoint = new IPEndPoint(address, m_nUdpPort); } data = SerializePacket(packet); clientInfo = m_UdpClients[endPoint.Address]; clientInfo.Client.BeginSend(data, data.Length, SendCompleteCallback, null); }
//---------------------------------------------------------------------------------- /// <summary> /// locate and return broadcasting peers for the network. /// </summary> //---------------------------------------------------------------------------------- public void FindPeers() { byte[] data; NetworkDataPacket packet = new NetworkDataPacket(); packet.NetworkID = 0; packet.MessageType = MessageType.SystemMessage; packet.SendMethod = NetworkSendMethod.FireForget; packet.MethodInvoke = "LookingForPeers"; m_udpClient.EnableBroadcast = true; data = SerializePacket(packet); m_bAllowConnections = true; m_udpClient.BeginSend(data, data.Length, new IPEndPoint(IPAddress.Loopback, m_nUdpPort), SendCompleteCallback, m_udpClient); }
//---------------------------------------------------------------------------------- /// <summary> /// This is the reply to a client looking for peers. /// </summary> /// <param name="packet">Received packet</param> //---------------------------------------------------------------------------------- private void PeerConnectionReply(NetworkDataPacket packet) { //get connection mask index int nMaskIndex = FindAvailableIndex(); //ensure its a valid index. if (nMaskIndex != InvalidIndex) { UdpClient newClient = new UdpClient(new IPEndPoint(packet.IPAddress, m_nUdpPort)); m_UdpClients.Add(packet.IPAddress, new ClientConnectedInfo(nMaskIndex, newClient, packet.IPAddress)); //lock wil updating mask to avoid race conditions. lock (m_connectionLock) m_ConnectedClientsMask |= nMaskIndex; if (ClientConnectedCallback != null) { ClientConnectedCallback(nMaskIndex); } } }
//---------------------------------------------------------------------------------- /// <summary> /// Deserilize a reveived packet. /// </summary> /// <param name="data">data to be deserialized.</param> /// <returns>NetworkDataPacket</returns> //---------------------------------------------------------------------------------- private NetworkDataPacket Deserialize(byte[] data) { NetworkDataPacket packet = null; m_ReceiveMemoryBuffer.Position = 0; try { byte[] decompressData = Decompress(data); m_ReceiveMemoryBuffer.Write(decompressData, 0, decompressData.Length); m_ReceiveMemoryBuffer.Flush(); m_ReceiveMemoryBuffer.Seek(0, SeekOrigin.Begin); m_ReceiveMemoryBuffer.Position = 0; packet = (NetworkDataPacket)m_formatter.Deserialize(m_ReceiveMemoryBuffer); } catch (System.Exception ex) { Debug.Assert(false); } return(packet); }
//---------------------------------------------------------------------------------- /// <summary> /// Send network message. Wraps hybrid client send packet. /// </summary> /// <param name="packet"></param> /// <param name="address"></param> //---------------------------------------------------------------------------------- public void SendMessage(NetworkDataPacket packet, IPAddress address = null) { m_hybridclient.SendPacket(packet, address); }
//---------------------------------------------------------------------------------- /// <summary> /// Processes the send queue and send all queued packets for the network. NOTE: This is done on a seperate thread. /// </summary> /// <param name="queue"></param> //---------------------------------------------------------------------------------- private void ProcessSendQueue(object queue) { lock (m_SendQueueLock) { if (m_OrderedSendQueue.Count > 0) { while (m_OrderedSendQueue.Count > 0) { NetworkSendItem item = m_OrderedSendQueue.Dequeue(); NetworkDataPacket packet = item.Packet; IPEndPoint endpoint = null; //serialise the packet to be sent. byte[] data = SerializePacket(packet); int nConnectionMask; int nCounter = 50; int nSpins = 0; m_AckClientsMask = 0; if (item.IPAdress != null) { endpoint = new IPEndPoint(item.IPAdress, m_nUdpPort); } if (endpoint == null) { //send the packet to all connected clients. foreach (ClientConnectedInfo clientInfo in m_UdpClients.Values) { clientInfo.Client.BeginSend(data, data.Length, SendCompleteCallback, clientInfo.Client); } //We don't obtain a double nested lock on the AckMask as //we want the thread to overwrite as necessary lock (m_connectionLock) nConnectionMask = m_ConnectedClientsMask; //We hold execution here until all acks have come back. while (0 != (m_AckClientsMask ^ nConnectionMask)) { ++nSpins; if (nSpins > nCounter) { //if a certain amount of time passes and not all replies have come back. // disconnect that client. int nDisconnectionMask = m_ConnectedClientsMask ^ m_AckClientsMask; for (int i = 0; i < 32; ++i) { if (0 != (m_ConnectedClientsMask & i)) { DisconnectPeer(i); } } } //We need to keep updating this every cycle to check if anything has changed. lock (m_connectionLock) nConnectionMask = m_ConnectedClientsMask; } } else { //This is if we are only sending an inorder packet to a single client. ClientConnectedInfo clientInfo = m_UdpClients[endpoint.Address]; clientInfo.Client.BeginSend(data, data.Length, SendCompleteCallback, clientInfo.Client); //We hold execution here till we get a response.. //if we dont we assume the client has disconnected. while (0 != (m_AckClientsMask & clientInfo.NetworkID)) { ++nSpins; if (nSpins > nCounter) { DisconnectPeer(clientInfo.Address); } } } } } } }