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