1 module gfm.enet.enet; 2 3 import derelict.enet.enet; 4 import derelict.util.exception; 5 6 import std.experimental.logger; 7 8 /// General ENet exception thrown for all cases. 9 final class ENetException : Exception 10 { 11 @safe pure nothrow this(string message, string file =__FILE__, size_t line = __LINE__, Throwable next = null) 12 { 13 super(message, file, line, next); 14 } 15 } 16 17 /// Owns the loader, logging, keyboard state... 18 /// This object is passed around to other ENet wrapper objects 19 /// to ensure library loading. 20 final class ENet 21 { 22 public 23 { 24 /// Loads DerelictENet and initializes the ENet library. 25 /// Throws: ENetException when enet_initialize fails. 26 this(Logger logger = null) 27 { 28 _logger = logger is null ? new NullLogger() : logger; 29 30 ShouldThrow missingSymFunc( string symName ) 31 { 32 // Supports from libenet 1.3.3 to 1.3.11+ 33 // Obviously we should take extras care in gfm:enet 34 // not to strictly rely on these functions. 35 36 if (symName == "enet_linked_version") 37 return ShouldThrow.No; 38 39 if (symName == "enet_socket_get_address") 40 return ShouldThrow.No; 41 42 if (symName == "enet_socket_get_option") 43 return ShouldThrow.No; 44 45 if (symName == "enet_socket_shutdown") 46 return ShouldThrow.No; 47 48 if (symName == "enet_host_random_seed") 49 return ShouldThrow.No; 50 51 if (symName == "enet_peer_ping_interval") 52 return ShouldThrow.No; 53 54 if (symName == "enet_peer_timeout") 55 return ShouldThrow.No; 56 57 if (symName == "enet_peer_on_connect") 58 return ShouldThrow.No; 59 60 if (symName == "enet_peer_on_disconnect") 61 return ShouldThrow.No; 62 63 // Any other missing symbol should throw. 64 return ShouldThrow.Yes; 65 } 66 67 DerelictENet.missingSymbolCallback = &missingSymFunc; 68 69 try 70 DerelictENet.load(); 71 catch(DerelictException e) 72 throw new ENetException(e.msg); 73 74 int errCode = enet_initialize(); 75 if(errCode < 0) 76 throw new ENetException("enet_initialize failed"); 77 78 _enetInitialized = true; 79 } 80 81 /// Deinitializes the ENet library and unloads DerelictENet. 82 ~this() 83 { 84 if(_enetInitialized) 85 { 86 debug ensureNotInGC("ENet"); 87 enet_deinitialize(); 88 _enetInitialized = false; 89 } 90 } 91 } 92 93 package 94 { 95 Logger _logger; 96 } 97 98 private 99 { 100 bool _enetInitialized = false; 101 } 102 } 103 104 /// Crash if the GC is running. 105 /// Useful in destructors to avoid reliance GC resource release. 106 package void ensureNotInGC(string resourceName) nothrow 107 { 108 import core.exception; 109 try 110 { 111 import core.memory; 112 cast(void) GC.malloc(1); // not ideal since it allocates 113 return; 114 } 115 catch(InvalidMemoryOperationError e) 116 { 117 118 import core.stdc.stdio; 119 fprintf(stderr, "Error: clean-up of %s incorrectly depends on destructors called by the GC.\n", resourceName.ptr); 120 assert(false); 121 } 122 }