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