Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/enet-1.1/host.c @ 22

Last change on this file since 22 was 13, checked in by landauf, 17 years ago

added enet

File size: 13.5 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 "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 incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
18    @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
19
20    @returns the host on success and NULL on failure
21
22    @remarks ENet will strategically drop packets on specific sides of a connection between hosts
23    to ensure the host's bandwidth is not overwhelmed.  The bandwidth parameters also determine
24    the window size of a connection which limits the amount of reliable packets that may be in transit
25    at any given time.
26*/
27ENetHost *
28enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
29{
30    ENetHost * host = (ENetHost *) enet_malloc (sizeof (ENetHost));
31    ENetPeer * currentPeer;
32
33    if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID)
34      return NULL;
35
36    host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer));
37    memset (host -> peers, 0, peerCount * sizeof (ENetPeer));
38
39    host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM, address);
40    if (host -> socket == ENET_SOCKET_NULL)
41    {
42       enet_free (host -> peers);
43       enet_free (host);
44
45       return NULL;
46    }
47
48    if (address != NULL)
49      host -> address = * address;
50
51    host -> incomingBandwidth = incomingBandwidth;
52    host -> outgoingBandwidth = outgoingBandwidth;
53    host -> bandwidthThrottleEpoch = 0;
54    host -> recalculateBandwidthLimits = 0;
55    host -> mtu = ENET_HOST_DEFAULT_MTU;
56    host -> peerCount = peerCount;
57    host -> lastServicedPeer = host -> peers;
58    host -> commandCount = 0;
59    host -> bufferCount = 0;
60    host -> receivedAddress.host = ENET_HOST_ANY;
61    host -> receivedAddress.port = 0;
62    host -> receivedDataLength = 0;
63     
64    for (currentPeer = host -> peers;
65         currentPeer < & host -> peers [host -> peerCount];
66         ++ currentPeer)
67    {
68       currentPeer -> host = host;
69       currentPeer -> incomingPeerID = currentPeer - host -> peers;
70       currentPeer -> data = NULL;
71
72       enet_list_clear (& currentPeer -> acknowledgements);
73       enet_list_clear (& currentPeer -> sentReliableCommands);
74       enet_list_clear (& currentPeer -> sentUnreliableCommands);
75       enet_list_clear (& currentPeer -> outgoingReliableCommands);
76       enet_list_clear (& currentPeer -> outgoingUnreliableCommands);
77
78       enet_peer_reset (currentPeer);
79    }
80 
81    return host;
82}
83
84/** Destroys the host and all resources associated with it.
85    @param host pointer to the host to destroy
86*/
87void
88enet_host_destroy (ENetHost * host)
89{
90    ENetPeer * currentPeer;
91
92    enet_socket_destroy (host -> socket);
93
94    for (currentPeer = host -> peers;
95         currentPeer < & host -> peers [host -> peerCount];
96         ++ currentPeer)
97    {
98       enet_peer_reset (currentPeer);
99    }
100
101    enet_free (host -> peers);
102    enet_free (host);
103}
104
105/** Initiates a connection to a foreign host.
106    @param host host seeking the connection
107    @param address destination for the connection
108    @param channelCount number of channels to allocate
109    @returns a peer representing the foreign host on success, NULL on failure
110    @remarks The peer returned will have not completed the connection until enet_host_service()
111    notifies of an ENET_EVENT_TYPE_CONNECT event for the peer.
112*/
113ENetPeer *
114enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount)
115{
116    ENetPeer * currentPeer;
117    ENetChannel * channel;
118    ENetProtocol command;
119
120    if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
121      channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
122    else
123    if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
124      channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
125
126    for (currentPeer = host -> peers;
127         currentPeer < & host -> peers [host -> peerCount];
128         ++ currentPeer)
129    {
130       if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
131         break;
132    }
133
134    if (currentPeer >= & host -> peers [host -> peerCount])
135      return NULL;
136
137    currentPeer -> state = ENET_PEER_STATE_CONNECTING;
138    currentPeer -> address = * address;
139    currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
140    currentPeer -> channelCount = channelCount;
141    currentPeer -> sessionID = (enet_uint32) enet_rand ();
142
143    if (host -> outgoingBandwidth == 0)
144      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
145    else
146      currentPeer -> windowSize = (host -> outgoingBandwidth /
147                                    ENET_PEER_WINDOW_SIZE_SCALE) * 
148                                      ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
149
150    if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
151      currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
152    else
153    if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
154      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
155         
156    for (channel = currentPeer -> channels;
157         channel < & currentPeer -> channels [channelCount];
158         ++ channel)
159    {
160        channel -> outgoingReliableSequenceNumber = 0;
161        channel -> outgoingUnreliableSequenceNumber = 0;
162        channel -> incomingReliableSequenceNumber = 0;
163        channel -> incomingUnreliableSequenceNumber = 0;
164
165        enet_list_clear (& channel -> incomingReliableCommands);
166        enet_list_clear (& channel -> incomingUnreliableCommands);
167    }
168       
169    command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
170    command.header.channelID = 0xFF;
171    command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
172    command.connect.mtu = ENET_HOST_TO_NET_16 (currentPeer -> mtu);
173    command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize);
174    command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
175    command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
176    command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
177    command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
178    command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
179    command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
180    command.connect.sessionID = currentPeer -> sessionID;
181   
182    enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0);
183
184    return currentPeer;
185}
186
187/** Queues a packet to be sent to all peers associated with the host.
188    @param host host on which to broadcast the packet
189    @param channelID channel on which to broadcast
190    @param packet packet to broadcast
191*/
192void
193enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet)
194{
195    ENetPeer * currentPeer;
196
197    for (currentPeer = host -> peers;
198         currentPeer < & host -> peers [host -> peerCount];
199         ++ currentPeer)
200    {
201       if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
202         continue;
203
204       enet_peer_send (currentPeer, channelID, packet);
205    }
206
207    if (packet -> referenceCount == 0)
208      enet_packet_destroy (packet);
209}
210
211/** Adjusts the bandwidth limits of a host.
212    @param host host to adjust
213    @param incomingBandwidth new incoming bandwidth
214    @param outgoingBandwidth new outgoing bandwidth
215    @remarks the incoming and outgoing bandwidth parameters are identical in function to those
216    specified in enet_host_create().
217*/
218void
219enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
220{
221    host -> incomingBandwidth = incomingBandwidth;
222    host -> outgoingBandwidth = outgoingBandwidth;
223    host -> recalculateBandwidthLimits = 1;
224}
225
226void
227enet_host_bandwidth_throttle (ENetHost * host)
228{
229    enet_uint32 timeCurrent = enet_time_get (),
230           elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch,
231           peersTotal = 0,
232           dataTotal = 0,
233           peersRemaining,
234           bandwidth,
235           throttle = 0,
236           bandwidthLimit = 0;
237    int needsAdjustment;
238    ENetPeer * peer;
239    ENetProtocol command;
240
241    if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
242      return;
243
244    for (peer = host -> peers;
245         peer < & host -> peers [host -> peerCount];
246         ++ peer)
247    {
248        if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
249          continue;
250
251        ++ peersTotal;
252        dataTotal += peer -> outgoingDataTotal;
253    }
254
255    if (peersTotal == 0)
256      return;
257
258    peersRemaining = peersTotal;
259    needsAdjustment = 1;
260
261    if (host -> outgoingBandwidth == 0)
262      bandwidth = ~0;
263    else
264      bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000;
265
266    while (peersRemaining > 0 && needsAdjustment != 0)
267    {
268        needsAdjustment = 0;
269       
270        if (dataTotal < bandwidth)
271          throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
272        else
273          throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
274
275        for (peer = host -> peers;
276             peer < & host -> peers [host -> peerCount];
277             ++ peer)
278        {
279            enet_uint32 peerBandwidth;
280           
281            if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
282                peer -> incomingBandwidth == 0 ||
283                peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
284              continue;
285
286            peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000;
287            if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth)
288              continue;
289
290            peer -> packetThrottleLimit = (peerBandwidth * 
291                                            ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal;
292           
293            if (peer -> packetThrottleLimit == 0)
294              peer -> packetThrottleLimit = 1;
295           
296            if (peer -> packetThrottle > peer -> packetThrottleLimit)
297              peer -> packetThrottle = peer -> packetThrottleLimit;
298
299            peer -> outgoingBandwidthThrottleEpoch = timeCurrent;
300
301           
302            needsAdjustment = 1;
303            -- peersRemaining;
304            bandwidth -= peerBandwidth;
305            dataTotal -= peerBandwidth;
306        }
307    }
308
309    if (peersRemaining > 0)
310    for (peer = host -> peers;
311         peer < & host -> peers [host -> peerCount];
312         ++ peer)
313    {
314        if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
315            peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
316          continue;
317
318        peer -> packetThrottleLimit = throttle;
319
320        if (peer -> packetThrottle > peer -> packetThrottleLimit)
321          peer -> packetThrottle = peer -> packetThrottleLimit;
322    }
323   
324    if (host -> recalculateBandwidthLimits)
325    {
326       host -> recalculateBandwidthLimits = 0;
327
328       peersRemaining = peersTotal;
329       bandwidth = host -> incomingBandwidth;
330       needsAdjustment = 1;
331
332       if (bandwidth == 0)
333         bandwidthLimit = 0;
334       else
335       while (peersRemaining > 0 && needsAdjustment != 0)
336       {
337           needsAdjustment = 0;
338           bandwidthLimit = bandwidth / peersRemaining;
339
340           for (peer = host -> peers;
341                peer < & host -> peers [host -> peerCount];
342                ++ peer)
343           {
344               if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
345                   peer -> incomingBandwidthThrottleEpoch == timeCurrent)
346                 continue;
347
348               if (peer -> outgoingBandwidth > 0 &&
349                   peer -> outgoingBandwidth >= bandwidthLimit)
350                 continue;
351
352               peer -> incomingBandwidthThrottleEpoch = timeCurrent;
353 
354               needsAdjustment = 1;
355               -- peersRemaining;
356               bandwidth -= peer -> outgoingBandwidth;
357           }
358       }
359
360       for (peer = host -> peers;
361            peer < & host -> peers [host -> peerCount];
362            ++ peer)
363       {
364           if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
365             continue;
366
367           command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
368           command.header.channelID = 0xFF;
369           command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
370
371           if (peer -> incomingBandwidthThrottleEpoch == timeCurrent)
372             command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth);
373           else
374             command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit);
375
376           enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
377       } 
378    }
379
380    host -> bandwidthThrottleEpoch = timeCurrent;
381
382    for (peer = host -> peers;
383         peer < & host -> peers [host -> peerCount];
384         ++ peer)
385    {
386        peer -> incomingDataTotal = 0;
387        peer -> outgoingDataTotal = 0;
388    }
389}
390   
391/** @} */
Note: See TracBrowser for help on using the repository browser.