1 /*
2  * DSFML - The Simple and Fast Multimedia Library for D
3  *
4  * Copyright (c) 2013 - 2018 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5  *
6  * This software is provided 'as-is', without any express or implied warranty.
7  * In no event will the authors be held liable for any damages arising from the
8  * use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not claim
15  * that you wrote the original software. If you use this software in a product,
16  * an acknowledgment in the product documentation would be appreciated but is
17  * not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  * misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source distribution
23  *
24  *
25  * DSFML is based on SFML (Copyright Laurent Gomila)
26  */
27 
28 /**
29  * Packets provide a safe and easy way to serialize data, in order to send it
30  * over the network using sockets (sf::TcpSocket, sf::UdpSocket).
31  *
32  * Packets solve 2 fundamental problems that arise when transferring data over
33  * the network:
34  * $(UL
35  * $(LI data is interpreted correctly according to the endianness)
36  * $(LI the bounds of the packet are preserved (one send == one receive)))
37  *
38  * $(PARA The $(U Packet) class provides both input and output modes.)
39  *
40  * Example:
41  * ---
42  * int x = 24;
43  * string s = "hello";
44  * double d = 5.89;
45  *
46  * // Group the variables to send into a packet
47  * auto packet = new Packet();
48  * packet.write(x);
49  * packet.write(s);
50  * packet.write(d);
51  *
52  * // Send it over the network (socket is a valid TcpSocket)
53  * socket.send(packet);
54  *
55  * ////////////////////////////////////////////////////////////////
56  *
57  * // Receive the packet at the other end
58  * auto packet = new Packet();
59  * socket.receive(packet);
60  *
61  * // Extract the variables contained in the packet
62  * int x;
63  *  s;
64  * double d;
65  * if (packet.read(x) && packet.read(s) && packet.read(d))
66  * {
67  *     // Data extracted successfully...
68  * }
69  * ---
70  *
71  * $(PARA Packets also provide an extra feature that allows to apply custom
72  * transformations to the data before it is sent, and after it is received. This
73  * is typically used to handle automatic compression or encryption of the data.
74  * This is achieved by inheriting from sf::Packet, and overriding the onSend and
75  * onReceive functions.)
76  *
77  * Example:
78  * ---
79  * class ZipPacket : Packet
80  * {
81  *     override const(void)[] onSend()
82  *     {
83  *         const(void)[] srcData = getData();
84  *
85  *         return MySuperZipFunction(srcData);
86  *     }
87  *
88  *     override void onReceive(const(void)[] data)
89  *     {
90  *         const(void)[] dstData = MySuperUnzipFunction(data);
91  *
92  *         append(dstData);
93  *     }
94  * }
95  *
96  * // Use like regular packets:
97  * auto packet = new ZipPacket();
98  * packet.write(x);
99  * packet.write(s);
100  * packet.write(d);
101  * ---
102  *
103  * See_Also:
104  * $(TCPSOCKET_LINK), $(UDPSOCKET_LINK)
105  */
106 module nudsfml.network.packet;
107 
108 import std.traits;
109 import std.range;
110 
111 /**
112  * Utility class to build blocks of data to transfer over the network.
113  */
114 class Packet
115 {
116     private
117     {
118         ubyte[] m_data;    /// Data stored in the packet
119         size_t  m_readPos; /// Current reading position in the packet
120         bool    m_isValid; /// Reading state of the packet
121     }
122 
123     /// Default constructor.
124     this()
125     {
126         m_readPos = 0;
127         m_isValid = true;
128     }
129 
130     /// Destructor.
131     ~this()
132     {
133         import nudsfml.system.config;
134         mixin(destructorOutput);
135     }
136 
137     /**
138      * Get a slice of the data contained in the packet.
139      *
140      * Returns: Slice containing the data.
141      */
142     const(void)[] getData() const
143     {
144         return m_data;
145     }
146 
147     /**
148      * Append data to the end of the packet.
149      *
150      * Params:
151      *	data = Pointer to the sequence of bytes to append
152      */
153     void append(const(void)[] data)
154     {
155         if(data != null && data.length > 0)
156         {
157             m_data ~= cast(ubyte[])data;
158         }
159     }
160 
161     /**
162      * Clear the packet.
163      *
164      * After calling Clear, the packet is empty.
165      */
166     void clear()
167     {
168         m_data.length = 0;
169         m_readPos = 0;
170         m_isValid = true;
171     }
172 
173     /**
174      * Tell if the reading position has reached the end of the packet.
175      *
176      * This function is useful to know if there is some data left to be read,
177      * without actually reading it.
178      *
179      * Returns: true if all data was read, false otherwise.
180      */
181     bool endOfPacket() const
182     {
183         return m_readPos >= m_data.length;
184     }
185 
186     /**
187      * Reads a primitive data type or string from the packet.
188      *
189      * The value in the packet at the current read position is set to value.
190      *
191      * Returns: true if last data extraction from packet was successful.
192      */
193     bool read(T)(out T value)
194     if(isScalarType!T)
195     {
196         import std.bitmanip;
197 
198         if (checkSize(T.sizeof))
199         {
200             value = std.bitmanip.peek!T(m_data, m_readPos);
201             m_readPos += T.sizeof;
202         }
203 
204         return m_isValid;
205     }
206 
207     /// ditto
208     bool read(T)(out T value)
209     if(isSomeString!T)
210     {
211         import std.conv;
212 
213         //Get the element type of the string
214         static if(isNarrowString!T)
215             alias ET = Unqual!(ElementEncodingType!T);
216         else
217             alias ET = Unqual!(ElementType!T);
218 
219         //get string length
220         uint length;
221         read(length);
222         ET[] temp = new ET[](length);
223 
224         //read each dchar of the string and put it in.
225         for(int i = 0; i < length && m_isValid; ++i)
226         {
227             read!ET(temp[i]);
228         }
229 
230         value = temp.to!T();
231 
232         return m_isValid;
233     }
234 
235     /// Writes a scalar data type or string to the packet.
236     void write(T)(T value)
237     if(isScalarType!T)
238     {
239         import std.bitmanip;
240 
241         size_t index = m_data.length;
242         m_data.reserve(value.sizeof);
243         m_data.length += value.sizeof;
244 
245         std.bitmanip.write!T(m_data, value, index);
246     }
247 
248     /// ditto
249     void write(T)(T value)
250     if(isSomeString!T)
251     {
252         //write length of string.
253         write(cast(uint) value.length);
254         //write append the string data
255         for(int i = 0; i < value.length; ++i)
256         {
257             write(value[i]);
258         }
259     }
260 
261     /**
262      * Called before the packet is sent over the network.
263      *
264      * This function can be defined by derived classes to transform the data
265      * before it is sent; this can be used for compression, encryption, etc.
266      *
267      * The function must return an array of the modified data, with a length of
268      * the number of bytes to send. The default implementation provides the
269      * packet's data without transforming it.
270      *
271      * Returns:  Array of bytes to send.
272      */
273     const(void)[] onSend()
274     {
275         return getData();
276     }
277 
278     /**
279      * Called after the packet is received over the network.
280      *
281      * This function can be defined by derived classes to transform the data
282      * after it is received; this can be used for uncompression, decryption,
283      * etc.
284      *
285      * The function receives an array of the received data, and must fill the
286      * packet with the transformed bytes. The default implementation fills the
287      * packet directly without transforming the data.
288      *
289      * Params:
290      * 		data = Array of the received bytes
291      */
292     void onRecieve(const(void)[] data)
293     {
294         append(data);
295     }
296 
297     private bool checkSize(size_t size)
298     {
299         m_isValid = m_isValid && (m_readPos + size <= m_data.length);
300 
301         return m_isValid;
302     }
303 }
304 
305 /*
306  * Utility class used internally to interact with DSFML-C to transfer Packet's
307  * data.
308  */
309 package class SfPacket
310 {
311     package sfPacket* sfPtr;
312 
313     //Default constructor
314     this()
315     {
316         sfPtr = sfPacket_create();
317     }
318 
319     //Destructor
320     ~this()
321     {
322         import nudsfml.system.config;
323         mixin(destructorOutput);
324         sfPacket_destroy(sfPtr);
325     }
326 
327     //Get a slice of the data contained in the packet.
328     //
329     //Returns: Slice containing the data.
330     const(void)[] getData() const
331     {
332         return sfPacket_getData(sfPtr)[0 .. sfPacket_getDataSize(sfPtr)];
333     }
334 
335     //Append data to the end of the packet.
336     //Params:
337     //		data = Pointer to the sequence of bytes to append.
338     void append(const(void)[] data)
339     {
340         sfPacket_append(sfPtr, data.ptr, void.sizeof * data.length);
341     }
342 }
343 
344 unittest
345 {
346     //TODO: Expand to use more of the mehtods found in Packet
347     version(DSFML_Unittest_Network)
348     {
349         import std.stdio;
350 
351         import nudsfml.network.socket;
352         import nudsfml.network.tcpsocket;
353         import nudsfml.network.tcplistener;
354         import nudsfml.network.ipaddress;
355 
356         writeln("Unittest for Packet");
357 
358         //packet to send data
359         auto sendPacket = new Packet();
360         //Packet to receive data
361         auto receivePacket = new Packet();
362 
363         //Let's greet the server!
364         sendPacket.write("Hello, I'm a client!");
365         sendPacket.write(42);
366         sendPacket.write(cast(double) 3.141592);
367         sendPacket.write(false);
368 
369         receivePacket.onRecieve(sendPacket.onSend());
370 
371         //What did we get from the client?
372         string message;
373         int sentInt;
374         double sentDouble;
375         bool sentBool;
376         assert(receivePacket.read!string(message));
377         assert(message == "Hello, I'm a client!");
378 
379         assert(receivePacket.read(sentInt));
380         assert(sentInt == 42);
381 
382         assert(receivePacket.read(sentDouble));
383         assert(sentDouble == 3.141592);
384 
385         assert(receivePacket.read(sentBool));
386         assert(!sentBool);
387         writeln("Gotten from client: ", message, ", ", sentInt, ", ", sentDouble, ", ", sentBool);
388 
389         //clear the packets to send/get new information
390         sendPacket.clear();
391         receivePacket.clear();
392 
393         //Respond back to the client
394         sendPacket.write("Hello, I'm your server.");
395         sendPacket.write(420UL);
396         sendPacket.write(cast(float) 2.7182818);
397         sendPacket.write(true);
398 
399         receivePacket.onRecieve(sendPacket.onSend());
400 
401         ulong sentULong;
402         float sentFloat;
403         assert(receivePacket.read!string(message));
404         assert(message == "Hello, I'm your server.");
405 
406         assert(receivePacket.read(sentULong));
407         assert(sentULong == 420UL);
408 
409         assert(receivePacket.read(sentFloat));
410         assert(sentFloat == 2.7182818f);
411 
412         assert(receivePacket.read(sentBool));
413         assert(sentBool);
414         writeln("Gotten from server: ", message, ", ", sentULong, ", ", sentFloat, ", ", sentBool);
415 
416         writeln("Done!");
417         writeln();
418     }
419 }
420 
421 package extern(C):
422 
423 struct sfPacket;
424 
425 ///Create a new packet
426 sfPacket* sfPacket_create();
427 
428 ///Create a new packet by copying an existing one
429 sfPacket* sfPacket_copy(const sfPacket* packet);
430 
431 ///Destroy a packet
432 void sfPacket_destroy(sfPacket* packet);
433 
434 ///Append data to the end of a packet
435 void sfPacket_append(sfPacket* packet, const void* data, size_t sizeInBytes);
436 
437 ///Clear a packet
438 void sfPacket_clear(sfPacket* packet);
439 
440 ///Get a pointer to the data contained in a packet
441 const(void)* sfPacket_getData(const sfPacket* packet);
442 
443 ///Get the size of the data contained in a packet
444 size_t sfPacket_getDataSize(const sfPacket* packet);
445 
446 ///Tell if the reading position has reached the end of a packet
447 bool sfPacket_endOfPacket(const sfPacket* packet);
448 
449 ///Test the validity of a packet, for reading
450 bool sfPacket_canRead(const sfPacket* packet);
451 
452 ///Functions to extract data from a packet
453 bool    sfPacket_readBool(sfPacket* packet);
454 byte    sfPacket_readInt8(sfPacket* packet);
455 ubyte   sfPacket_readUint8(sfPacket* packet);
456 short   sfPacket_readInt16(sfPacket* packet);
457 ushort  sfPacket_readUint16(sfPacket* packet);
458 int     sfPacket_readInt32(sfPacket* packet);
459 uint    sfPacket_readUint32(sfPacket* packet);
460 float    sfPacket_readFloat(sfPacket* packet);
461 double   sfPacket_readDouble(sfPacket* packet);
462 
463 //Remove in lieu of readUint16 and readUint32 for W and D chars in D?
464 //void     sfPacket_readString(sfPacket* packet, char* string);
465 //void     sfPacket_readWideString(sfPacket* packet, wchar_t* string);
466 
467 ///Functions to insert data into a packet
468 void sfPacket_writeBool(sfPacket* packet, bool);
469 void sfPacket_writeInt8(sfPacket* packet, byte);
470 void sfPacket_writeUint8(sfPacket* packet, ubyte);
471 void sfPacket_writeInt16(sfPacket* packet, short);
472 void sfPacket_writeUint16(sfPacket* packet, ushort);
473 void sfPacket_writeInt32(sfPacket* packet, int);
474 void sfPacket_writeUint32(sfPacket* packet, uint);
475 void sfPacket_writeFloat(sfPacket* packet, float);
476 void sfPacket_writeDouble(sfPacket* packet, double);