1 /// Provides the Host, Server, and Client classes.
2 
3 module gfm.enet.host;
4 
5 import std.algorithm,
6        /*std.stdio,*/
7        std.typecons;
8 
9 import derelict.enet.enet;
10 
11 import gfm.enet.address,
12        gfm.enet.event,
13        gfm.enet.enet,
14        gfm.enet.packet,
15        gfm.enet.peer;
16 
17 /**
18  * A subclass of Host that exists to concisely create a client-oriented Host
19  * 
20  * The Host superclass' address is set to null which disallows other peers from
21  * connecting to the Host.
22  * 
23  * See_Also: Host, Client
24  */
25 final class Client : Host
26 {
27     /**
28      * Creates a new Client, a subclass of Host
29      * 
30      * Params:
31      *  peerCount         = The maximum number of peers that should be allocated
32      *                      for the host, for clients this is useful for
33      *                      connecting to multiple servers.
34      *  channelLimit      = The maximum number of channels allowed; if 0, then
35      *                      this is equivalent to
36      *                      ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
37      *  incomingBandwidth = downstream bandwidth of the host in bytes/second; if
38      *                      0, ENet will assume unlimited bandwidth
39      *  outgoingBandwidth = upstream bandwidth of the host in bytes/second; if
40      *                      0, ENet will assume unlimited bandwidth
41      *
42      * Throws: ENetException if enet_create_host fails
43      */
44     this(ENet enet,
45          size_t peerCount,
46          size_t channelLimit,
47          uint incomingBandwidth=0,
48          uint outgoingBandwidth=0)
49     {
50         super(enet,
51               null,
52               peerCount,
53               channelLimit,
54               incomingBandwidth,
55               outgoingBandwidth);
56     }
57 
58 
59     
60     /**
61      * Creates a new Client, a subclass of Host
62      * 
63      * This constructor sets peerCount to 1, meaning it can only connect to a
64      * singular Peer.
65      * 
66      * Params:
67      *  channelLimit      = The maximum number of channels allowed; if 0, then
68      *                      this is equivalent to
69      *                      ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
70      *  bandwidthLimits   = downstream and upstream bandwidth of the host in
71      *                      bytes/second; if 0, ENet will assume unlimited
72      *                      bandwidth
73      * 
74      * Throws: ENetException if enet_create_host fails
75      */
76     this(ENet enet, size_t channelLimit, uint[2] bandwidthLimits=[0, 0])
77     {
78         this(enet, 1, channelLimit, bandwidthLimits[0], bandwidthLimits[1]);
79     }  
80     
81 }
82 
83 /**
84  * A subclass of Host that exists to concisely create a server-oriented Host
85  * 
86  * The Host superclass' address is set to ENET_HOST_ANY which binds the server
87  * to the default localhost.
88  * 
89  * See_Also: Host, Client
90  */
91 class Server : Host
92 {
93     /**
94      * Creates a new Server, which is a type of Host.
95      * 
96      * Params:
97      *  port              = Port that the server binds to
98      *  peerCount         = The maximum number of peers that should be allocated
99      *                      for the host
100      *  channelLimit      = The maximum number of channels allowed; if 0, then
101      *                      this is equivalent to
102      *                      ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
103      *  incomingBandwidth = downstream bandwidth of the host in bytes/second; if
104      *                      0, ENet will assume unlimited bandwidth
105      *  outgoingBandwidth = upstream bandwidth of the host in bytes/second; if
106      *                      0, ENet will assume unlimited bandwidth
107      * 
108      * Throws: ENetException if enet_address_set_host fails
109      * Throws: ENetException if enet_create_host fails
110      */
111     this(ENet enet,
112          ushort port,
113          size_t peerCount,
114          size_t channelLimit = 0,
115          uint incomingBandwidth = 0,
116          uint outgoingBandwidth = 0)
117     {
118         Address serverAddress = Address(ENET_HOST_ANY, port);
119         super(enet,
120               &serverAddress,
121               peerCount,
122               channelLimit,
123               incomingBandwidth,
124               outgoingBandwidth);
125     }
126 }
127 
128 /**
129  * Encompasses an ENetHost structure with an object-oriented wrapper
130  * 
131  * See_Also: Client, Server
132  */
133 class Host
134 {
135     protected ENetHost *_handle;
136     protected ENet _enet;
137     private
138     {
139         Peer[] _peers;
140         bool _usingRangeCoder;
141     }
142 
143     /**
144      * Creates a new Host
145      * 
146      * Params:
147      *  address           = The address at which other peers may connect to this
148      *                      host; if null, then no peers may connect to the host
149      *  peerCount         = The maximum number of peers that should be allocated
150      *                      for the host
151      *  channelLimit      = The maximum number of channels allowed; if 0, then
152      *                      this is equivalent to
153      *                      ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
154      *  incomingBandwidth = downstream bandwidth of the host in bytes/second; if
155      *                      0, ENet will assume unlimited bandwidth
156      *  outgoingBandwidth = upstream bandwidth of the host in bytes/second; if
157      *                      0, ENet will assume unlimited bandwidth
158      *
159      * Throws: ENetException if enet_create_host fails
160      */
161     this(ENet enet,
162          Address* address,
163          size_t peerCount,
164          size_t channelLimit,
165          uint incomingBandwidth=0,
166          uint outgoingBandwidth=0)
167     {
168         _enet = enet;
169 
170         _handle = enet_host_create(&address.address,
171                                    peerCount,
172                                    channelLimit,
173                                    incomingBandwidth,
174                                    outgoingBandwidth);
175 
176         if(_handle is null)
177             throw new ENetException("enet_host_create failed");
178         _usingRangeCoder = false;
179     }
180     
181     ~this()
182     {
183         close();
184     }
185 
186     /// Cleans up any resources used by the Host
187     void close()
188     {
189         if(_handle !is null)
190         {
191             enet_host_destroy(_handle);
192             _handle = null;
193         }
194     }
195 
196     /** 
197      * Initiates a connection to a foreign host
198      * 
199      * Params:
200      *  foreignAddress = Destination for the connection
201      *  channelCount   = Number of channels to allocate
202      *  data           = User data supplied to the receiving host
203      * 
204      * Throws: ENetException if enet_host_connect fails
205      */
206     Peer connect(Address foreignAddress, size_t channelCount, uint data=0)
207     {
208         ENetPeer *peer = enet_host_connect(_handle,
209                                            &foreignAddress.address,
210                                            channelCount,
211                                            data);
212 
213         if(peer is null)
214             throw new ENetException("enet_host_connect failed");
215 
216         return new Peer(_enet, peer);
217     }
218     
219     /** 
220      * Initiates a connection to a foreign host
221      * 
222      * Params:
223      *  hostName     = Destination hostname for the connection
224      *  port         = Destination port for the connection
225      *  channelCount = Number of channels to allocate
226      *  data         = User data supplied to the receiving host
227      * 
228      * Throws: ENetException if enet_host_connect fails
229      * See_Also: connect
230      */
231     Peer connect(string hostName, ushort port, size_t channelCount, uint data=0)
232     {
233         Address foreignAddress = Address(hostName, port);
234         return connect(foreignAddress, channelCount, data);
235     }
236 
237     /**
238      * Queues a packet to be sent to all peers associated with the host
239      * 
240      * Dirties the sent packet, meaning it cannot be changed.
241      * 
242      * Params:
243      *  packet    = Packet to broadcast to all associated peers
244      *  channelID = Channel to broadcast the packet on
245      */
246     void broadcast(Packet packet, ubyte channelID=0)
247     {
248         // If there are no references to the packet, ENet destroys it
249         if(packet._handle.referenceCount == 0)
250             packet._destroyed = true;
251         enet_host_broadcast(_handle, channelID, packet._handle);
252         packet._dirty = true;
253     }
254     
255     /// Sets the packet compressor the host should use to the default range
256     /// coder
257     void compressWithRangeCoder()
258     {
259         if(!_usingRangeCoder)
260         {
261             auto errCode = enet_host_compress_with_range_coder(_handle);
262             if(errCode < 0)
263             {
264                 string msg = "enet_host_compress_with_range_coder failed";
265                 throw new ENetException(msg);
266             }
267             _usingRangeCoder = true;
268         }
269     }
270     
271     /// Turns off the range coder packet compressor
272     void disableRangeCoder()
273     {
274         if(_usingRangeCoder)
275             enet_host_compress(_handle, null);
276         _usingRangeCoder = false;
277     }
278 
279     /**
280      * Sends any queued packets on the host specified to its designated peers
281      * 
282      * This is not necessary if you are using service, which will also send
283      * queued packets to its designated peers.
284      * 
285      * See_Also: service
286      */
287     void flush()
288     {
289         enet_host_flush(_handle);
290     }
291 
292     /**
293      * Checks for any queued events on the host and dispatches one if available
294      * 
295      * Returns:
296      *  The event which occurred if one happened or null if no event happened.
297      * 
298      * Throws: ENetException if enet_host_check_events fails
299      * 
300      * See_Also: service
301      */
302     Event checkEvents()
303     {
304         ENetEvent event;
305         auto result = enet_host_check_events(_handle, &event);
306         if(result == 0)
307             return null;
308         else if(result > 0)
309         {
310             Event wrappedEvent = new Event(_enet, &event);
311             processPeers(wrappedEvent);
312             return wrappedEvent;
313         }
314         else
315             throw new ENetException("enet_host_check_events failed");
316     }
317 
318     /**
319      * Waits for events on the host and shuttles packets between the host and
320      * its peers
321      * 
322      * Params:
323      *  timeout = number of milliseconds that ENet should wait for events
324      * 
325      * Returns:
326      *  The event which occurred if one happened or null if no event happened.
327      * 
328      * Throws: ENetException if enet_host_service fails
329      * 
330      * See_Also: checkEvents
331      */
332     Event service(uint timeout=0)
333     {
334         ENetEvent event;
335         auto result = enet_host_service(_handle, &event, timeout);
336         if(result == 0)
337             return null;
338         else if(result > 0)
339         {
340             Event wrappedEvent = new Event(_enet, &event);
341             processPeers(wrappedEvent);
342             return wrappedEvent;
343         }
344         else
345             throw new ENetException("enet_host_service failed");
346     }
347 
348     private void processPeers(Event event)
349     {
350         if(event.type() == ENET_EVENT_TYPE_CONNECT)
351         {
352             _peers.length++;
353             _peers[$-1] = event.peer();
354         }
355         else if(event.type() == ENET_EVENT_TYPE_DISCONNECT)
356         {
357             _peers = _peers.remove(_peers.countUntil(event.peer()));
358         }
359     }
360 
361     @property
362     {
363         ENetChecksumCallback checksum()
364         {
365             return _handle.checksum;
366         }
367 
368         void checksum(ENetChecksumCallback callback)
369         {
370             _handle.checksum = callback;
371         }
372     }
373 
374     @property
375     {
376         ENetCompressor compressor()
377         {
378             return _handle.compressor;
379         }
380         
381         void compressor(ENetCompressor compressor)
382         {
383             if(_usingRangeCoder)
384                 disableRangeCoder();
385             _handle.compressor = compressor;
386         }
387 
388         bool compress()
389         {
390             return _usingRangeCoder;
391         }
392         
393         void compress(bool shouldCompress)
394         {
395             if(shouldCompress == true)
396                 compressWithRangeCoder();
397             else
398                 disableRangeCoder();
399         }
400     }
401 
402     @property
403     {
404         ENetInterceptCallback intercept()
405         {
406             return _handle.intercept;
407         }
408         
409         void intercept(ENetInterceptCallback callback)
410         {
411             _handle.intercept = callback;
412         }
413     }
414 
415     @property
416     {
417         size_t channelLimit()
418         {
419             return _handle.channelLimit;
420         }
421 
422         void channelLimit(size_t channelLimit)
423         {
424             enet_host_channel_limit(_handle, channelLimit);
425         }
426     }
427 
428     @property
429     {
430         uint[2] bandwidthLimits()
431         {
432             return [ _handle.incomingBandwidth, _handle.outgoingBandwidth ];
433         }
434 
435         uint incomingBandwidth()
436         {
437             return _handle.incomingBandwidth;
438         }
439         
440         uint outgoingBandwidth()
441         {
442             return _handle.outgoingBandwidth;
443         }
444 
445         void bandwidthLimits(uint[2] limits)
446         {
447             enet_host_bandwidth_limit(_handle, limits[0], limits[1]);
448         }
449 
450         void incomingBandwidth(uint limit)
451         {
452             enet_host_bandwidth_limit(_handle, limit, _handle.outgoingBandwidth);
453         }
454 
455         void outgoingBandwidth(uint limit)
456         {
457             enet_host_bandwidth_limit(_handle, _handle.incomingBandwidth, limit);
458         }
459     }
460 
461     @property
462     {
463         size_t duplicatePeers()
464         {
465             return _handle.duplicatePeers;
466         }
467 
468         void duplicatePeers(size_t maxDuplicatePeers)
469         {
470             _handle.duplicatePeers = maxDuplicatePeers;
471         }
472     }
473 
474     // Getters
475     @property
476     {
477         Address address() { return Address(_handle.address); }
478 
479         Address receivedAddress() { return Address(_handle.receivedAddress); }
480         Peer[] peers() { return _peers; }
481 
482         // _ENetHost structure properties
483         ENetSocket socket() { return _handle.socket; }
484         uint bandwidthThrottleEpoch() { return _handle.bandwidthThrottleEpoch; }
485         uint mtu() { return _handle.mtu; }
486         uint randomSeed() { return _handle.randomSeed; }
487         int recalculateBandwidthLimits() { return _handle.recalculateBandwidthLimits; }
488         size_t peerCount() { return _handle.peerCount; }
489         uint serviceTime() { return _handle.serviceTime; }
490         ENetList dispatchQueue() { return _handle.dispatchQueue; }
491         int continueSending() { return _handle.continueSending; }
492         size_t packetSize() { return _handle.packetSize; }
493         ushort headerFlags() { return _handle.headerFlags; }
494         ENetProtocol[] commands() { return _handle.commands; }
495         size_t commandCount() { return _handle.commandCount; }
496         ENetBuffer[] buffers() { return _handle.buffers; }
497         size_t bufferCount() { return _handle.bufferCount; }
498         ubyte[ENET_PROTOCOL_MAXIMUM_MTU][2] packetData() { return _handle.packetData; }
499         ubyte *receivedData() { return _handle.receivedData; }
500         size_t receivedDataLength() { return _handle.receivedDataLength; }
501         uint totalSentData() { return _handle.totalSentData; }
502         uint totalSentPackets() { return _handle.totalSentPackets; }
503         uint totalReceivedData() { return _handle.totalReceivedData; }
504         uint totalReceivedPackets() { return _handle.totalReceivedPackets; }
505         size_t connectedPeers() { return _handle.connectedPeers; }
506         size_t bandwidthLimitedPeers() { return _handle.bandwidthLimitedPeers; }
507     }
508 }
509 
510 
511 // Tests
512 
513 // Creates a Client with 2 peers and 4 channels maximum
514 unittest
515 {
516     auto enet = scoped!ENet();
517     auto client = new Client(enet, 2, 4);
518     assert(client.peerCount == 2);
519     assert(client.channelLimit == 4);
520     assert(client.bandwidthLimits == [0, 0]);
521     assert(client.incomingBandwidth == 0);
522     assert(client.outgoingBandwidth == 0);
523     client.close(); // Don't let the GC destroy your resources.
524 }
525 
526 // Similar to before, only with a downstream and upstream bandwidth limit
527 unittest
528 {
529     auto enet = scoped!ENet();
530     auto maxPeers = 32;
531     auto maxChannels = 16;
532     auto inLimit = (1*10^^6)/8; // 1mbps
533     auto outLimit = (512*10^^5)/8; // 512kbps
534     auto client = new Client(enet, maxPeers, maxChannels, inLimit, outLimit);
535     assert(client.peerCount == 32);
536     assert(client.channelLimit == 16);
537     assert(client.bandwidthLimits == [(1*10^^6)/8, (512*10^^5)/8]);
538     assert(client.incomingBandwidth == (1*10^^6)/8);
539     assert(client.outgoingBandwidth == (512*10^^5)/8);
540     client.close(); // Don't let the GC destroy your resources.
541 }
542 
543 // Creates a client with 1 channel maximum, no bandwidth limits
544 unittest
545 {
546     auto enet = scoped!ENet();
547     auto client = new Client(enet, 1);
548     assert(client.peerCount == 1);
549     assert(client.channelLimit == 1);
550     assert(client.bandwidthLimits == [0, 0]);
551     assert(client.incomingBandwidth == 0);
552     assert(client.outgoingBandwidth == 0);
553     client.close(); // Can either manually destroy it or let the GC do it
554 }
555 
556 // 4 channels, 768kbps downstream, 128kbps upstream
557 unittest
558 {
559     auto enet = scoped!ENet();
560     auto inLimit = (768*10^^5)/8; // 768kbps
561     auto outLimit = (128*10^^5)/8; // 128kbps
562     auto client = new Client(enet, 4, [inLimit, outLimit]);
563     assert(client.peerCount == 1);
564     assert(client.channelLimit == 4);
565     assert(client.bandwidthLimits == [inLimit, outLimit]);
566     assert(client.incomingBandwidth == inLimit);
567     assert(client.outgoingBandwidth == outLimit);
568     client.close(); // Can either manually destroy it or let the GC do it
569 }
570 
571 // Creates a server on port 27777, with 32 max peers and 8 max channels, with bandwidth limits
572 unittest
573 {
574     auto enet = scoped!ENet();
575     auto inLimit = (20*10^^6)/8; // 20mbps
576     auto outLimit = (10*10^^6)/8; // 10mbps
577     auto server = new Server(enet, 27777, 32, 8, inLimit, outLimit);
578     assert(server.peerCount == 32);
579     assert(server.channelLimit == 8);
580     assert(server.bandwidthLimits == [inLimit, outLimit]);
581     assert(server.incomingBandwidth == inLimit);
582     assert(server.outgoingBandwidth == outLimit);
583     server.close(); // Can either manually destroy it or let the GC do it
584 }
585 
586 // Creates Host for purposes of being a server
587 unittest
588 {
589     auto enet = scoped!ENet();
590     auto maxPeers = 32;
591     auto maxChans = 16;
592     auto inLimit = (2*10^^6)/8;
593     auto outLimit = (2*10^^6)/8;
594     // Create an address which refers to localhost address and port 32807
595     auto hostAddr = new Address(ENET_HOST_ANY, 32807);
596     auto host = new Host(enet, hostAddr, maxPeers, maxChans, inLimit, outLimit);
597     assert(host.peerCount == 32);
598     assert(host.channelLimit == 16);
599     assert(host.bandwidthLimits == [inLimit, outLimit]);
600     assert(host.incomingBandwidth == inLimit);
601     assert(host.outgoingBandwidth == outLimit);
602     host.close(); // Can either manually destroy it or let the GC do it
603 }
604 
605 // Creates Host for purposes of being a client
606 unittest
607 {
608     auto enet = scoped!ENet();
609     // Bandwidth defaults to 0, which means unlimited
610     auto maxPeers = 32;
611     auto maxChannels = 16;
612     // A null Address disallows other peers from connecting
613     auto host = new Host(enet, null, maxPeers, maxChannels);
614     assert(host.peerCount == 32);
615     assert(host.channelLimit == 16);
616     assert(host.bandwidthLimits == [0, 0]);
617     assert(host.incomingBandwidth == 0);
618     assert(host.outgoingBandwidth == 0);
619     host.close(); // Can either manually destroy it or let the GC do it
620 }
621 
622 // Creates and destroys a host
623 unittest
624 {
625     auto enet = scoped!ENet();
626     auto host = new Host(enet, null, 1, 1);
627     host.close();
628     assert(host._handle == null); // _handle is of package visibility
629 }
630 
631 // Creates 2 hosts and connect them together
632 unittest
633 {
634     auto enet = scoped!ENet();
635     ushort port = 25403;
636     auto maxPeers = 1;
637     auto maxChannels = 1;
638     auto server = new Server(enet, port, maxPeers, maxChannels);
639     auto client = new Client(enet, maxPeers, maxChannels);
640     client.connect(server.address, maxChannels);
641     client.close(); // Or let GC handle it
642     server.close();
643 }
644 
645 // Creates 2 hosts and connect them together
646 unittest
647 {
648     auto enet = scoped!ENet();
649     ushort port = 25403;
650     auto maxPeers = 1;
651     auto maxChannels = 1;
652     auto server = new Server(enet, port, maxPeers, maxChannels);
653     auto client = new Client(enet, maxPeers, maxChannels);
654     client.connect("localhost", port, maxChannels);
655     client.close(); // Or let GC handle it
656     server.close();
657 }
658 
659 // Broadcasts a packet to several peers
660 unittest
661 {
662     auto enet = scoped!ENet();
663     ushort port = 25403;
664     auto maxPeers = 2;
665     auto maxChannels = 1;
666     auto server = new Server(enet, port, maxPeers, maxChannels);
667     auto client1 = new Client(enet, 1, maxChannels);
668     auto client2 = new Client(enet, 1, maxChannels);
669     auto packet = new Packet(enet, [0, 1, 2, 3, 4]);
670     client1.connect(server.address, maxChannels);
671     client2.connect(server.address, maxChannels);
672     server.broadcast(packet);
673     client1.close(); // Or let GC clean up
674     client2.close();
675     server.close();
676 }
677 
678 // Makes a server and client host which use range coder
679 unittest
680 {
681     import std.stdio;
682     auto enet = scoped!ENet();
683     ushort port = 25403;
684     auto maxPeers = 1;
685     auto maxChannels = 1;
686     auto server = new Server(enet, port, maxPeers, maxChannels);
687     auto client = new Client(enet, maxPeers, maxChannels);
688     // Both hosts must be using range coder
689     server.compressWithRangeCoder();
690     client.compressWithRangeCoder();
691     auto serverAddress = Address("localhost", port);
692     client.connect(serverAddress, port);
693     client.close(); // Or let GC clean it up
694     server.close();
695 }
696 
697 // Makes a server, enables the range coder, then disables it
698 unittest
699 {
700     auto enet = scoped!ENet();
701     auto server = new Server(enet, 25403, 1, 1);
702     server.compressWithRangeCoder(); // Enable
703     server.disableRangeCoder(); // Disable
704     server.close(); // Or let GC clean it up
705 }