PageRenderTime 56ms CodeModel.GetById 8ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

/src/middleware/enet/host.c

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