| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- /**
- @file host.c
- @brief ENet host management functions
- */
- #define ENET_BUILDING_LIB 1
- #define __MINGW_USE_VC2005_COMPAT 1
- #include <string.h>
- #include <time.h>
- #include "enet/enet.h"
- /** @defgroup host ENet host functions
- @{
- */
- /** 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 channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
- @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, size_t channelLimit,
- enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
- {
- ENetHost * host;
- ENetPeer * currentPeer;
- if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID)
- return NULL;
- host = (ENetHost *) enet_malloc(sizeof(ENetHost));
- if (host == NULL)
- return NULL;
- memset(host, 0, sizeof(ENetHost));
- host->peers = (ENetPeer *) enet_malloc(peerCount * sizeof(ENetPeer));
- if (host->peers == NULL)
- {
- enet_free(host);
- return NULL;
- }
- memset(host->peers, 0, peerCount * sizeof(ENetPeer));
- host->socket = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
- if (host->socket == ENET_SOCKET_NULL
- || (address != NULL && enet_socket_bind(host->socket, address) < 0))
- {
- if (host->socket != ENET_SOCKET_NULL)
- enet_socket_destroy(host->socket);
- enet_free(host->peers);
- enet_free(host);
- return NULL;
- }
- enet_socket_set_option(host->socket, ENET_SOCKOPT_NONBLOCK, 1);
- enet_socket_set_option(host->socket, ENET_SOCKOPT_BROADCAST, 1);
- enet_socket_set_option(host->socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
- enet_socket_set_option(host->socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
- if (address != NULL)
- host->address = *address;
- if (!channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
- channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
- else if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
- channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
- host->randomSeed = (enet_uint32) time(NULL) + (enet_uint32) (size_t) host;
- host->randomSeed = (host->randomSeed << 16) | (host->randomSeed >> 16);
- host->channelLimit = channelLimit;
- host->incomingBandwidth = incomingBandwidth;
- host->outgoingBandwidth = outgoingBandwidth;
- host->bandwidthThrottleEpoch = 0;
- host->recalculateBandwidthLimits = 0;
- host->mtu = ENET_HOST_DEFAULT_MTU;
- host->peerCount = peerCount;
- host->commandCount = 0;
- host->bufferCount = 0;
- host->checksum = NULL;
- host->receivedAddress.host = ENET_HOST_ANY;
- host->receivedAddress.port = 0;
- host->receivedData = NULL;
- host->receivedDataLength = 0;
- host->totalSentData = 0;
- host->totalSentPackets = 0;
- host->totalReceivedData = 0;
- host->totalReceivedPackets = 0;
- host->compressor.context = NULL;
- host->compressor.compress = NULL;
- host->compressor.decompress = NULL;
- host->compressor.destroy = NULL;
- enet_list_clear(&host->dispatchQueue);
- for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer)
- {
- currentPeer->host = host;
- currentPeer->incomingPeerID = currentPeer - host->peers;
- currentPeer->outgoingSessionID = currentPeer->incomingSessionID = 0xFF;
- currentPeer->data = NULL;
- enet_list_clear(¤tPeer->acknowledgements);
- enet_list_clear(¤tPeer->sentReliableCommands);
- enet_list_clear(¤tPeer->sentUnreliableCommands);
- enet_list_clear(¤tPeer->outgoingReliableCommands);
- enet_list_clear(¤tPeer->outgoingUnreliableCommands);
- enet_list_clear(¤tPeer->dispatchedCommands);
- enet_peer_reset(currentPeer);
- }
- return host;
- }
- void enet_enable_crc(ENetHost* host)
- {
- host->checksum = enet_crc32;
- }
- /** Destroys the host and all resources associated with it.
- @param host pointer to the host to destroy
- */
- void enet_host_destroy(ENetHost * host)
- {
- ENetPeer * currentPeer;
- enet_socket_destroy(host->socket);
- for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer)
- {
- enet_peer_reset(currentPeer);
- }
- if (host->compressor.context != NULL && host->compressor.destroy)
- (*host->compressor.destroy)(host->compressor.context);
- enet_free(host->peers);
- enet_free(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
- @param data user data supplied to the receiving host
- @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,
- enet_uint32 data)
- {
- 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->channels = (ENetChannel *) enet_malloc(channelCount * sizeof(ENetChannel));
- if (currentPeer->channels == NULL)
- return NULL;
- currentPeer->channelCount = channelCount;
- currentPeer->state = ENET_PEER_STATE_CONNECTING;
- currentPeer->address = *address;
- currentPeer->connectID = ++host->randomSeed;
- 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 < ¤tPeer->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);
- channel->usedReliableWindows = 0;
- memset(channel->reliableWindows, 0, sizeof(channel->reliableWindows));
- }
- command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
- command.header.channelID = 0xFF;
- command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
- command.connect.incomingSessionID = currentPeer->incomingSessionID;
- command.connect.outgoingSessionID = currentPeer->outgoingSessionID;
- command.connect.mtu = ENET_HOST_TO_NET_32 (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);
- command.connect.connectID = currentPeer->connectID;
- command.connect.data = ENET_HOST_TO_NET_32 (data);
- enet_peer_queue_outgoing_command(currentPeer, &command, NULL, 0, 0);
- return currentPeer;
- }
- /** Queues a packet to be sent to all peers associated with the host.
- @param host host on which to broadcast the packet
- @param channelID channel on which to broadcast
- @param packet packet to broadcast
- */
- void enet_host_broadcast(ENetHost * host, enet_uint8 channelID, ENetPacket * packet)
- {
- ENetPeer * currentPeer;
- for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer)
- {
- if (currentPeer->state != ENET_PEER_STATE_CONNECTED)
- continue;
- enet_peer_send(currentPeer, channelID, packet);
- }
- if (packet->referenceCount == 0)
- enet_packet_destroy(packet);
- }
- /** Sets the packet compressor the host should use to compress and decompress packets.
- @param host host to enable or disable compression for
- @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled
- */
- void enet_host_compress(ENetHost * host, const ENetCompressor * compressor)
- {
- if (host->compressor.context != NULL && host->compressor.destroy)
- (*host->compressor.destroy)(host->compressor.context);
- if (compressor)
- host->compressor = *compressor;
- else
- host->compressor.context = NULL;
- }
- /** Limits the maximum allowed channels of future incoming connections.
- @param host host to limit
- @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
- */
- void enet_host_channel_limit(ENetHost * host, size_t channelLimit)
- {
- if (!channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
- channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
- else if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
- channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
- host->channelLimit = channelLimit;
- }
- /** Adjusts the bandwidth limits of a host.
- @param host host to adjust
- @param incomingBandwidth new incoming bandwidth
- @param outgoingBandwidth new outgoing bandwidth
- @remarks the incoming and outgoing bandwidth parameters are identical in function to those
- specified in enet_host_create().
- */
- void enet_host_bandwidth_limit(ENetHost * host, enet_uint32 incomingBandwidth,
- enet_uint32 outgoingBandwidth)
- {
- host->incomingBandwidth = incomingBandwidth;
- host->outgoingBandwidth = outgoingBandwidth;
- host->recalculateBandwidthLimits = 1;
- }
- void enet_host_bandwidth_throttle(ENetHost * host)
- {
- enet_uint32 timeCurrent = enet_time_get(), elapsedTime = timeCurrent
- - host->bandwidthThrottleEpoch, peersTotal = 0, dataTotal = 0, peersRemaining,
- bandwidth, throttle = 0, bandwidthLimit = 0;
- int needsAdjustment;
- ENetPeer * peer;
- ENetProtocol command;
- if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
- return;
- for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer)
- {
- if (peer->state != ENET_PEER_STATE_CONNECTED
- && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)
- continue;
- ++peersTotal;
- dataTotal += peer->outgoingDataTotal;
- }
- if (peersTotal == 0)
- return;
- peersRemaining = peersTotal;
- needsAdjustment = 1;
- if (host->outgoingBandwidth == 0)
- bandwidth = ~0;
- else
- bandwidth = (host->outgoingBandwidth * elapsedTime) / 1000;
- while (peersRemaining > 0 && needsAdjustment != 0)
- {
- needsAdjustment = 0;
- if (dataTotal < bandwidth)
- throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
- else
- throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
- for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer)
- {
- enet_uint32 peerBandwidth;
- if ((peer->state != ENET_PEER_STATE_CONNECTED
- && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)
- || peer->incomingBandwidth == 0
- || peer->outgoingBandwidthThrottleEpoch == timeCurrent)
- continue;
- peerBandwidth = (peer->incomingBandwidth * elapsedTime) / 1000;
- if ((throttle * peer->outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE
- <= peerBandwidth)
- continue;
- peer->packetThrottleLimit = (peerBandwidth * ENET_PEER_PACKET_THROTTLE_SCALE)
- / peer->outgoingDataTotal;
- if (peer->packetThrottleLimit == 0)
- peer->packetThrottleLimit = 1;
- if (peer->packetThrottle > peer->packetThrottleLimit)
- peer->packetThrottle = peer->packetThrottleLimit;
- peer->outgoingBandwidthThrottleEpoch = timeCurrent;
- needsAdjustment = 1;
- --peersRemaining;
- bandwidth -= peerBandwidth;
- dataTotal -= peerBandwidth;
- }
- }
- if (peersRemaining > 0)
- for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer)
- {
- if ((peer->state != ENET_PEER_STATE_CONNECTED
- && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)
- || peer->outgoingBandwidthThrottleEpoch == timeCurrent)
- continue;
- peer->packetThrottleLimit = throttle;
- if (peer->packetThrottle > peer->packetThrottleLimit)
- peer->packetThrottle = peer->packetThrottleLimit;
- }
- if (host->recalculateBandwidthLimits)
- {
- host->recalculateBandwidthLimits = 0;
- peersRemaining = peersTotal;
- bandwidth = host->incomingBandwidth;
- needsAdjustment = 1;
- if (bandwidth == 0)
- bandwidthLimit = 0;
- else
- while (peersRemaining > 0 && needsAdjustment != 0)
- {
- needsAdjustment = 0;
- bandwidthLimit = bandwidth / peersRemaining;
- for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer)
- {
- if ((peer->state != ENET_PEER_STATE_CONNECTED
- && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)
- || peer->incomingBandwidthThrottleEpoch == timeCurrent)
- continue;
- if (peer->outgoingBandwidth > 0 && peer->outgoingBandwidth >= bandwidthLimit)
- continue;
- peer->incomingBandwidthThrottleEpoch = timeCurrent;
- needsAdjustment = 1;
- --peersRemaining;
- bandwidth -= peer->outgoingBandwidth;
- }
- }
- for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer)
- {
- if (peer->state != ENET_PEER_STATE_CONNECTED
- && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)
- continue;
- command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT
- | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
- command.header.channelID = 0xFF;
- command.bandwidthLimit.outgoingBandwidth =
- ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
- if (peer->incomingBandwidthThrottleEpoch == timeCurrent)
- command.bandwidthLimit.incomingBandwidth =
- ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth);
- else
- command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit);
- enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0);
- }
- }
- host->bandwidthThrottleEpoch = timeCurrent;
- for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer)
- {
- peer->incomingDataTotal = 0;
- peer->outgoingDataTotal = 0;
- }
- }
- /** @} */
|