1 /** 2 Copyright: Copyright (c) 2014-2016 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 module netlib.connection; 8 9 import std.experimental.logger; 10 import std.conv : to; 11 12 import derelict.enet.enet; 13 import cbor; 14 public import derelict.enet.enet : ENetPeer; 15 import std.string : format; 16 17 18 // Client id type. Used in server to identify clients. 19 alias ClientId = size_t; 20 21 22 void loadEnet(string[] libNames) 23 { 24 DerelictENet.load(libNames); 25 26 int err = enet_initialize(); 27 28 if (err != 0) 29 { 30 error("Error loading ENet library"); 31 return; 32 } 33 else 34 { 35 ENetVersion ever = enet_linked_version(); 36 infof("Loaded ENet library v%s.%s.%s", 37 ENET_VERSION_GET_MAJOR(ever), 38 ENET_VERSION_GET_MINOR(ever), 39 ENET_VERSION_GET_PATCH(ever)); 40 } 41 } 42 43 /// Packet handler. 44 /// Returns true if data was valid and false otherwise. 45 alias PacketHandler = void delegate(ubyte[] packetData, ClientId clientId); 46 47 struct PacketInfo 48 { 49 string name; 50 PacketHandler handler; 51 size_t id; 52 } 53 54 struct ConnectionSettings 55 { 56 ENetAddress* address; 57 size_t maxPeers; 58 size_t numChannels; 59 uint incomingBandwidth; 60 uint outgoingBandwidth; 61 } 62 63 // packetData must contain data with packet id stripped off. 64 P unpackPacket(P)(ubyte[] packetData) 65 { 66 return decodeCborSingleDup!P(packetData); 67 } 68 69 P unpackPacketNoDup(P)(ubyte[] packetData) 70 { 71 return decodeCborSingle!P(packetData); 72 } 73 74 abstract class Connection 75 { 76 // True if connection is still open. 77 bool isRunning; 78 79 // Local side of connection. 80 ENetHost* host; 81 82 // Used when handling packet based on its id. 83 PacketInfo*[] packetArray; 84 85 // Used to get packet id when sending packet. 86 PacketInfo*[TypeInfo] packetMap; 87 88 ubyte[] buffer = new ubyte[1024*1024]; 89 90 void delegate(ref ENetEvent) connectHandler; 91 void delegate(ref ENetEvent) disconnectHandler; 92 93 void start(ConnectionSettings settings) 94 { 95 if (isRunning) stop(); 96 97 host = enet_host_create(settings.address, 98 settings.maxPeers, 99 settings.numChannels, 100 settings.incomingBandwidth, 101 settings.outgoingBandwidth); 102 103 if (host is null) 104 { 105 error("An error occured while trying to create an ENet host"); 106 return; 107 } 108 109 isRunning = true; 110 } 111 112 size_t packetId(P)() 113 { 114 return packetMap[typeid(P)].id; 115 } 116 117 string packetName(size_t packetId) 118 { 119 if (packetId >= packetArray.length) return "!UnknownPacket!"; 120 return packetArray[packetId].name; 121 } 122 123 void registerPacket(P)(PacketHandler handler = null, string packetName = P.stringof) 124 { 125 size_t newId = packetArray.length; 126 PacketInfo* pinfo = new PacketInfo(packetName, handler, newId); 127 packetArray ~= pinfo; 128 assert(typeid(P) !in packetMap); 129 packetMap[typeid(P)] = pinfo; 130 } 131 132 void registerPacketHandler(P)(PacketHandler handler) 133 { 134 assert(typeid(P) in packetMap, format("Packet '%s' was not registered", typeid(P))); 135 packetMap[typeid(P)].handler = handler; 136 } 137 138 bool handlePacket(size_t packetId, ubyte[] packetData, ClientId peerInfo) 139 { 140 if (packetId >= packetArray.length) 141 return false; // invalid packet 142 143 auto handler = packetArray[packetId].handler; 144 if (handler is null) 145 return false; // handler is not set 146 147 handler(packetData, peerInfo); 148 return true; 149 } 150 151 ubyte[] createPacket(P)(auto ref const(P) packet) 152 { 153 ubyte[] bufferTemp = buffer; 154 size_t size; 155 156 size = encodeCbor(bufferTemp[], packetId!P); 157 size += encodeCbor(bufferTemp[size..$], packet); 158 159 return bufferTemp[0..size]; 160 } 161 162 string[] packetNames() @property 163 { 164 import std.algorithm : map; 165 import std.array : array; 166 return packetArray.map!(a => a.name).array; 167 } 168 169 void printPacketMap() 170 { 171 foreach(i, packetInfo; packetArray) 172 { 173 tracef("% 2s: %s", i, packetInfo.name); 174 } 175 } 176 177 void shufflePackets() 178 { 179 import std.random; 180 randomShuffle(packetArray[1..$]); 181 foreach (i, packetInfo; packetArray) 182 packetInfo.id = i; 183 } 184 185 void flush() 186 { 187 if (!isRunning) return; 188 enet_host_flush(host); 189 } 190 191 void stop() 192 { 193 isRunning = false; 194 enet_host_destroy(host); 195 } 196 197 void update() 198 { 199 if (!isRunning) return; 200 ENetEvent event; 201 while (enet_host_service(host, &event, 0) > 0) 202 { 203 final switch (event.type) 204 { 205 case ENET_EVENT_TYPE_NONE: 206 break; 207 case ENET_EVENT_TYPE_CONNECT: 208 onConnect(event); 209 break; 210 case ENET_EVENT_TYPE_RECEIVE: 211 onPacketReceived(event); 212 break; 213 case ENET_EVENT_TYPE_DISCONNECT: 214 onDisconnect(event); 215 break; 216 } 217 } 218 } 219 220 void onConnect(ref ENetEvent event) 221 { 222 if (connectHandler) connectHandler(event); 223 } 224 225 void onPacketReceived(ref ENetEvent event) 226 { 227 ubyte[] packetData = event.packet.data[0..event.packet.dataLength]; 228 auto fullPacketData = packetData; 229 size_t packetId; 230 231 try 232 { 233 // decodes and pops ulong from range. 234 packetId = cast(size_t)decodeCborSingle!ulong(packetData); 235 236 handlePacket(packetId, packetData, cast(ClientId)event.peer.data); 237 } 238 catch(CborException e) 239 { 240 error(e.to!string); 241 errorf("packet:%s length:%s data:%(%x%)", packetName(packetId), event.packet.dataLength, fullPacketData); 242 printCborStream(fullPacketData); 243 } 244 } 245 246 void onDisconnect(ref ENetEvent event) 247 { 248 if (disconnectHandler) disconnectHandler(event); 249 } 250 }