private unsafe void ReceiveDatagram(IAsyncResult ar) { IPEndPoint fromAddr = new IPEndPoint(IPAddress.Any, 0); byte[] data = connection.EndReceive(ar, ref fromAddr); if (!shuttingDown) connection.BeginReceive(ReceiveDatagram, null); #region "ENet Structure Handling" var handle = GCHandle.Alloc(data, GCHandleType.Pinned); IntPtr dataStart = handle.AddrOfPinnedObject(); ENetProtocolHeader header = (ENetProtocolHeader)Marshal.PtrToStructure(dataStart, typeof(ENetProtocolHeader)); Util.ToHostOrder(ref header.PeerID); ushort flag = (ushort)(header.PeerID & PROTOCOL_HEADER_FLAG_MASK); header.PeerID &= unchecked((ushort)~(uint)PROTOCOL_HEADER_FLAG_MASK); ENetPeer? peer = null; if (header.PeerID != PROTOCOL_MAXIMUM_PEER_ID) //peer remains null if the first command is expected to be a connect { try { peer = Peers[header.PeerID]; if (peer.Value.State == ENetPeerState.Disconnected || peer.Value.State == ENetPeerState.Zombie || //Don't include ENET_HOST_BROADCAST, it's meant for clients broadcasting the connect packet and communicating with any server that responds !peer.Value.Address.Equals(fromAddr) /* && peer.Value.Address != ENET_HOST_BROADCAST */) { goto finalPacket; //The peer is disconnected, dead or the packets origin doesn't match the peer - Ignore them } } catch (System.Collections.Generic.KeyNotFoundException) { goto finalPacket; //The client doesn't exist and this doesn't follow connection protocol - Ignore them } } int currentDataOffset = ((flag & PROTOCOL_HEADER_FLAG_SENT_TIME) != 0) ? sizeof(ENetProtocolHeader) : sizeof(ENetProtocolHeader) - 2; //sentTime is 2 bytes while (currentDataOffset < data.Length) { ENetProtocol packet = (ENetProtocol)Marshal.PtrToStructure(dataStart + currentDataOffset, typeof(ENetProtocol)); Util.ToHostOrder(ref packet.Header.ReliableSequenceNumber); if (packet.Header.ChannelID >= ChannelLayout.ChannelCount()) continue; ENetCommand command = (ENetCommand)(packet.Header.Command & (byte)ENetCommand.COMMAND_MASK); if(command >= ENetCommand.COUNT) continue; if (peer == null && command != ENetCommand.CONNECT) return; //Peer was following connection protocol but didn't send the connect first currentDataOffset += command.Size(); if (currentDataOffset > data.Length) return; //The ENetCommand is larger than the remaining data switch (command) { case ENetCommand.ACKNOWLEDGE: Util.ToHostOrder(ref packet.Acknowledge.ReceivedReliableSequenceNumber); Util.ToHostOrder(ref packet.Acknowledge.ReceivedSentTime); //TODO: Handle Acknowledge break; case ENetCommand.BANDWIDTH_LIMIT: Util.ToHostOrder(ref packet.BandwidthLimit.IncomingBandwidth); Util.ToHostOrder(ref packet.BandwidthLimit.OutgoingBandwidth); //TODO: Handle Bandwidth Limit break; case ENetCommand.CONNECT: Console.WriteLine("A connected peer sent a connect packet. WTF are they doing?"); Util.ToHostOrder(ref packet.Connect.MTU); Util.ToHostOrder(ref packet.Connect.WindowSize); Util.ToHostOrder(ref packet.Connect.ChannelCount); Util.ToHostOrder(ref packet.Connect.IncomingBandwidth); Util.ToHostOrder(ref packet.Connect.OutgoingBandwidth); Util.ToHostOrder(ref packet.Connect.PacketThrottleInterval); Util.ToHostOrder(ref packet.Connect.PacketThrottleAcceleration); Util.ToHostOrder(ref packet.Connect.PacketThrottleDeceleration); Util.ToHostOrder(ref packet.Connect.SessionID); peer = HandleConnect(fromAddr, packet.Connect); break; case ENetCommand.DISCONNECT: Util.ToHostOrder(ref packet.Disconnect.Data); //TODO: Handle Disconnect break; case ENetCommand.PING: //Ping has no handling in the real ENet break; case ENetCommand.SEND_FRAGMENT: Util.ToHostOrder(ref packet.SendFragment.DataLength); //We have to assume the fragment is the right type for the channel until I figure out how it indicates it Util.ToHostOrder(ref packet.SendFragment.StartSequenceNumber); Util.ToHostOrder(ref packet.SendFragment.FragmentCount); Util.ToHostOrder(ref packet.SendFragment.FragmentNumber); Util.ToHostOrder(ref packet.SendFragment.TotalLength); Util.ToHostOrder(ref packet.SendFragment.FragmentOffset); byte[] fragmentData = new byte[packet.SendFragment.DataLength]; Marshal.Copy(dataStart + currentDataOffset, fragmentData, 0, packet.SendFragment.DataLength); currentDataOffset += packet.SendFragment.DataLength; HandleReliable(peer.Value, packet, true, fragmentData); break; case ENetCommand.SEND_RELIABLE: Util.ToHostOrder(ref packet.SendReliable.DataLength); if ((ChannelLayout[packet.Header.ChannelID] & ENetSendType.RELIABLE) == 0) //Each of the data handlers uses this to quickly discard any data not matching the channels send type { currentDataOffset += packet.SendReliable.DataLength; break; } byte[] reliableData = new byte[packet.SendReliable.DataLength]; Marshal.Copy(dataStart + currentDataOffset, reliableData, 0, packet.SendReliable.DataLength); currentDataOffset += packet.SendReliable.DataLength; HandleReliable(peer.Value, packet, false, reliableData); break; case ENetCommand.SEND_UNRELIABLE: Util.ToHostOrder(ref packet.SendUnreliable.DataLength); if ((ChannelLayout[packet.Header.ChannelID] & ENetSendType.UNRELIABLE) == 0) { currentDataOffset += packet.SendUnreliable.DataLength; break; } Util.ToHostOrder(ref packet.SendUnreliable.UnreliableSequenceNumber); byte[] unreliableData = new byte[packet.SendUnreliable.DataLength]; Marshal.Copy(dataStart + currentDataOffset, unreliableData, 0, packet.SendUnreliable.DataLength); currentDataOffset += packet.SendUnreliable.DataLength; HandleUnreliable(peer.Value, packet.SendUnreliable, unreliableData); break; case ENetCommand.SEND_UNSEQUENCED: Util.ToHostOrder(ref packet.SendUnsequenced.DataLength); if ((ChannelLayout[packet.Header.ChannelID] & ENetSendType.UNSEQUENCED) == 0) { currentDataOffset += packet.SendUnsequenced.DataLength; break; } Util.ToHostOrder(ref packet.SendUnsequenced.UnsequencedGroup); byte[] unsequencedData = new byte[packet.SendUnsequenced.DataLength]; Marshal.Copy(dataStart + currentDataOffset, unsequencedData, 0, packet.SendUnsequenced.DataLength); currentDataOffset += packet.SendUnsequenced.DataLength; HandleUnsequenced(peer.Value, packet.SendUnsequenced, unsequencedData); break; case ENetCommand.THROTTLE_CONFIGURE: Util.ToHostOrder(ref packet.ThrottleConfigure.PacketThrottleInterval); Util.ToHostOrder(ref packet.ThrottleConfigure.PacketThrottleAcceleration); Util.ToHostOrder(ref packet.ThrottleConfigure.PacketThrottleDeceleration); //TODO: Handle Throttle Configure break; case ENetCommand.VERIFY_CONNECT: Util.ToHostOrder(ref packet.VerifyConnect.MTU); Util.ToHostOrder(ref packet.VerifyConnect.WindowSize); Util.ToHostOrder(ref packet.VerifyConnect.ChannelCount); Util.ToHostOrder(ref packet.VerifyConnect.IncomingBandwidth); Util.ToHostOrder(ref packet.VerifyConnect.OutgoingBandwidth); Util.ToHostOrder(ref packet.VerifyConnect.PacketThrottleInterval); Util.ToHostOrder(ref packet.VerifyConnect.PacketThrottleAcceleration); Util.ToHostOrder(ref packet.VerifyConnect.PacketThrottleDeceleration); //TODO: Handle Verify Connect break; default: goto finalPacket; } } finalPacket: handle.Free(); #endregion if (shuttingDown) shutdownComplete.Set(); }