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         ~this()
82         {
83             close();
84         }
85 
86         /// Deinitializes the ENet library and unloads DerelictENet.
87         void close()
88         {
89             if(_enetInitialized)
90             {
91                 enet_deinitialize();
92                 DerelictENet.unload();
93                 _enetInitialized = false;
94             }
95         }
96     }
97 
98     package
99     {
100         Logger _logger;
101     }
102 
103     private
104     {
105         bool _enetInitialized = false;
106 
107     }
108 }