1 module connection; 2 3 import std.stdio; 4 5 import derelict.enet.enet; 6 import cbor; 7 8 9 //version = debug_packets; 10 11 // Stores info about connected peer. Used in server 12 alias UserId = size_t; 13 14 /// Packet handler. 15 /// Returns true if data was valid and false otherwise. 16 alias PacketHandler = void delegate(ubyte[] packetData, UserId peer); 17 18 struct PacketInfo 19 { 20 string name; 21 PacketHandler handler; 22 TypeInfo packetType; 23 size_t id; 24 } 25 26 abstract class Connection 27 { 28 // Local side of connection. 29 ENetHost* host; 30 31 // Used when handling packet based on its id. 32 PacketInfo[] packetArray; 33 // Used to get packet id when sending packet. 34 PacketInfo[TypeInfo] packetMap; 35 36 ubyte[] buffer = new ubyte[1024*1024]; 37 38 string side; 39 40 size_t packetId(P)() 41 { 42 return packetMap[typeid(P)].id; 43 } 44 45 string packetName(size_t packetId) 46 { 47 if (packetId >= packetArray.length) return "!Unknown!"; 48 return packetArray[packetId].name; 49 } 50 51 void registerPacket(P)(PacketHandler handler = null, string packetName = P.stringof) 52 { 53 size_t newId = packetArray.length; 54 PacketInfo pinfo = PacketInfo(packetName, handler, typeid(P), newId); 55 packetArray ~= pinfo; 56 assert(typeid(P) !in packetMap); 57 packetMap[typeid(P)] = pinfo; 58 } 59 60 bool handlePacket(size_t packetId, ubyte[] packetData, UserId peerInfo) 61 { 62 if (packetId >= packetArray.length) 63 return false; // invalid packet 64 65 auto handler = packetArray[packetId].handler; 66 if (handler is null) 67 return false; // handler is not set 68 69 handler(packetData, peerInfo); 70 return true; 71 } 72 73 ubyte[] createPacket(P)(auto ref P packet) 74 { 75 ubyte[] bufferTemp = buffer; 76 size_t size; 77 78 version(debug_packets) writefln("%s: creating packet %s with id %s", side, P.stringof, packetId!P); 79 version(debug_packets) writefln("%s: with fields {%s}:%s", side, packet.tupleof, numEncodableMembers!(packet)); 80 81 size = encodeCbor(bufferTemp[], packetId!P); 82 83 version(debug_packets) writef("%s: size %s, bufferTemp[0]%02x", side, size, bufferTemp[0]); 84 version(debug_packets) writefln(" %s", decodeCbor(bufferTemp[0..1])); 85 86 size += encodeCbor(bufferTemp[size..$], packet); 87 return bufferTemp[0..size]; 88 } 89 90 // packetData must contain data with packet id stripped off. 91 auto ref P unpackPacket(P)(ubyte[] packetData) 92 { 93 return decodeCborSingleDup!P(packetData); // TODO: check for excess data. 94 } 95 96 void printPacketMap() 97 { 98 foreach(i, packetInfo; packetArray) 99 { 100 writefln("% 2s: %s", i, packetInfo.name); 101 } 102 } 103 104 void flush() 105 { 106 enet_host_flush(host); 107 } 108 109 void stop() 110 { 111 enet_host_destroy(host); 112 } 113 114 void update(uint msecs = 1000) 115 { 116 ENetEvent event; 117 int eventStatus = enet_host_service(host, &event, msecs); 118 119 if (eventStatus == 0) return; 120 121 final switch (event.type) 122 { 123 case ENET_EVENT_TYPE_NONE: 124 break; 125 case ENET_EVENT_TYPE_CONNECT: 126 onConnect(event); 127 break; 128 case ENET_EVENT_TYPE_RECEIVE: 129 onPacketReceived(event); 130 break; 131 case ENET_EVENT_TYPE_DISCONNECT: 132 onDisconnect(event); 133 break; 134 } 135 } 136 137 void onConnect(ref ENetEvent event) 138 { 139 } 140 141 void onPacketReceived(ref ENetEvent event) 142 { 143 try 144 { 145 ubyte[] packetData = event.packet.data[0..event.packet.dataLength]; 146 auto fullPacketData = packetData; 147 size_t packetId = cast(size_t)decodeCborSingle!ulong(packetData); // decodes and pops ulong from range. 148 149 version(debug_packets) writefln("%s: %s:%s received len %s | text %s | hex %(%02x%)", 150 side, packetName(packetId), packetId, 151 event.packet.dataLength, cast(char[])fullPacketData, fullPacketData); 152 153 handlePacket(packetId, packetData, cast(UserId)event.peer.data); 154 } 155 catch(CborException e) 156 { 157 writeln(e); 158 } 159 } 160 161 void onDisconnect(ref ENetEvent event) 162 { 163 } 164 }