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