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