1 /**
2 Copyright: Copyright (c) 2014-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module netlib.packetmanagement;
8 
9 import cbor;
10 import voxelman.log;
11 import derelict.enet.enet;
12 import voxelman.container.hash.set;
13 
14 void loadEnet()
15 {
16 	int err = enet_initialize();
17 
18 	if (err != 0)
19 	{
20 		error("Error loading ENet library");
21 		return;
22 	}
23 	else
24 	{
25 		ENetVersion ever = enet_linked_version();
26 		infof("Loaded ENet library v%s.%s.%s",
27 			ENET_VERSION_GET_MAJOR(ever),
28 			ENET_VERSION_GET_MINOR(ever),
29 			ENET_VERSION_GET_PATCH(ever));
30 	}
31 }
32 
33 // packetData must contain data with packet id stripped off.
34 P unpackPacket(P)(ubyte[] packetData)
35 {
36 	return decodeCborSingleDup!P(packetData);
37 }
38 
39 P unpackPacketNoDup(P)(ubyte[] packetData)
40 {
41 	return decodeCborSingle!P(packetData);
42 }
43 
44 //version = Packet_Sniffing;
45 
46 struct PacketSniffer(bool client)
47 {
48 	private enum sideName = client ? "[CLIENT]" : "[SERVER]";
49 	HashSet!string disallowedPakets;
50 
51 	void onPacketCreate(string name, ubyte[] packetData) {
52 		version (Packet_Sniffing) {
53 			if (name !in disallowedPakets)
54 				tracef(sideName ~ " create %s %(%02x%)", name, packetData);
55 		}
56 	}
57 
58 	void onPacketHandle(string name, ubyte[] packetData) {
59 		version (Packet_Sniffing) {
60 			if (name !in disallowedPakets)
61 				tracef(sideName ~ " handle %s %(%02x%)", name, packetData);
62 		}
63 	}
64 }
65 
66 mixin template PacketManagement(bool client)
67 {
68 	static if (client)
69 		alias PacketHandler = void delegate(ubyte[] packetData);
70 	else
71 		alias PacketHandler = void delegate(ubyte[] packetData, SessionId sessionId);
72 
73 	PacketSniffer!client sniffer;
74 
75 	static struct PacketInfo
76 	{
77 		string name;
78 		PacketHandler handler;
79 		size_t id;
80 	}
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 	size_t packetId(P)()
89 	{
90 		return packetMap[typeid(P)].id;
91 	}
92 
93 	string packetName(size_t packetId)
94 	{
95 		if (packetId >= packetArray.length) return "!UnknownPacket!";
96 		return packetArray[packetId].name;
97 	}
98 
99 	void registerPacket(P)(PacketHandler handler = null, string packetName = P.stringof)
100 	{
101 		size_t newId = packetArray.length;
102 		PacketInfo* pinfo = new PacketInfo(packetName, handler, newId);
103 		packetArray ~= pinfo;
104 		assert(typeid(P) !in packetMap);
105 		packetMap[typeid(P)] = pinfo;
106 	}
107 
108 	void registerPacketHandler(P)(PacketHandler handler)
109 	{
110 		import std.string : format;
111 		assert(typeid(P) in packetMap, format("Packet '%s' was not registered", typeid(P)));
112 		packetMap[typeid(P)].handler = handler;
113 	}
114 
115 	bool handlePacket(size_t packetId, ubyte[] packetData, ENetPeer* peer)
116 	{
117 		if (packetId >= packetArray.length)
118 			return false; // invalid packet
119 
120 		sniffer.onPacketHandle(packetArray[packetId].name, packetData);
121 
122 		auto handler = packetArray[packetId].handler;
123 		if (handler is null)
124 			return false; // handler is not set
125 
126 		static if (client) {
127 			handler(packetData);
128 		} else {
129 			auto sessionId = SessionId(cast(size_t)peer.data);
130 			handler(packetData, sessionId);
131 		}
132 
133 		return true;
134 	}
135 
136 	ubyte[] createPacket(P)(auto ref const(P) packet)
137 	{
138 		ubyte[] bufferTemp = buffer;
139 		size_t size;
140 
141 		size_t pid = packetId!P;
142 		size = encodeCbor(bufferTemp[], pid);
143 		size += encodeCbor(bufferTemp[size..$], packet);
144 		sniffer.onPacketCreate(packetArray[pid].name, bufferTemp[0..size]);
145 
146 		return bufferTemp[0..size];
147 	}
148 
149 	string[] packetNames() @property
150 	{
151 		import std.algorithm : map;
152 		import std.array : array;
153 		return packetArray.map!(a => a.name).array;
154 	}
155 
156 	void printPacketMap()
157 	{
158 		foreach(i, packetInfo; packetArray)
159 		{
160 			tracef("% 2s: %s", i, packetInfo.name);
161 		}
162 	}
163 
164 	void shufflePackets()
165 	{
166 		import std.random;
167 		randomShuffle(packetArray[1..$]);
168 		foreach (i, packetInfo; packetArray)
169 			packetInfo.id = i;
170 	}
171 }