Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/ipv6/src/external/enet/host.c @ 7330

Last change on this file since 7330 was 7330, checked in by adrfried, 14 years ago

patch libenet to support ipv6

This is only done for Linux so far.

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