/** Creates a host for communicating to peers. @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host. @param peerCount the maximum number of peers that should be allocated for the host. @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. @returns the host on success and NULL on failure @remarks ENet will strategically drop packets on specific sides of a connection between hosts to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. */ ENetHost * enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) { ENetHost * host = (ENetHost *) enet_malloc (sizeof (ENetHost)); ENetPeer * currentPeer; if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID) return NULL; host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer)); memset (host -> peers, 0, peerCount * sizeof (ENetPeer)); host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM, address); if (host -> socket == ENET_SOCKET_NULL) { enet_free (host -> peers); enet_free (host); return NULL; } if (address != NULL) host -> address = * address; host -> incomingBandwidth = incomingBandwidth; host -> outgoingBandwidth = outgoingBandwidth; host -> bandwidthThrottleEpoch = 0; host -> recalculateBandwidthLimits = 0; host -> mtu = ENET_HOST_DEFAULT_MTU; host -> peerCount = peerCount; host -> lastServicedPeer = host -> peers; host -> commandCount = 0; host -> bufferCount = 0; host -> receivedAddress.host = ENET_HOST_ANY; host -> receivedAddress.port = 0; host -> receivedDataLength = 0; for (currentPeer = host -> peers; currentPeer < & host -> peers [host -> peerCount]; ++ currentPeer) { currentPeer -> host = host; currentPeer -> incomingPeerID = currentPeer - host -> peers; currentPeer -> data = NULL; enet_list_clear (& currentPeer -> acknowledgements); enet_list_clear (& currentPeer -> sentReliableCommands); enet_list_clear (& currentPeer -> sentUnreliableCommands); enet_list_clear (& currentPeer -> outgoingReliableCommands); enet_list_clear (& currentPeer -> outgoingUnreliableCommands); enet_peer_reset (currentPeer); } return host; }
/** Initiates a connection to a foreign host. @param host host seeking the connection @param address destination for the connection @param channelCount number of channels to allocate @returns a peer representing the foreign host on success, NULL on failure @remarks The peer returned will have not completed the connection until enet_host_service() notifies of an ENET_EVENT_TYPE_CONNECT event for the peer. */ ENetPeer * enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount) { ENetPeer * currentPeer; ENetChannel * channel; ENetProtocol command; if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; else if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; for (currentPeer = host -> peers; currentPeer < & host -> peers [host -> peerCount]; ++ currentPeer) { if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) break; } if (currentPeer >= & host -> peers [host -> peerCount]) return NULL; currentPeer -> state = ENET_PEER_STATE_CONNECTING; currentPeer -> address = * address; currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); currentPeer -> channelCount = channelCount; currentPeer -> challenge = (enet_uint32) rand (); if (host -> outgoingBandwidth == 0) currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; else currentPeer -> windowSize = (host -> outgoingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; else if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; for (channel = currentPeer -> channels; channel < & currentPeer -> channels [channelCount]; ++ channel) { channel -> outgoingReliableSequenceNumber = 0; channel -> outgoingUnreliableSequenceNumber = 0; channel -> incomingReliableSequenceNumber = 0; channel -> incomingUnreliableSequenceNumber = 0; enet_list_clear (& channel -> incomingReliableCommands); enet_list_clear (& channel -> incomingUnreliableCommands); } command.header.command = ENET_PROTOCOL_COMMAND_CONNECT; command.header.channelID = 0xFF; command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE; command.header.commandLength = sizeof (ENetProtocolConnect); command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); command.connect.mtu = ENET_HOST_TO_NET_16 (currentPeer -> mtu); command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize); command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount); command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval); command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration); command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration); enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0); return currentPeer; }
/** Queues a packet to be sent. @param peer destination for the packet @param channelID channel on which to send @param packet packet to send @retval 0 on success @retval < 0 on failure */ int enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) { ENetChannel * channel = & peer -> channels [channelID]; ENetProtocol command; size_t fragmentLength; if (peer -> state != ENET_PEER_STATE_CONNECTED || channelID >= peer -> channelCount) return -1; fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment); if (packet -> dataLength > fragmentLength) { enet_uint16 startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingReliableSequenceNumber + 1); enet_uint32 fragmentCount = ENET_HOST_TO_NET_32 ((packet -> dataLength + fragmentLength - 1) / fragmentLength), fragmentNumber, fragmentOffset; ENetList fragments; ENetOutgoingCommand * fragment; enet_list_clear (& fragments); for (fragmentNumber = 0, fragmentOffset = 0; fragmentOffset < packet -> dataLength; ++ fragmentNumber, fragmentOffset += fragmentLength) { if (packet -> dataLength - fragmentOffset < fragmentLength) fragmentLength = packet -> dataLength - fragmentOffset; fragment = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand)); if (fragment == NULL) { while (! enet_list_empty (& fragments)) { fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments)); enet_free (fragment); } return -1; } fragment -> fragmentOffset = fragmentOffset; fragment -> fragmentLength = fragmentLength; fragment -> packet = packet; fragment -> command.header.command = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; fragment -> command.header.channelID = channelID; fragment -> command.sendFragment.startSequenceNumber = startSequenceNumber; fragment -> command.sendFragment.dataLength = ENET_HOST_TO_NET_16 (fragmentLength); fragment -> command.sendFragment.fragmentCount = fragmentCount; fragment -> command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber); fragment -> command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength); fragment -> command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset); enet_list_insert (enet_list_end (& fragments), fragment); } packet -> referenceCount += fragmentNumber; while (! enet_list_empty (& fragments)) { fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments)); enet_peer_setup_outgoing_command (peer, fragment); } return 0; } command.header.channelID = channelID; if (packet -> flags & ENET_PACKET_FLAG_RELIABLE) { command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); } else if (packet -> flags & ENET_PACKET_FLAG_UNSEQUENCED) { command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup + 1); command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); } else if (channel -> outgoingUnreliableSequenceNumber >= 0xFFFF) { command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); } else { command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE; command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingUnreliableSequenceNumber + 1); command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); } if (enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength) == NULL) return -1; return 0; }
static ENetPeer * enet_protocol_handle_connect (ENetHost * host, const ENetProtocolHeader * header, const ENetProtocol * command) { enet_uint16 mtu; enet_uint32 windowSize; ENetChannel * channel; size_t channelCount; ENetPeer * currentPeer; ENetProtocol verifyCommand; if (command -> header.commandLength < sizeof (ENetProtocolConnect)) return NULL; channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount); if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) return NULL; for (currentPeer = host -> peers; currentPeer < & host -> peers [host -> peerCount]; ++ currentPeer) { if (currentPeer -> state != ENET_PEER_STATE_DISCONNECTED && currentPeer -> address.host == host -> receivedAddress.host && currentPeer -> address.port == host -> receivedAddress.port && currentPeer -> challenge == header -> challenge) return NULL; } for (currentPeer = host -> peers; currentPeer < & host -> peers [host -> peerCount]; ++ currentPeer) { if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) break; } if (currentPeer >= & host -> peers [host -> peerCount]) return NULL; currentPeer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; currentPeer -> challenge = header -> challenge; currentPeer -> address = host -> receivedAddress; currentPeer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID); currentPeer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth); currentPeer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth); currentPeer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval); currentPeer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration); currentPeer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration); currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); currentPeer -> channelCount = channelCount; for (channel = currentPeer -> channels; channel < & currentPeer -> channels [channelCount]; ++ channel) { channel -> outgoingReliableSequenceNumber = 0; channel -> outgoingUnreliableSequenceNumber = 0; channel -> incomingReliableSequenceNumber = 0; channel -> incomingUnreliableSequenceNumber = 0; enet_list_clear (& channel -> incomingReliableCommands); enet_list_clear (& channel -> incomingUnreliableCommands); } mtu = ENET_NET_TO_HOST_16 (command -> connect.mtu); if (mtu < ENET_PROTOCOL_MINIMUM_MTU) mtu = ENET_PROTOCOL_MINIMUM_MTU; else if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) mtu = ENET_PROTOCOL_MAXIMUM_MTU; currentPeer -> mtu = mtu; if (host -> outgoingBandwidth == 0 && currentPeer -> incomingBandwidth == 0) currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; else currentPeer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, currentPeer -> incomingBandwidth) / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; else if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; if (host -> incomingBandwidth == 0) windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; else windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize)) windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize); if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; else if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT; verifyCommand.header.channelID = 0xFF; verifyCommand.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE; verifyCommand.header.commandLength = sizeof (ENetProtocolVerifyConnect); verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_16 (currentPeer -> mtu); verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize); verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount); verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval); verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration); verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration); enet_peer_queue_outgoing_command (currentPeer, & verifyCommand, NULL, 0, 0); return currentPeer; }