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