1 /**
2  @file host.c
3  @brief ENet host management functions
4 */
5 #define ENET_BUILDING_LIB 1
6 #include <string.h>
7 #include "enet/enet.h"
8 
9 /** @defgroup host ENet host functions
10     @{
11 */
12 
13 /** Creates a host for communicating to peers.
14 
15     @param address   the address at which other peers may connect to this host.  If NULL, then no peers may connect to the host.
16     @param peerCount the maximum number of peers that should be allocated for the host.
17     @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
18     @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
19     @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
20 
21     @returns the host on success and NULL on failure
22 
23     @remarks ENet will strategically drop packets on specific sides of a connection between hosts
24     to ensure the host's bandwidth is not overwhelmed.  The bandwidth parameters also determine
25     the window size of a connection which limits the amount of reliable packets that may be in transit
26     at any given time.
27 */
28 ENetHost *
enet_host_create(const ENetAddress * address,size_t peerCount,size_t channelLimit,enet_uint32 incomingBandwidth,enet_uint32 outgoingBandwidth)29 enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
30 {
31     ENetHost * host;
32     ENetPeer * currentPeer;
33 
34     if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID)
35       return NULL;
36 
37     host = (ENetHost *) enet_malloc (sizeof (ENetHost));
38     if (host == NULL)
39       return NULL;
40     memset (host, 0, sizeof (ENetHost));
41 
42     host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer));
43     if (host -> peers == NULL)
44     {
45        enet_free (host);
46 
47        return NULL;
48     }
49     memset (host -> peers, 0, peerCount * sizeof (ENetPeer));
50 
51     host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM);
52     if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0))
53     {
54        if (host -> socket != ENET_SOCKET_NULL)
55          enet_socket_destroy (host -> socket);
56 
57        enet_free (host -> peers);
58        enet_free (host);
59 
60        return NULL;
61     }
62 
63     enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1);
64     enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1);
65     enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
66     enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
67 
68     if (address != NULL && enet_socket_get_address (host -> socket, & host -> address) < 0)
69       host -> address = * address;
70 
71     if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
72       channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
73     else
74     if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
75       channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
76 
77     host -> randomSeed = (enet_uint32) (size_t) host;
78     host -> randomSeed += enet_host_random_seed ();
79     host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16);
80     host -> channelLimit = channelLimit;
81     host -> incomingBandwidth = incomingBandwidth;
82     host -> outgoingBandwidth = outgoingBandwidth;
83     host -> bandwidthThrottleEpoch = 0;
84     host -> recalculateBandwidthLimits = 0;
85     host -> mtu = ENET_HOST_DEFAULT_MTU;
86     host -> peerCount = peerCount;
87     host -> commandCount = 0;
88     host -> bufferCount = 0;
89     host -> checksum = NULL;
90     host -> receivedAddress.host = ENET_HOST_ANY;
91     host -> receivedAddress.port = 0;
92     host -> receivedData = NULL;
93     host -> receivedDataLength = 0;
94 
95     host -> totalSentData = 0;
96     host -> totalSentPackets = 0;
97     host -> totalReceivedData = 0;
98     host -> totalReceivedPackets = 0;
99 
100     host -> connectedPeers = 0;
101     host -> bandwidthLimitedPeers = 0;
102     host -> duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID;
103     host -> maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE;
104     host -> maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA;
105 
106     host -> compressor.context = NULL;
107     host -> compressor.compress = NULL;
108     host -> compressor.decompress = NULL;
109     host -> compressor.destroy = NULL;
110 
111     host -> intercept = NULL;
112 
113     enet_list_clear (& host -> dispatchQueue);
114 
115     for (currentPeer = host -> peers;
116          currentPeer < & host -> peers [host -> peerCount];
117          ++ currentPeer)
118     {
119        currentPeer -> host = host;
120        currentPeer -> incomingPeerID = currentPeer - host -> peers;
121        currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF;
122        currentPeer -> data = NULL;
123 
124        enet_list_clear (& currentPeer -> acknowledgements);
125        enet_list_clear (& currentPeer -> sentReliableCommands);
126        enet_list_clear (& currentPeer -> sentUnreliableCommands);
127        enet_list_clear (& currentPeer -> outgoingReliableCommands);
128        enet_list_clear (& currentPeer -> outgoingUnreliableCommands);
129        enet_list_clear (& currentPeer -> dispatchedCommands);
130 
131        enet_peer_reset (currentPeer);
132     }
133 
134     return host;
135 }
136 
137 /** Destroys the host and all resources associated with it.
138     @param host pointer to the host to destroy
139 */
140 void
enet_host_destroy(ENetHost * host)141 enet_host_destroy (ENetHost * host)
142 {
143     ENetPeer * currentPeer;
144 
145     if (host == NULL)
146       return;
147 
148     enet_socket_destroy (host -> socket);
149 
150     for (currentPeer = host -> peers;
151          currentPeer < & host -> peers [host -> peerCount];
152          ++ currentPeer)
153     {
154        enet_peer_reset (currentPeer);
155     }
156 
157     if (host -> compressor.context != NULL && host -> compressor.destroy)
158       (* host -> compressor.destroy) (host -> compressor.context);
159 
160     enet_free (host -> peers);
161     enet_free (host);
162 }
163 
164 /** Initiates a connection to a foreign host.
165     @param host host seeking the connection
166     @param address destination for the connection
167     @param channelCount number of channels to allocate
168     @param data user data supplied to the receiving host
169     @returns a peer representing the foreign host on success, NULL on failure
170     @remarks The peer returned will have not completed the connection until enet_host_service()
171     notifies of an ENET_EVENT_TYPE_CONNECT event for the peer.
172 */
173 ENetPeer *
enet_host_connect(ENetHost * host,const ENetAddress * address,size_t channelCount,enet_uint32 data)174 enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data)
175 {
176     ENetPeer * currentPeer;
177     ENetChannel * channel;
178     ENetProtocol command;
179 
180     if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
181       channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
182     else
183     if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
184       channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
185 
186     for (currentPeer = host -> peers;
187          currentPeer < & host -> peers [host -> peerCount];
188          ++ currentPeer)
189     {
190        if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
191          break;
192     }
193 
194     if (currentPeer >= & host -> peers [host -> peerCount])
195       return NULL;
196 
197     currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
198     if (currentPeer -> channels == NULL)
199       return NULL;
200     currentPeer -> channelCount = channelCount;
201     currentPeer -> state = ENET_PEER_STATE_CONNECTING;
202     currentPeer -> address = * address;
203     currentPeer -> connectID = ++ host -> randomSeed;
204 
205     if (host -> outgoingBandwidth == 0)
206       currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
207     else
208       currentPeer -> windowSize = (host -> outgoingBandwidth /
209                                     ENET_PEER_WINDOW_SIZE_SCALE) *
210                                       ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
211 
212     if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
213       currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
214     else
215     if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
216       currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
217 
218     for (channel = currentPeer -> channels;
219          channel < & currentPeer -> channels [channelCount];
220          ++ channel)
221     {
222         channel -> outgoingReliableSequenceNumber = 0;
223         channel -> outgoingUnreliableSequenceNumber = 0;
224         channel -> incomingReliableSequenceNumber = 0;
225         channel -> incomingUnreliableSequenceNumber = 0;
226 
227         enet_list_clear (& channel -> incomingReliableCommands);
228         enet_list_clear (& channel -> incomingUnreliableCommands);
229 
230         channel -> usedReliableWindows = 0;
231         memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows));
232     }
233 
234     command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
235     command.header.channelID = 0xFF;
236     command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
237     command.connect.incomingSessionID = currentPeer -> incomingSessionID;
238     command.connect.outgoingSessionID = currentPeer -> outgoingSessionID;
239     command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu);
240     command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize);
241     command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
242     command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
243     command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
244     command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
245     command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
246     command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
247     command.connect.connectID = currentPeer -> connectID;
248     command.connect.data = ENET_HOST_TO_NET_32 (data);
249 
250     enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0);
251 
252     return currentPeer;
253 }
254 
255 /** Queues a packet to be sent to all peers associated with the host.
256     @param host host on which to broadcast the packet
257     @param channelID channel on which to broadcast
258     @param packet packet to broadcast
259 */
260 void
enet_host_broadcast(ENetHost * host,enet_uint8 channelID,ENetPacket * packet)261 enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet)
262 {
263     ENetPeer * currentPeer;
264 
265     for (currentPeer = host -> peers;
266          currentPeer < & host -> peers [host -> peerCount];
267          ++ currentPeer)
268     {
269        if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
270          continue;
271 
272        enet_peer_send (currentPeer, channelID, packet);
273     }
274 
275     if (packet -> referenceCount == 0)
276       enet_packet_destroy (packet);
277 }
278 
279 /** Sets the packet compressor the host should use to compress and decompress packets.
280     @param host host to enable or disable compression for
281     @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled
282 */
283 void
enet_host_compress(ENetHost * host,const ENetCompressor * compressor)284 enet_host_compress (ENetHost * host, const ENetCompressor * compressor)
285 {
286     if (host -> compressor.context != NULL && host -> compressor.destroy)
287       (* host -> compressor.destroy) (host -> compressor.context);
288 
289     if (compressor)
290       host -> compressor = * compressor;
291     else
292       host -> compressor.context = NULL;
293 }
294 
295 /** Limits the maximum allowed channels of future incoming connections.
296     @param host host to limit
297     @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
298 */
299 void
enet_host_channel_limit(ENetHost * host,size_t channelLimit)300 enet_host_channel_limit (ENetHost * host, size_t channelLimit)
301 {
302     if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
303       channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
304     else
305     if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
306       channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
307 
308     host -> channelLimit = channelLimit;
309 }
310 
311 
312 /** Adjusts the bandwidth limits of a host.
313     @param host host to adjust
314     @param incomingBandwidth new incoming bandwidth
315     @param outgoingBandwidth new outgoing bandwidth
316     @remarks the incoming and outgoing bandwidth parameters are identical in function to those
317     specified in enet_host_create().
318 */
319 void
enet_host_bandwidth_limit(ENetHost * host,enet_uint32 incomingBandwidth,enet_uint32 outgoingBandwidth)320 enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
321 {
322     host -> incomingBandwidth = incomingBandwidth;
323     host -> outgoingBandwidth = outgoingBandwidth;
324     host -> recalculateBandwidthLimits = 1;
325 }
326 
327 void
enet_host_bandwidth_throttle(ENetHost * host)328 enet_host_bandwidth_throttle (ENetHost * host)
329 {
330     enet_uint32 timeCurrent = enet_time_get (),
331            elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch,
332            peersRemaining = (enet_uint32) host -> connectedPeers,
333            dataTotal = ~0,
334            bandwidth = ~0,
335            throttle = 0,
336            bandwidthLimit = 0;
337     int needsAdjustment = host -> bandwidthLimitedPeers > 0 ? 1 : 0;
338     ENetPeer * peer;
339     ENetProtocol command;
340 
341     if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
342       return;
343 
344     host -> bandwidthThrottleEpoch = timeCurrent;
345 
346     if (peersRemaining == 0)
347       return;
348 
349     if (host -> outgoingBandwidth != 0)
350     {
351         dataTotal = 0;
352         bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000;
353 
354         for (peer = host -> peers;
355              peer < & host -> peers [host -> peerCount];
356             ++ peer)
357         {
358             if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
359               continue;
360 
361             dataTotal += peer -> outgoingDataTotal;
362         }
363     }
364 
365     while (peersRemaining > 0 && needsAdjustment != 0)
366     {
367         needsAdjustment = 0;
368 
369         if (dataTotal <= bandwidth)
370           throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
371         else
372           throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
373 
374         for (peer = host -> peers;
375              peer < & host -> peers [host -> peerCount];
376              ++ peer)
377         {
378             enet_uint32 peerBandwidth;
379 
380             if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
381                 peer -> incomingBandwidth == 0 ||
382                 peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
383               continue;
384 
385             peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000;
386             if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth)
387               continue;
388 
389             peer -> packetThrottleLimit = (peerBandwidth *
390                                             ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal;
391 
392             if (peer -> packetThrottleLimit == 0)
393               peer -> packetThrottleLimit = 1;
394 
395             if (peer -> packetThrottle > peer -> packetThrottleLimit)
396               peer -> packetThrottle = peer -> packetThrottleLimit;
397 
398             peer -> outgoingBandwidthThrottleEpoch = timeCurrent;
399 
400             peer -> incomingDataTotal = 0;
401             peer -> outgoingDataTotal = 0;
402 
403             needsAdjustment = 1;
404             -- peersRemaining;
405             bandwidth -= peerBandwidth;
406             dataTotal -= peerBandwidth;
407         }
408     }
409 
410     if (peersRemaining > 0)
411     {
412         if (dataTotal <= bandwidth)
413           throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
414         else
415           throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
416 
417         for (peer = host -> peers;
418              peer < & host -> peers [host -> peerCount];
419              ++ peer)
420         {
421             if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
422                 peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
423               continue;
424 
425             peer -> packetThrottleLimit = throttle;
426 
427             if (peer -> packetThrottle > peer -> packetThrottleLimit)
428               peer -> packetThrottle = peer -> packetThrottleLimit;
429 
430             peer -> incomingDataTotal = 0;
431             peer -> outgoingDataTotal = 0;
432         }
433     }
434 
435     if (host -> recalculateBandwidthLimits)
436     {
437        host -> recalculateBandwidthLimits = 0;
438 
439        peersRemaining = (enet_uint32) host -> connectedPeers;
440        bandwidth = host -> incomingBandwidth;
441        needsAdjustment = 1;
442 
443        if (bandwidth == 0)
444          bandwidthLimit = 0;
445        else
446        while (peersRemaining > 0 && needsAdjustment != 0)
447        {
448            needsAdjustment = 0;
449            bandwidthLimit = bandwidth / peersRemaining;
450 
451            for (peer = host -> peers;
452                 peer < & host -> peers [host -> peerCount];
453                 ++ peer)
454            {
455                if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
456                    peer -> incomingBandwidthThrottleEpoch == timeCurrent)
457                  continue;
458 
459                if (peer -> outgoingBandwidth > 0 &&
460                    peer -> outgoingBandwidth >= bandwidthLimit)
461                  continue;
462 
463                peer -> incomingBandwidthThrottleEpoch = timeCurrent;
464 
465                needsAdjustment = 1;
466                -- peersRemaining;
467                bandwidth -= peer -> outgoingBandwidth;
468            }
469        }
470 
471        for (peer = host -> peers;
472             peer < & host -> peers [host -> peerCount];
473             ++ peer)
474        {
475            if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
476              continue;
477 
478            command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
479            command.header.channelID = 0xFF;
480            command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
481 
482            if (peer -> incomingBandwidthThrottleEpoch == timeCurrent)
483              command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth);
484            else
485              command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit);
486 
487            enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
488        }
489     }
490 }
491 
492 /** @} */
493