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 }