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);